【问题标题】:Transaction Management in Hibernate with DAO Design PatternHibernate 中的事务管理与 DAO 设计模式
【发布时间】:2015-01-26 00:24:22
【问题描述】:

我有很多表,对于每个表,我们都有 DAO 接口和 DAOImplementation 类。

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;
}

示例 DAO 实现类

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 的参数传递还是有更好的方法

【问题讨论】:

  • 使用基于 AOP 的 TX 管理并删除 80% 的代码。
  • 您使用的是 Spring 框架吗?如果是这样,看看这个:docs.spring.io/spring-framework/docs/current/…
  • @Anarki no im 仅使用 Jse(core java)
  • 你可以自己实现它......我认为一个好的起点是复制TransactionTemplate逻辑 - 即能够在活动内部执行回调(例如Callable)的组件事务/持久会话。这意味着在调用回调之前启动事务(创建Session)并在调用回调之后完成事务(刷新Session,在异常情况下提交或回滚)。
  • 不完全是,但这将是一个好的开始。正如我所说 - 主要目标是摆脱 DAO 方法中不必要的重复逻辑。

标签: java hibernate design-patterns transactions dao


【解决方案1】:

我强烈建议不要重新发明轮子;使用现有的、健壮的和经过测试的代码。

cmets 已经提到了 AOP 和Spring 框架。这是恕我直言的方法。 Spring 框架甚至有一个名为Spring Data 的子项目,它允许您以声明的方式定义您的查找器方法(如findByCancelPolicyData)。

这将为您节省大量工作。

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

【讨论】:

    【解决方案2】:

    虽然使用 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 是唯一且唯一的方法,但对于现有代码,这些类型的实现工作,额外的好处是不需要添加所有额外的依赖项。
    • SessionFactory 实例在哪里?
    【解决方案3】:

    如果您有 Web 应用程序并且您没有在项目中使用 spring 进行会话管理,我建议使用拦截器将会话处理逻辑分离到您的 DAO 之外。您可以参考下面提到的文章。请注意,这种方法存在一定的缺点,但我们发现它是我们案例中最方便的方法。

    Open Session in View Pattern

    下面是我们如何将 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
    }
    

    在上面的代码中使用了保存方法,该方法在 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 会话是通过 getCurrentSession 方法从单例 sessionFactory 中检索的。请注意,在获取当前会话之前,您需要运行活动事务,您可以在拦截器中提供该会话(本文开头提供的链接)。

    请告诉我。

    【讨论】:

      【解决方案4】:

      不需要使用框架,CancelPolicy 对象是主要对象,它应该具有休眠功能(级联)检查链接(Cascading life cycle

      所以你用所有需要的关系填充 CancelPolicy 对象,然后说保存,它将保存相关对象的所有图形。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-05
        • 2012-08-21
        • 1970-01-01
        • 2012-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-02
        相关资源
        最近更新 更多