在使用DAO设计模式的情况下,Hibernate中的事务管理

8

我有很多数据表,对于每个数据表,我们都有DAO接口和DAO实现类。

DAO接口示例:

public interface CancelPolicyDAO {

public CancelPolicy insertCancelPolicy(CancelPolicy cpdao)throws ChannelDispatcherException;

public CancelPolicy updateCancelPolicy(CancelPolicy cpdao)throws ChannelDispatcherException;

public void deleteCancelPolicy(CancelPolicy cpdao)throws ChannelDispatcherException;

public CancelPolicy findByCancelPolicyData(Integer id, Integer offSetUM, Integer nights, Float pOrAm, Byte isPercent)throws ChannelDispatcherException;

public CancelPolicy findByCancelPolicyId(Integer id)throws ChannelDispatcherException;
}

示例DAOImplementation类

public class CancelPolicyDAOImpl implements CancelPolicyDAO {

@Override
public CancelPolicy insertCancelPolicy(CancelPolicy bean) throws ChannelDispatcherException {

    Session ses = null;
    try {

        ses = HibernateConnector.getInstance().getSession();
        ses.save(bean);
        ses.flush();
        return bean;
    } catch (Exception e) {
        e.printStackTrace();
        throw new ChannelDispatcherException(DbUtil.getStackTraceMessage(e));
    } finally {
        if (ses != null) {
            try {
                ses.close();
            } catch (Exception er) {
                er.printStackTrace();
            }
        }
    }

}

@Override
public CancelPolicy updateCancelPolicy(CancelPolicy bean) throws ChannelDispatcherException {
    Session sess = null;

    try {

        sess = HibernateConnector.getInstance().getSession();
        sess.update(bean);
        sess.flush();
        return bean;
    } catch (Exception e) {
      e.printStackTrace();
        throw new ChannelDispatcherException(DbUtil.getStackTraceMessage(e));
    }

}

@Override
public void deleteCancelPolicy(CancelPolicy bean) throws ChannelDispatcherException {
    Session sess = null;

    try {

        sess = HibernateConnector.getInstance().getSession();
        sess.delete(bean);
        sess.flush();
    } catch (Exception e) {
     e.printStackTrace();
        throw new ChannelDispatcherException(DbUtil.getStackTraceMessage(e));
    }

}

@Override
public CancelPolicy findByCancelPolicyData(Integer id, Integer offSetUM, Integer nights, Float pOrAm, Byte isPercent) throws ChannelDispatcherException {

    Session ses = null;
    try {
        ses = HibernateConnector.getInstance().getSession();
        Query query = ses.createQuery("from CancelPolicy a where "
                + " a.cancelPolicyTypeId =:cancelPolicyTypeId  "
                + " and   a.offsetUnitMultiplier =:offsetUnitMultiplier  "
                + " and   a.nights =:nights  "
                + " and   a.percentOramount =:percentOramount "
                + " and   a.isPercent =:isPercent");

        query.setParameter("cancelPolicyTypeId", id);
        query.setParameter("offsetUnitMultiplier", (offSetUM));
        query.setParameter("nights", (nights));
        query.setParameter("percentOramount", pOrAm);
        query.setParameter("isPercent", isPercent);

        List queryList = query.list();
        if (queryList != null && queryList.isEmpty()) {
            return null;
        } else {
            return (CancelPolicy) queryList.get(0);
        }
    } catch (Exception e) {
       e.printStackTrace();
        throw new ChannelDispatcherException(DbUtil.getStackTraceMessage(e));
    } finally {
        if (ses != null) {
            try {
                ses.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

public CancelPolicy findByCancelPolicyId(Integer id) throws ChannelDispatcherException {

    Session ses = null;
    try {
        ses = HibernateConnector.getInstance().getSession();
        Query query = ses.createQuery("from CancelPolicy a where "
                + " a.id =:id  ");

        query.setParameter("id", id);

        List queryList = query.list();
        if (queryList != null && queryList.isEmpty()) {
            return null;
        } else {
            return (CancelPolicy) queryList.get(0);
        }
    } catch ( Exception e) {
       e.printStackTrace();
        throw new ChannelDispatcherException(DbUtil.getStackTraceMessage(e));
    } finally {
        if (ses != null) {
            try {
                ses.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}  

示例主方法

 public static void main(String[] args)  {

       // How to handel Transaction in Hibernate ?
     CancelPolicyDAO cancelPolicyDAO = HibernateDAOFactory.getInstance().getCancelPolicyDAO();


     CancelPolicy insertCancelPolicy = cancelPolicyDAO.findByCancelPolicyData(2, 76, 25, 25.36f, 3);
    if(insertCancelPolicy==null){
        CancelPolicy cancelPolicy = new CancelPolicy();
    cancelPolicy.setCancelPolicyTypeId(1);
    cancelPolicy.setNights(2);
     insertCancelPolicy = cancelPolicyDAO.insertCancelPolicy(cancelPolicy);
    }
    Integer autoIncrementId = insertCancelPolicy.getId();

    AvailabilityDAO availabilityDAO =  HibernateDAOFactory.getInstance().getAvailabilityDAO();
    Availability availability = new Availability();
//        using  CancelPolicy autoIncrementId 
    availability.setId(autoIncrementId);
    availability.setCount(2);
    availability.setMaxLos(5);
    availabilityDAO.insertAvailability(availability);
    .
    .
    .
    .
    .


 }

现在我的问题是如何在DAOImpl中处理事务?我应该为每个DAOImpl传递Session对象作为参数,还是有更好的方法?

1
使用基于AOP的事务管理,可以删除80%的代码。 - Pavel Horal
1
你可以自行实现... 我认为一个好的起点是复制TransactionTemplate的逻辑 - 即一个能够在活动事务/持久化会话中执行回调(例如Callable)的组件。这意味着在调用回调之前启动事务(创建Session),并在调用回调后完成事务(刷新Session,在异常情况下提交或回滚)。 - Pavel Horal
主要目标是消除重复的不必要的 try/catch/finally、回滚逻辑、刷新等。 - Pavel Horal
@PavelHoral 所以您建议我在主方法的第一行创建事务对象,并通过SessionFactory在DAOImpl中访问,这样如果异常发生则在主方法的catch块中回滚?否则提交。 - LMK
1
不完全是这样,但这将是一个很好的开始。正如我所说 - 主要目标是消除DAO方法中不必要的重复逻辑。 - Pavel Horal
显示剩余6条评论
4个回答

11

虽然使用Spring可能是添加事务管理的最佳方式,但即使不重构所有代码库,您仍然可以解决问题。

您需要做的是设置以下配置:

hibernate.current_session_context_class=thread

您的DAO应该通过以下方式访问Session:
[SessionFactory.getCurrentSession()][1];

您仍然需要在服务层中声明事务边界:

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();

    dao1.find();
    dao2.save(entity);

    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e;
}
finally {
    sess.close();
}

DAO1和DAO2将使用相同的Hibernate Session和相同的Transaction。

为了避免冗长的事务管理代码处理,您可以编写一个简单的TransactionManager实用程序,例如:

public static abstract class TransactionCallable<T> {
    public abstract T execute();
}

public class TransactionManager {   
    public static <T> T doInTransaction(TransactionCallable<T> callable) {
        T result = null;
        Session session = null;
        Transaction txn = null;
        try {
            session = sf.openSession();
            txn = session.beginTransaction();

            result = callable.execute();
            txn.commit();
        } catch (RuntimeException e) {
            if ( txn != null && txn.isActive() ) txn.rollback();
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }
        return result;
    }
}

以下是服务的外观:

TransactionManager.doInTransaction(new TransactionCallable<Void>() {
    @Override
    public Void execute() {
        dao1.find();
        dao2.save(entity);
    }
});

非常好。我实际上正在寻找这样的东西。虽然每个人都说Spring是唯一的出路,但对于现有的代码,这些实现方式是可行的,并且不需要添加所有额外依赖项的负担。 - ibelcomputing
SessionFactory实例在哪里? - Eldar Agalarov

10

我强烈建议不要重复造轮子,而是使用现有的、稳健的和经过测试的代码。

评论中已经提到了AOP和Spring框架。在我看来,这是正确的方法。Spring框架甚至有一个名为Spring Data的子项目,允许您以声明方式定义您的查找方法(如findByCancelPolicyData)。

这将为您节省大量工作。

如果由于任何原因您不想/不能使用Spring,仍然可以阅读基础框架和提到的Spring Data的文档,以获得关于事务(通过AOP)、代码重用(通过通用DAO)或API设计的优秀思路。不要错过这个阅读机会。


2
如果您有Web应用程序,且在项目中未使用Spring进行会话管理,我建议使用拦截器将会话处理逻辑分离出DAO。您可以参考下面提到的文章。请注意,这种方法存在某些缺点,但我们发现在我们的情况下这是最方便的方式。 打开视图模式 以下是我们如何使用泛型的Open Session in View模式的示例,对于您的类,我们可以有:
   public class CancelPolicyDAOImpl extends GenericDAOImpl<CancelPolicy, Long> implements CancelPolicyDAO {

   @Override
public CancelPolicy insertCancelPolicy(CancelPolicy bean) throws ChannelDispatcherException {


        try {
          // save method is implemented in GenericDAOImpl described below.
           return save(bean);
        } catch (Exception e) {
            e.printStackTrace();
        } 

        }
//remaining methods
}

在上述代码中使用了save方法,该方法是在GenericHibernateDAOImpl中实现的,如下所示。您可以搜索获取GenericHibernateDAOImpl类,其中包含许多基本操作的附加方法。
public abstract class GenericDAOImpl<T, ID extends Serializable>  {

    private Session session;

 public T save(final T entity) {
        Transaction trans = null;
        try {
            trans = session.beginTransaction();
            session.saveOrUpdate(entity);
            session.flush();
            trans.commit();
        } catch (Exception ex) {
            rollBackTransaction(trans);
// you might require to throw error again here
        }
        return entity;
    }

//in case any complex operations required you can get core session object from this method
    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }
}

现在回答你的问题,关于从哪里获取会话,如上所示,GenericHibernateDAOImpl会话是通过单例sessionFactory通过getCurrentSession方法检索的。请注意,在获取当前会话之前,您需要有活动事务,您可以在拦截器中提供(链接在本帖子开头提供)。
请告诉我。

0

无需使用框架,CancelPolicy对象是主要对象,应具有Hibernate特性(级联)。请查看链接(Cascading life cycle

因此,您可以填充CancelPolicy对象的所有必需关系,然后说保存,它将保存所有相关对象的图形。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接