在以下场景中,在callSessionBean2()中启动的事务将如何表现?它暂停了吗?如果在SessionBean2中抛出异常会发生什么?SessionBean2设置为BEAN事务管理类型,因为它不与任何数据库通信,仅通过LDAP与AD服务器通信。
我问,因为我一直有问题在生产服务器部署后的几个星期,调用SessionBean2开始挂起,事务超时作为唯一的错误。我觉得这种设置可能是件坏事,有人能解释一下吗?
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class SessionBean1 {
@Inject private SessionBean2 sessionBean2;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void callSessionBean2(){
sessionBean2.doThingsThatMightCauseException();
}
}
@Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class SessionBean2 {
public void doThingsThatMightCauseException(){...}
}
在EJB 3.1规范(§13.6.1)中声明,调用者的事务将被挂起:
如果客户端请求与事务T1相关联,则实例未与事务关联时,容器将挂起对象调用该方法未指定的事务上下文。容器恢复客户机的事务关联(T1)当方法(与任何关联的拦截器方法)完成。
因此,与SessionBean1
相关联的事务被挂起,并且SessionBean2
在任何情况下抛出的异常将由调用bean处理,使用适当的语义(即由CMT会话处理)
你的代码是正确的,尽管我宁愿使用:
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class SessionBean2 {
public void doThingsThatMightCauseException(){...}
}
有同样的效果
您遇到的问题可能与@Singleton
注释有关根据§4.8.5.3和§4.8.5.3,默认bean为:
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Lock(LockType.WRITE)
这会序列化doThingsThatMightCauseException
的调用,导致随机的ConcurrentAccessTimeoutException
。虽然可以通过@AccessTimeout
配置并发访问超时,但如果(序列化的)doThingsThatMightCauseException
访问延迟超过为CMT事务定义的超时,则会导致事务超时(请记住,与SessionBean1
关联的CMT事务被挂起,但时钟仍在计数……)。
结束:
你需要改变doThingsThatMightCauseException
上的访问锁(要注意共享状态):
@Lock(LockType.READ)
public void doThingsThatMightCauseException(){...}
这将删除访问序列化,有望解决超时问题。
如果仍然遇到超时,则与doThingsThatMightCauseException
中包含的操作缓慢有关。在这种情况下,您需要:
- 在任何事务之外调用方法,
- 或更改CMT事务超时(在特定于AS的配置/部署中)
- 或将
SessionBean1
转换为BMT,从而利用UserTransaction.setTransactionTimeout
我的建议是手动管理所有事务。根据Java 6 EE规范:
使用容器管理事务划分的企业bean不能使用任何干扰事务管理的方法容器的事务划分边界。这类例子的commit、setAutoCommit和rollback方法的提交和回滚方法javax.jms.Session。如果你需要控制交易划分,必须使用应用程序管理的事务划分。
使用容器管理事务划分的企业bean也不能使用javax.transaction.UserTransaction接口。