seam中有两种事务管理方式,1)页面请求自动开启、关闭事务,不需要在代码上做任务额外处理;2)通过@Transactional注释在类或方法上增加事务。
第1种是在jsf的PhaseListener(SeamPhaseListener)中实现的,阶段中调用业务方法,阶段后在SeamPhaseListener中根据事务的状态来决定提交还是回滚事务。事务的状态是如何更改的呢?阶段前开始事务后事务的状态是active的,阶段中调用业务方法如果产生异常会改变事务的状态为Rollback,否则事务状态还是active的。由于阶段后不能捕获阶段中的异常,所以通过RollbackInterceptor 拦截器来让业务方法失败后改变事务状态。不是任何异常都会改变事务状态,如下异常才会改变事务状态。
1)所有不带@javax.ejb.ApplicationException或@ApplicationException(seam的)的RuntimeException
2)不包括jsf 的ValidatorException和ConverterException
3)所有带@javax.ejb.ApplicationException或@ApplicationException(seam的),并且rollback=ture的任何Exception
第2种在方法上增加拦截,如果方法本身抛出异常,根据上面的3种异常情况决定是提交还是回滚事务,如果方法捕获了调用其它组件的方法抛出的异常,也可能会导致事务回滚,其它组件的方法中抛出的异常是通过RollbackInterceptor 拦截器来改变事务状态的,这种情况与第1种事务管理方式一样。
//加在每个可拦截的方法上
@Interceptor(stateless=true)
public class RollbackInterceptor extends AbstractInterceptor
{
private static final long serialVersionUID = 5551801508325093417L;
@AroundInvoke
public Object aroundInvoke(InvocationContext invocation) throws Exception
{
try
{
return invocation.proceed();
}
catch (Exception e)
{
if ( isRollbackRequired(e, getComponent().getType() == JAVA_BEAN) )
{
try
{
Transaction.instance().setRollbackOnly();//设置当前事务(底层事务)回滚
}
catch (Exception te) {} //如果当前线程没有活动的事务,忽略任何异常
}
throw e;
}
}
public boolean isInterceptorEnabled()
{
// Just here for consistency
return true;
}
}
总结:默认情况下方法中抛出的runtimeException会导致事务回滚,检查时异常不会导致事务回滚,在异常类型不能改变的情况下通过@javax.ejb.ApplicationException或@ApplicationException(seam的)的rollback来决定是否需要回滚事务。
如下异常可能就是因为忽略了runtimeException导致的。
2013-01-04 14:50:10,709 ERROR [org.hibernate.util.JDBCExceptionReporter] Transaction is not active: tx=TransactionImple < ac, BasicAction: -53ee3e68:e32b:50e0f672:2d76 status: ActionStatus.ABORT_ONLY >; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: -53ee3e68:e32b:50e0f672:2d76 status: ActionStatus.ABORT_ONLY >)
2013-01-04 14:50:10,709 INFO [org.hibernate.event.def.DefaultLoadEventListener] Error performing load command
org.hibernate.exception.GenericJDBCException: Cannot open connection
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:126)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:114)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:52)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:449)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:161)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1573)
at org.hibernate.loader.Loader.doQuery(Loader.java:696)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadEntity(Loader.java:1881)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:71)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:65)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3072)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:434)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:415)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:165)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:223)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:126)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:905)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:842)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:835)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:182)
at sun.reflect.GeneratedMethodAccessor761.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jboss.seam.persistence.EntityManagerInvocationHandler.invoke(EntityManagerInvocationHandler.java:46)
at $Proxy174.find(Unknown Source)
at org.iata.ios.core.dao.BaseDao.find(BaseDao.java:82)
at org.iata.seurat.app.systemadmin.user.dao.UserDao.getLoginUser(UserDao.java:48)