I was setting up a basic CRUD web app with JPA in plain Spring (no Spring Boot or Spring Data JPA) for educational purposes and faced a strange problem: Spring doesn’t translate exceptions for my repository. According to the Spring documentation (here and here), it is sufficient to mark the repository with the @Repository annotation and Spring will automatically enable exception translation for this repository.
However, when I did so and triggered a UNIQUE constraint violation, I still was getting a JPA PersistenceException (with a Hibernate ConstraintViolationException inside) instead of the Spring DataIntegrityViolationException.
I used pure Java Spring configuration and it took me quite some time to realize that I should compare it with the XML configuration in the documentation. Compared to the pure Java configuration, the XML configuration adds a PersistenceExceptionTranslationPostProcessor into the context. When I added it manually with @Bean, it worked, but now I have a question.
Have I misconfigured something? The Spring documentation doesn’t require registering that post-processor manually for pure Java configuration. Maybe there is another way to register it, say an @EnableXXX annotation?
Here is the summary of my configuration.
@Configuration
@ComponentScan("com.example.secured_crm")
public class SpringConfiguration {
// the problem is solved if I uncomment this
//@Bean
//public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
// return new PersistenceExceptionTranslationPostProcessor();
//}
}
@Configuration
@PropertySource("classpath:db.properties")
@EnableTransactionManagement
public class DataSourceConfiguration {
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
// ...
@Value("${hibernate.debug}")
private String hibernateDebug;
@Bean
public DataSource dataSource() {
var dataSource = new ComboPooledDataSource();
// ...
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
var emFactory = new LocalContainerEntityManagerFactoryBean();
emFactory.setDataSource(dataSource());
emFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emFactory.setPackagesToScan("com.example.secured_crm.entities");
var properties = new Properties();
properties.setProperty("hibernate.dialect", hibernateDialect);
properties.setProperty("hibernate.show_sql", hibernateDebug);
properties.setProperty("hibernate.format_sql", "true");
emFactory.setJpaProperties(properties);
return emFactory;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
var txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
public interface UserRepository {
User findByName(String username);
List<User> findAll();
void save(User user);
boolean deleteById(int id);
User findById(int id);
}
@Repository
public class UserJpaRepository implements UserRepository {
@PersistenceContext
EntityManager em;
@Override
public void save(User user) {
if (user.getId() == null) {
em.persist(user);
} else {
em.merge(user);
}
}
// and so on...
}
By the way, when I tried to add the post-processor in DataSourceConfiguration, it disabled @PropertySource effect. So far my impression of Spring is that it’s one big hack…
>Solution :
It requires to manually register PersistenceExceptionTranslationPostProcessor in order for the exception translation to take effect.
The documentation you mentioned simply does not updated yet to show a fully working java configuration. It should mention to register this post processor. ( So feel free to provide a PR to update the docs.).
If you check from its javadoc , it already mentioned PersistenceExceptionTranslationPostProcessor is necessary to be registered :
As a consequence, all that is usually needed to enable automatic
exception translation is marking all affected beans (such as
Repositories or DAOs) with the @Repository annotation, along with
defining this post-processor as a bean in the application context.
P.S. If you are using spring-boot , and if it detects PersistenceExceptionTranslationPostProcessor is in the class-path , it will automatically register it by default such that you do not need to register manually.