使用Spring& JPA& Hibernate我使用方法来保存一些具有Exception处理的实体,如下所示:
@Repository
public class UserDAOImpl implements UserDAO {
@PersistenceContext
EntityManager em;
@Override
public void createUserRole(String role) throws RoleAlreadyExistsException {
try {
UserRole userRole = new UserRole(role);
em.persist(userRole);
} catch (Exception e) {
throw new RoleAlreadyExistsException();
}
}
}
我的服务:
@Service("userService")
public class UserService
@Transactional
public void createUserRole(String role) throws RoleAlreadyExistsException {
userDao.createUserRole(role);
}
}
稍后我实现了一些逻辑:
try{
userService.createUserRole(role.name());
} catch (AuthorityEntityAlreadyExistsException e){}
但它没有捕获有关重复键的报告异常:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'ROLE_ADMIN' for key 'PRIMARY'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
所以,没有抛出我的RoleAlreadyExistsException,错过了catch块。在服务方法结束时在事务提交期间抛出异常。怎么抓住那个例外?或者如何以另一种方式在Spring中实现这个逻辑?
答案 0 :(得分:0)
EntityManager.persist没有必要执行sql语句:新实体在缓存中注册,并在需要时发送到数据库。
如果数据库检查了您的约束,那么当该实体发送到数据库时,您会看到一个错误,在交易提交的最佳情况下:它是您在跟踪中看到的行为
您可以通过调用EntityManager.flush()
强制您的JPA实现将其缓存发送到数据库public void createUserRole(String role) throws RoleAlreadyExistsException {
try {
UserRole userRole = new UserRole(role);
em.persist(userRole);
em.flush();
} catch (Exception e) {
throw new RoleAlreadyExistsException();
}
}
然而,调用flush太多可能会导致性能下降,您应该在必要时调用它。分离DAO ans服务可能更简单:在UserService中尝试cach DataIntegrityException并且不要在DAO中调用flush
答案 1 :(得分:0)
对DAO或服务或控制器层使用@transactional注释,以便在提交期间捕获SQLexception时要回滚的方法。
i.e @transactional
public void method(@param somevalue)
{
// Database saving or updating function
}
如果您希望在例外
期间显示特定消息 如果你想拥有特定expception类的全局处理程序,请使用下面的代码@ExceptionHandler(SQLException.class)
public String exceptionMessage(){ //return exception message}
答案 2 :(得分:0)
1)DAO不应该捕获异常。让Hibernate或Spring生成的异常抛出而不管它。特别是如果您的异常转换非常广泛并且丢失信息(抛弃原始堆栈跟踪)。
2)不要依赖约束违规来告诉你有一个预先存在的条目。您可以在告诉Hibernate执行插入之前查询它。只要查询与插入在同一事务中,并且隔离级别是可重复读取或更好,查询结果将是可靠的。