Я создал собственный репозиторий для переопределения методов сохранения и попытался подключить его, как описано в файле весенние документы. Я не получаю ошибок, все объекты и репозитории обнаруживаются при запуске, и когда я вызываю repo.saveAll(entities), постоянство работает нормально. Однако мой пользовательский код никогда не вызывается. Я добавил операторы журнала и даже бросил RuntimeExceptions в свой код, просто чтобы увидеть, выполняется ли он, но он определенно игнорируется. Какой шаг я пропустил?
@Configuration
@Profile("test")
@EnableJpaRepositories(repositoryBaseClass = SetClientInfoRepositoryImpl.class,
basePackages = {"gov.penndot.hwy.apras.common.repository" },
entityManagerFactoryRef = "serviceEntityManagerFactory",
transactionManagerRef = "serviceTransactionManager")
public class TestDatabaseConfig {
@Bean(name = "serviceDataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");
dataSource.setUsername("sa");
dataSource.setPassword("sa");
return dataSource;
}
@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<String, Object>(), null);
}
@Bean(name = "serviceEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean serviceEntityManagerFactory(EntityManagerFactoryBuilder builder,
@Qualifier("serviceDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("stuff")
.persistenceUnit("service")
.build();
}
@Bean(name = "serviceTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("serviceEntityManagerFactory") EntityManagerFactory serviceEntityManagerFactory) {
return new JpaTransactionManager(serviceEntityManagerFactory);
}
Репозиторий:
@NoRepositoryBean
public class SetClientInfoRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private static final Logger log = LoggerFactory.getLogger(SetClientInfoRepositoryImpl.class);
private final EntityManager em;
public SetClientInfoRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
}
@Transactional
@Override
public <S extends T> S save(S entity) {
setClientInfo();
return super.save(entity);
}
@Transactional
@Override
public void deleteById(ID id) {
setClientInfo();
super.deleteById(id);
}
@Transactional
@Override
public void delete(T entity) {
setClientInfo();
super.delete(entity);
}
@Transactional
@Override
public void deleteAll(Iterable<? extends T> entities) {
setClientInfo();
super.deleteAll(entities);
}
@Transactional
@Override
public void deleteInBatch(Iterable<T> entities) {
setClientInfo();
super.deleteInBatch(entities);
}
@Transactional
@Override
public void deleteAll() {
setClientInfo();
super.deleteAll();
}
@Transactional
@Override
public void deleteAllInBatch() {
setClientInfo();
super.deleteAllInBatch();
}
@Transactional
@Override
public <S extends T> S saveAndFlush(S entity) {
setClientInfo();
return super.saveAndFlush(entity);
}
@Transactional
@Override
public <S extends T> List<S> saveAll(Iterable<S> entities) {
setClientInfo();
super.saveAll(entities);
throw new RuntimeException("foo");
}
private void setClientInfo() {
log.debug("Entering setClientInfo method");
[stuff]
}
}
Вы уверены, что ваш основной класс конфигурации, в котором вы используете @EnableJpaRepositories, помечен @Profile("!test")?
@Derek - (спасибо за ответ!) Я вызываю метод saveAll() в другом интерфейсе, аннотированном как репозиторий. Зачем мне нужен другой интерфейс, когда метод тот же, а документы Spring не вызывают его? Я использовал пользовательский интерфейс, когда требуются пользовательские методы
@Selindek - (спасибо за ответ!) Да, проблема не в профиле. Правильная конфигурация базы данных загружается в Spring, потому что все сохранение происходит в базе данных h2, как определено в классе конфигурации.
Хорошо, тогда давайте удостоверимся, что оригинал @EnableJpaRepositories не выбран: закомментируйте его и запустите тест таким образом.
@Selindek: закомментировано, но поведение такое же. Я отредактировал, чтобы включить полный класс конфигурации базы данных (у меня есть несколько БД в этой службе, поэтому требуется несколько конфигураций.) Может ли один из экземпляров в этом классе перезаписывать аннотированный репозиторийBaseClass? Когда я отлаживаю метод getTargetRepository в Spring, он определенно использует SimpleJpaRepository в качестве базового класса.




Хорошо, это довольно отчаянная идея, но попробовать стоит...
Создайте пользовательский интерфейс репозитория:
public interface SetClientInfoRepository<T, ID> extends JpaRepository<T, ID> {
}
Реализуйте этот интерфейс репозитория с помощью собственного базового репозитория:
public class SetClientInfoRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements SetClientInfoRepository<T, ID> {
}
... и, наконец, расширить этот интерфейс интерфейсами вашего репозитория вместо JpaRepository
Таким образом, Spring должен создает репозиторий-прокси из вашей реализации, потому что нет других классов, которые он мог бы использовать. Кроме того, если он не может создать репозитории по какой-либо причине, вы получите более информативное исключение во время запуска.
Использование пользовательского интерфейса репозитория само по себе неплохо, потому что всегда есть хороший шанс, что вы захотите добавить некоторые общие пользовательские методы в свои репозитории позже, и тогда это пригодится.
Спасибо за всю твою помощь! Ваш ответ сработал. Я создал новый простой проект и получил исходную аннотацию baseRepositoryClass, работающую правильно, поэтому я уверен, что где-то напортачил конфигурацию.
Я считаю, что Spring Data JPA (и Spring Data Rest) являются (одним из) наиболее сложными пакетами Spring. Я не удивлюсь, если эта проблема возникнет из-за ошибки в ней.
Чтобы подтвердить, вы вызываете метод saveAll() в SetClientInfoRepositoryImpl, а не в каком-то другом классе? Обычно здесь нужно создать интерфейс для SetClientInfoRepository, автоматически связать/внедрить этот репозиторий, куда вам нужно, а затем вызвать метод saveAll(), используя этот интерфейс. stackoverflow.com/questions/13036159/…