【问题标题】:Extend Spring Data Repository扩展 Spring 数据存储库
【发布时间】:2017-09-22 11:53:52
【问题描述】:

我想向我的所有存储库介绍<T> T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory)。 于是新建了一个界面

@NoRepositoryBean
public interface ExtendedJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
    T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory);
}

.

public class ExtendedJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements ExtendedJpaRepository<T, ID> {

    private final JpaEntityInformation entityInformation;
    private final EntityManager entityManager;

    public ExtendedJpaRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityInformation = entityInformation;
        this.entityManager = entityManager;
    }

    @Override
    public T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory) {
        throw new NotImplementedException("No implemented yet");
    }
}

然后我在我的具体存储库中使用这个接口,例如配方成分库:

public interface RecipeIngredientRepository extends ExtendedJpaRepository<RecipeIngredient, Long> {}

当我最终将存储库注入我的服务时,我得到以下异常:

java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'recipeIngredientRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property find found for type RecipeIngredient! Did you mean 'id'?

它正在我的实体RecipeIngredient 中搜索find 属性。我不希望它这样做。我认为这与JPA Query Methods 有关。所以我将名称从findOrCreate 更改为xxx 以绕过任何查询方法检测 - 没有成功。然后它会搜索xxx 属性。

是什么让 spring data 寻找这个属性? 我正在使用org.springframework.boot:spring-boot-starter-data-jpa

【问题讨论】:

    标签: spring-boot spring-data spring-repositories


    【解决方案1】:

    您需要通过@EnableJpaRepositories(repositoryBaseClass = ExtendedJpaRepositoryImpl.class) 指定您的自定义存储库实现。

    查看参考文档:Adding custom behavior to all repositories

    【讨论】:

    • 谢谢!天哪,我确实遵循了这个文档!但是我使用了@EnableJpaRepositories("org.project.data.repository") 来指定basePackages 而不是repositoryBaseClass。我没想到@EnableJpaRepositories 注释处的错误。
    【解决方案2】:

    添加到@md911de 答案:

    因此,您可以定义一个通用接口,该接口具有您希望在所有存储库中拥有的基本方法:

    @NoRepositoryBean
    interface BaseGenericReactiveMongoRepository<T> : 
    ReactiveMongoRepository<T, String> {
       fun patch(id: String, fields: Map<String, Any>): Mono<T>
    }
    

    那么你需要实现这个并通知spring使用实现类来实现接口。

    class SimpleBaseGenericReactiveMongoRepository<ENTITY>(
            private val entityInformation: MappingMongoEntityInformation<ENTITY, String>,
            private val template: ReactiveMongoTemplate
    ) : SimpleReactiveMongoRepository<ENTITY, String>(entityInformation, template),
            BaseGenericReactiveMongoRepository<ENTITY> {
    
        private val eventPublisher: ApplicationEventPublisher?
    
        init {
            val context = template.converter.mappingContext as MongoMappingContext
            val indexCreator = MongoPersistentEntityIndexCreator(context) { collectionName ->
                IndexOperationsAdapter.blocking(template.indexOps(collectionName))
            }
            eventPublisher = MongoMappingEventPublisher(indexCreator)
        }
    
        override fun patch(id: String, fields: Map<String, Any>): Mono<ENTITY> {
            val collection = entityInformation.collectionName
            val query = Query(Criteria.where("_id").`is`(id))
            val document = Document()
    
            return findById(id)
                    .flatMap { entity ->
                        maybeEmitEvent(BeforeConvertEvent<ENTITY>(entity, collection))
    
                        document.putAll(fields)
    
                        val update = Update()
    
                        fields
                                .filter { entry ->
                                    !hashSetOf("_id", "createdAt", "createdBy", "modifiedAt", "modifiedBy").contains(entry.key)
                                }
                                .forEach { entry -> update.set(entry.key, entry.value) }
    
                        maybeEmitEvent(BeforeSaveEvent<ENTITY>(entity, document, collection))
    
                        template.updateFirst(query, update, collection)
                    }
                    .then(findById(id)).map { entity ->
                        maybeEmitEvent(AfterSaveEvent<ENTITY>(entity, document, collection))
                        entity
                    }
        }
    
        private fun <T> maybeEmitEvent(event: MongoMappingEvent<T>) {
            eventPublisher?.publishEvent(event)
        }
    
    }
    

    最后一部分是通知spring数据。

    @Configuration
    @EnableReactiveMongoRepositories(
        basePackages = ["**.repository"],
        repositoryBaseClass = SimpleBaseGenericReactiveMongoRepository::class
    )
    class MongoConfiguration
    

    现在您可以将该接口用作存储库的基本接口,并拥有适用于您的域的功能。

    interface BookRepository : BaseMongoRepository<Book> {
    
        findByNameContainingIgnoreCaseAndVisibileIsTrue(name:String): Flux<Book>
    
    }
    

    如果您需要一个工作示例,欢迎您查看我的媒体:

    https://medium.com/@ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9

    【讨论】:

      猜你喜欢
      • 2014-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-04
      • 1970-01-01
      • 1970-01-01
      • 2011-04-07
      • 1970-01-01
      相关资源
      最近更新 更多