Расширить хранилище данных Spring

Я хотел бы представить <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");
    }
}

Затем я использую этот интерфейс в своих конкретных репозиториях, например, RecipeIngredientRepository:

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'?

Это ищет find собственность в моем праве RecipeIngredient, Я не хотел этого делать. Я думаю, что это связано с методами запросов JPA. Поэтому я изменил название findOrCreate в xxx Обойти любой метод обнаружения запросов - без успеха. Он ищет xxx собственность тогда.

Что заставляет весенние данные искать это свойство? я использую org.springframework.boot:spring-boot-starter-data-jpa,

1 ответ

Решение

Вам нужно указать свою индивидуальную реализацию репозитория через @EnableJpaRepositories(repositoryBaseClass = ExtendedJpaRepositoryImpl.class),

Взгляните на справочные документы: Добавление пользовательского поведения во все репозитории.

Добавление в ответ @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)
    }

}

И последняя часть - сообщить весенние данные.

@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

Другие вопросы по тегам