简体   繁体   中英

Integrating Query dsl with Reactive Mongo Repository in spring 5

Spring 5 comes with a reactive implementation for spring data mongo , before the i was able to extend my mongo repository with QuerydslPredicateExecutor

 @Repository
 public interface AccountRepo extends MongoRepository<Account,String> 
 ,QuerydslPredicateExecutor<Account>

if i try this

 @Repository
 public interface AccountRepo extends
 ReactiveMongoRepository<Account,String>,
 QuerydslPredicateExecutor<Account>

my application does not start it because :

Caused by: org.springframework.data.mapping.PropertyReferenceException: No property exists found for type Account!

is there a way around this

here is the account class

package com.devop.models;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
@Data
public class Account {
@Id
String id;

String accountName;

String accountNumber;

String schemeCode;

String openDate;

String accountCategory;

String accountCurrency = "Naira";

String accountSecondaryCategory;

String receiveSmsAlert;

String recieveEmailAlert;

String cifId;

}

here is the interface AccountRepo

 package com.devop.mongoRepo;

 import com.devop.models.Account;
 import org.springframework.data.mongodb.repository.*;
 import org.springframework.data.mongodb.repository.support*;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.stereotype.Repository;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;

 @Repository
 public interface AccountRepo extends 
 ReactiveMongoRepository<Account,String> 
 ,QuerydslPredicateExecutor<Account> {

Flux<Account> findAccountByAccountNameContains(String accountName);
Mono<Account> findAccountByAccountNumberEquals(String accountNumber);

}

You could use ReactiveQuerydslPredicateExecutor . See example for more information https://github.com/spring-projects/spring-data-examples/tree/master/mongodb/querydsl

I found a workaround solution using a Custom ReactiveMongoRepositoryFactoryBean and ReactiveMongoRepositoryFactory , it could help someone else that is struggling with that

First you need to use repositoryFactoryBeanClass in the reactive annotation.

@EnableReactiveMongoRepositories(repositoryFactoryBeanClass = CustomReactiveMongoRepositoryFactoryBean.class)
@Configuration
public class ReactiveMongoDbHack {
}

CustomReactiveMongoRepositoryFactoryBean class

import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

public class CustomReactiveMongoRepositoryFactoryBean extends ReactiveMongoRepositoryFactoryBean {
    public CustomReactiveMongoRepositoryFactoryBean(Class repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport getFactoryInstance(ReactiveMongoOperations operations) {
        return new CustomReactiveMongoRepositoryFactory(operations);
    }
}

CustomReactiveMongoRepositoryFactory class

import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT;

public class CustomReactiveMongoRepositoryFactory extends ReactiveMongoRepositoryFactory {

    private static MongoOperations operations;
    private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;

    public CustomReactiveMongoRepositoryFactory(ReactiveMongoOperations operations) {
    super(operations);
    this.mappingContext = operations.getConverter().getMappingContext();
    }

    //TODO Must set MongoOperations(MongoTemplate)
    public static void setOperations(MongoOperations operations) {
    CustomReactiveMongoRepositoryFactory.operations = operations;
    }

    @Override
    protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {

    RepositoryComposition.RepositoryFragments fragments = RepositoryComposition.RepositoryFragments.empty();

    boolean isQueryDslRepository = QUERY_DSL_PRESENT
        && QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface());

    if (isQueryDslRepository) {

        MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType(),
            metadata);

        fragments = fragments.append(RepositoryFragment.implemented(
            getTargetRepositoryViaReflection(QuerydslMongoPredicateExecutor.class, entityInformation, operations)));
    }

    return fragments;
    }

    private <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> domainClass,
                                                                   @Nullable RepositoryMetadata metadata) {

    MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
    return MongoEntityInformationSupport.<T, ID> entityInformationFor(entity,
        metadata != null ? metadata.getIdType() : null);
    }

}

MongoEntityInformationSupport class copied from spring-data-mongodb

import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * Support class responsible for creating {@link MongoEntityInformation} instances for a given
 * {@link MongoPersistentEntity}.
 *
 * @author Christoph Strobl
 * @author Mark Paluch
 * @since 1.10
 */
final class MongoEntityInformationSupport {

    private MongoEntityInformationSupport() {}

    /**
     * Factory method for creating {@link MongoEntityInformation}.
     *
     * @param entity must not be {@literal null}.
     * @param idType can be {@literal null}.
     * @return never {@literal null}.
     */
    @SuppressWarnings("unchecked")
    static <T, ID> MongoEntityInformation<T, ID> entityInformationFor(MongoPersistentEntity<?> entity,
                                                                  @Nullable Class<?> idType) {

    Assert.notNull(entity, "Entity must not be null!");

    MappingMongoEntityInformation<T, ID> entityInformation = new MappingMongoEntityInformation<T, ID>(
        (MongoPersistentEntity<T>) entity, (Class<ID>) idType);

    return ClassUtils.isAssignable(Persistable.class, entity.getType())
        ? new PersistableMongoEntityInformation<>(entityInformation) : entityInformation;
    }
}

PersistableMongoEntityInformation class also copied from spring-data-mongodb

import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;

/**
 * {@link MongoEntityInformation} implementation wrapping an existing {@link MongoEntityInformation} considering
 * {@link Persistable} types by delegating {@link #isNew(Object)} and {@link #getId(Object)} to the corresponding
 * {@link Persistable#isNew()} and {@link Persistable#getId()} implementations.
 *
 * @author Christoph Strobl
 * @author Oliver Gierke
 * @since 1.10
 */
@RequiredArgsConstructor
class PersistableMongoEntityInformation<T, ID> implements MongoEntityInformation<T, ID> {

    private final @NonNull MongoEntityInformation<T, ID> delegate;

    /*
     * (non-Javadoc)
     * @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
     */
    @Override
    public String getCollectionName() {
    return delegate.getCollectionName();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.mongodb.repository.MongoEntityInformation#getIdAttribute()
     */
    @Override
    public String getIdAttribute() {
    return delegate.getIdAttribute();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.EntityInformation#isNew(java.lang.Object)
     */
    @Override
    @SuppressWarnings("unchecked")
    public boolean isNew(T t) {

    if (t instanceof Persistable) {
        return ((Persistable<ID>) t).isNew();
    }

    return delegate.isNew(t);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object)
     */
    @Override
    @SuppressWarnings("unchecked")
    public ID getId(T t) {

    if (t instanceof Persistable) {
        return ((Persistable<ID>) t).getId();
    }

    return delegate.getId(t);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
     */
    @Override
    public Class<ID> getIdType() {
    return delegate.getIdType();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.support.EntityMetadata#getJavaType()
     */
    @Override
    public Class<T> getJavaType() {
    return delegate.getJavaType();
    }
}

Then your Reactive Repository with Query DSL would work.

public interface AssetRepository extends ReactiveCrudRepository<Asset, String>, QuerydslPredicateExecutor<Asset> {
}

PS: It must need to set MongoOperations at CustomReactiveMongoRepositoryFactory.setOperations , what I did was to use the auto config classes(MongoAutoConfiguration and MongoDataAutoConfiguration) for Mongo then using @PostConstruct in a method to set it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM