在普通 Spring 中为 JPA 启用异常转换
Enabling exception translation for JPA in plain Spring
出于教育目的,我在 Spring(没有 Spring Boot 或 Spring Data JPA)中使用 JPA 设置了一个基本的 CRUD Web 应用程序,但遇到了一个奇怪的问题:Spring 不会为我的存储库翻译异常。根据 Spring 文档 (here and here),使用 @Repository
注释标记存储库就足够了,Spring 将自动为该存储库启用异常转换。
但是,当我这样做并触发了 UNIQUE
约束违规时,我仍然得到一个 JPA PersistenceException
(内部有一个 Hibernate ConstraintViolationException
)而不是 Spring DataIntegrityViolationException
.
我使用了纯粹的 Java Spring 配置,我花了很长时间才意识到我应该将它与文档中的 XML 配置进行比较。与纯 Java 配置相比,XML 配置在上下文中添加了一个 PersistenceExceptionTranslationPostProcessor
。当我用 @Bean
手动添加它时,它起作用了,但现在我有一个问题。
我配置有误吗? Spring 文档不需要为纯 Java 配置手动注册 post 处理器。 也许还有另一种注册方式,比如 @EnableXXX
注解?
这是我的配置摘要。
@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...
}
顺便说一下,当我尝试在 DataSourceConfiguration
中添加 post-处理器时,它禁用了 @PropertySource
效果。到目前为止,我对 Spring 的印象是它是一个大 hack...
异常翻译需要手动注册PersistenceExceptionTranslationPostProcessor
才能生效
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 更新文档。)。
如果你从它的 javadoc 查看,它已经提到 PersistenceExceptionTranslationPostProcessor
需要注册 :
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。如果你正在使用 spring-boot ,并且如果它检测到 PersistenceExceptionTranslationPostProcessor
在 class-path 中,它会默认自动注册它,这样你就不需要手动注册。
出于教育目的,我在 Spring(没有 Spring Boot 或 Spring Data JPA)中使用 JPA 设置了一个基本的 CRUD Web 应用程序,但遇到了一个奇怪的问题:Spring 不会为我的存储库翻译异常。根据 Spring 文档 (here and here),使用 @Repository
注释标记存储库就足够了,Spring 将自动为该存储库启用异常转换。
但是,当我这样做并触发了 UNIQUE
约束违规时,我仍然得到一个 JPA PersistenceException
(内部有一个 Hibernate ConstraintViolationException
)而不是 Spring DataIntegrityViolationException
.
我使用了纯粹的 Java Spring 配置,我花了很长时间才意识到我应该将它与文档中的 XML 配置进行比较。与纯 Java 配置相比,XML 配置在上下文中添加了一个 PersistenceExceptionTranslationPostProcessor
。当我用 @Bean
手动添加它时,它起作用了,但现在我有一个问题。
我配置有误吗? Spring 文档不需要为纯 Java 配置手动注册 post 处理器。 也许还有另一种注册方式,比如 @EnableXXX
注解?
这是我的配置摘要。
@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...
}
顺便说一下,当我尝试在 DataSourceConfiguration
中添加 post-处理器时,它禁用了 @PropertySource
效果。到目前为止,我对 Spring 的印象是它是一个大 hack...
异常翻译需要手动注册PersistenceExceptionTranslationPostProcessor
才能生效
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 更新文档。)。
如果你从它的 javadoc 查看,它已经提到 PersistenceExceptionTranslationPostProcessor
需要注册 :
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。如果你正在使用 spring-boot ,并且如果它检测到 PersistenceExceptionTranslationPostProcessor
在 class-path 中,它会默认自动注册它,这样你就不需要手动注册。