【发布时间】:2015-04-22 00:07:46
【问题描述】:
我有一个 Spring MVC 应用程序,它使用 Spring Data JPA 进行持久性,并将 Hibernate 作为我的 JPA 提供程序。我有一个对列具有唯一约束的数据库表,因此为什么保存相应的实体可能会导致违反唯一约束。我想检测这是否发生在我的服务层中,以便我可以向用户显示有意义的错误消息。下面是我的服务方式。
@Service
public class IndustryServiceImpl implements IndustryService {
@Autowired
private IndustryRepository industryRepository;
@Override
@Transactional
public void add(Collection<Industry> industries) {
this.industryRepository.save(industries);
this.industryRepository.flush();
}
}
我的 Spring Data JPA 存储库如下所示:
@Repository
public interface IndustryRepository extends JpaRepository<Industry, Integer> { }
现在,当发生唯一约束违规时,我得到以下异常(为简洁起见而缩短):
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
org.hibernate.exception.ConstraintViolationException: could not execute statement
...
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "industry_name_unique_index"
Detail: Key (name)=(SomeValue) already exists.
...
如您所见,“main”异常的类型为javax.persistence.PersistenceException,这是一个非常普遍的异常。理想情况下,我想捕获一个异常,告诉我错误是由于违反了唯一约束。因此我有几个问题:
- 异常应该是
javax.persistence.PersistenceException类型是否正确,还是因为没有启动Spring 异常转换?我可以配置 Spring 来给我更少的一般例外吗,例如DuplicateKeyException? - 如果此行为与缺少异常转换无关,那么您是否知道比调用
e.getCause()检测更具体错误的更好方法?我可以很容易地做到这一点,但这对我来说似乎不合适,原因有两个;首先,代码对我来说似乎更“脆弱”,其次,我还必须检查 JPA 提供程序特定的异常,因为嵌套异常的类型为org.hibernate.exception.ConstraintViolationException。如果可能,我希望我的服务不知道我正在使用哪个 JPA 提供程序。
以下是我的持久性配置,以防万一。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "com.example.company.repository", "com.example.account.repository" })
public class PersistenceConfig {
@Autowired
private LocalValidatorFactoryBean validator;
@Value("${jndi.data.source}")
private String dataSourceName;
@Bean
public DataSource dataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
return dsLookup.getDataSource(this.dataSourceName);
}
@Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabase(Database.POSTGRESQL);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
String[] packagesToScan = new String[] { "com.example.company.entity", "com.example.account.entity" };
factory.setPackagesToScan(packagesToScan);
factory.setDataSource(this.dataSource());
factory.setValidationMode(ValidationMode.NONE); // Prevents errors when using custom validators when persisting
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(this.entityManagerFactory());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
提前谢谢你!
【问题讨论】:
-
我会说你应该明确地验证数据而不是依赖异常。
-
@AlanHay 是的,我实际上正在这样做,但是如果发生幻读,那么这种情况可能仍然会发生(尽管可能性很小) - 我不想锁定表.在其他情况下,我可能还需要这个问题的答案。 :-)
-
@AlanHay 你无法在繁忙的服务器中验证唯一的数据库约束
-
很公平。无论如何,我已经在本地对此进行了测试,无论我是否在应用程序配置中实际注册了 PetPostProcessor,我都会返回以下内容,因此确实会发生翻译:org.springframework.dao.DataIntegrityViolationException > javax.persistence.PersistenceException > org.hibernate.exception .ConstraintViolationException
-
@AlanHay 嗯,奇怪的是这不是我得到的行为。如果您对可能出现的问题有任何想法,或者您希望我发布更多代码,请告诉我。非常感谢您抽出宝贵时间对此进行测试 - 谢谢!
标签: java spring spring-mvc exception-handling spring-data-jpa