Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Enabling exception translation for JPA in plain Spring

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading