使用CMT,事务划分在会话bean部署描述符中声明,而不是以编程方式执行。
但我无法找到有关如何执行此操作的完整示例。
这就是我的想法,我的代码应该是这样的:
@Stateless
public class Dao{
@Inject // or some other annotation
private SessionFactory factory;
public void doDaoStuff(){
Object obj = factory.getCurrentSession().get(Entity.class, "Id");
// do something with obj
return;
}
}
hibernate具有的所有样板都是免费的,因为事务应该由容器启动,提交和滚动。
那么,有可能这样做吗?虽然文档说明应该在 bean部署描述符中指定所需的声明,但使用注释执行此操作会很棒。
答案 0 :(得分:1)
在JavaEE环境中,Hibernate可以使用CMT(容器管理事务)策略,该策略将hibernate事务与底层JTA事务绑定,从而消除了手动存在,提交和回滚事务的需要。示例here。
但是,有一些问题:
这不适用于所有Java EE容器。不支持较新版本的Websphere,并引用source code of hibernate - WebSphere,但是,它不是一个理智的JEE / JTA容器......
这限制了一个会话一个事务习惯用法。因此,在调用EJB业务方法期间,只能有一个JTA或Hibernate事务。
幸运的是,使用CDI和一些自定义拦截器可以解决这个问题,并且可以删除大量的Hibernate样板。我写了一个github的样本。
此方法为Hibernate SessionFactory创建了一个包装器,并提供了最常用的API。使用CDI,@RequestScoped
会话被自动启用并关闭。使用Interceptor管理事务。 @RequestScoped
每个请求确保一个Session
,因此不会在多个请求之间共享会话。
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
@RequestScoped
public class MySessionFactory implements SessionFactoryTemplate{
@Inject
private SessionFactory sessionFactory;// Inject after creating the singleton instance
private Session currentSession;
public Session openSession(){
return sessionFactory.openSession();
}
public Session getCurrentSession(){
if(currentSession == null){
currentSession = sessionFactory.openSession();
}
return currentSession;
}
public StatelessSession openStatelessSession() {
return sessionFactory.openStatelessSession();
}
@PreDestroy
private void closeSession(){
if(currentSession!=null && currentSession.isOpen()) {
currentSession.close();
}
}
}
然后将这个实现注入数据库层并用于获取会话。
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.ares.cdi.hibernate.interceptors.Transactional;
public class Dao {
@Inject
private MySessionFactory sf;
public void get(int id){
sf.getCurrentSession().get(clazz,id);
}
@Transactional
public void add(Object entity){
sf.getCurrentSesion().add(entity);
}
}
事务由TranscationManager
拦截器管理,并由@Transactional
注释声明。
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
@Interceptor
@Transactional
public class TransactionManager {
@Inject
private MySessionFactory sessionFactory;
@AroundInvoke
public Object handleTransaction(InvocationContext context) throws Exception{
Session session = sessionFactory.getCurrentSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
return context.proceed();
}
catch(Exception e){
tx.rollback();
throw e;
}
finally{
if(tx.getStatus().equals(TransactionStatus.ACTIVE)){
try{
tx.commit();
}
catch(Exception e){
tx.rollback();
throw e;
}
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Transactional {
}
答案 1 :(得分:0)
默认情况下,EJB方法是事务性的。
您可以使用TransactionAttribute注释调整其行为。
您可以在此处阅读有关CMT的更多信息:
https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIJ
https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH
答案 2 :(得分:0)
你的典型例子就像 -
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
@TransactionManagement(TransactionManagementType.CONTAINER)
@Stateless(..)
public class YourBean{
@TransactionAttribute(TransactionAttributeType.REQUIRED) // if in case you wanted to use 'existing' transaction
public void DoStuff(){
}
}
在您的服务器配置中,您需要<enterprise-beans>
<transaction-type>Container</transaction-type>