这是Hibernate会话/事务管理的正确模式吗?

时间:2011-10-21 11:30:43

标签: hibernate design-patterns repository-pattern

我们正在使用JSF 2.0(primefaces)和Hibernate 3.6.1开发一个应用程序 我们正在遵循让应用程序的更高级别与DAL框架无关的方法....这意味着我们不使用每请求会话方法,但我们配置Hibernate来处理会话每线程。 我们已经实现了一个类,它的作用是处理“原子操作”,在它们不存在时打开会话和事务,并简单地将其他数据库操作挂钩到现有事务,使它们成为一部分主要操作,让他们知道他们是独立执行还是作为更大的操作的一部分。 这是我们的OperationManager的代码:

public class OperationManager implements IOperationManager {

    Transaction tx = null;
    boolean isInternalTransaction = false;

    /* (non-Javadoc)
     * @see alekso.npe.dal.IOperationManager#beginOperation()
     */
    @Override
    public Session beginOperation(){            
        Session session = SessionFactoryUtil.getInstance().getCurrentSession();
        if (session.getTransaction().isActive()) {
            isInternalTransaction = false;  
            tx = session.getTransaction();
        }
        else {
            isInternalTransaction = true;
            tx = session.beginTransaction();
        }
        return session;
    }

    /* (non-Javadoc)
     * @see alekso.npe.dal.IOperationManager#commitOperation()
     */
    @Override
    public void commitOperation(){
        if (isInternalTransaction)
            tx.commit();
    }

    /* (non-Javadoc)
     * @see alekso.npe.dal.IOperationManager#rollbackOperation()
     */
    @Override
    public void rollbackOperation(){
        if (isInternalTransaction)
            tx.rollback();
    }
}

SessionFactoryUtil类是hibernate的“经典”工厂工具,随处可见......我们选择了这个实现:

public class SessionFactoryUtil {

final static Logger log = Logger.getLogger(SessionFactoryUtil.class);

/** The single instance of hibernate SessionFactory */
private static org.hibernate.SessionFactory sessionFactory;

/**
 * disable contructor to guaranty a single instance
 */
private SessionFactoryUtil() {
}

static {
    // Annotation and XML
    // sessionFactory = new
    // AnnotationConfiguration().configure().buildSessionFactory();
    // XML only
    try {
        sessionFactory = new Configuration().configure().buildSessionFactory();
    } catch (Exception e) {
        log.error("Errore nella creazione del session factory", e);
        System.out.println("Errore nella creazione del session factory");
    }

}

public static SessionFactory getInstance() {
    try{
        return sessionFactory;
    } catch (Exception ex)
    {
        log.error("errore nella creazione della session di hibernate", ex);
        return null;
    }
}

/**
 * Opens a session and will not bind it to a session context
 * 
 * @return the session
 */
public Session openSession() {
    return sessionFactory.openSession();
}

/**
 * Returns a session from the session context. If there is no session in the
 * context it opens a session, stores it in the context and returns it. This
 * factory is intended to be used with a hibernate.cfg.xml including the
 * following property <property
 * name="current_session_context_class">thread</property> This would return
 * the current open session or if this does not exist, will create a new
 * session
 * 
 * @return the session
 */
public Session getCurrentSession() {
    return sessionFactory.getCurrentSession();
}

/**
 * closes the session factory
 */
public static void close() {
    if (sessionFactory != null)
        sessionFactory.close();
    sessionFactory = null;

}
}

现在,我们如何使用运营经理? 简单地说,无论谁想要对db上的原子操作负责,都可以在OperationManager上调用该方法。

public class BL1{
    public void highMethod() {      
        IOperationManager om = new OperationManager();
        BL2 bl2 = new BL2();
        BL3 bl3 = new BL3();
        try {           
            om.beginOperation();            

            bl2.midMethod();
            bl3.midMethod();

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
}

public class BL2{
    public void midMethod() {       
        IOperationManager om = new OperationManager();
        DAL1 dal1 = new DAL1();
        DAL2 dal2 = new DAL2();
        try {           
            om.beginOperation();            

            dal1.lowMethod1();
            dal2.lowMethod1();

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
}

public class BL3{
    public void midMethod() {       
        IOperationManager om = new OperationManager();
        DAL1 dal1 = new DAL1();
        DAL2 dal2 = new DAL2();
        try {           
            om.beginOperation();            

            dal1.lowMethod2();
            dal2.lowMethod2();

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
}

public class DAL1{
    public void lowMethod1() {      
        IOperationManager om = new OperationManager();
        Session session = nullM
        try {                                   
            session = om.beginOperation();

            // do some work on session
            session.saveOrUpdate(...);
            session.load(..);
            session.somethingElse(....);

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
    public void lowMethod2() {      
        IOperationManager om = new OperationManager();
        Session session = nullM
        try {                                   
            session = om.beginOperation();

            // do some work on session
            session.saveOrUpdate(...);
            session.load(..);
            session.somethingElse(....);

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
}

public class DAL2{
    public void lowMethod1() {      
        IOperationManager om = new OperationManager();
        Session session = nullM
        try {                                   
            session = om.beginOperation();

            // do some work on session
            session.saveOrUpdate(...);
            session.load(..);
            session.somethingElse(....);

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
    public void lowMethod2() {      
        IOperationManager om = new OperationManager();
        Session session = nullM
        try {                                   
            session = om.beginOperation();

            // do some work on session
            session.saveOrUpdate(...);
            session.load(..);
            session.somethingElse(....);

            om.commitOperation();           
        } catch (Exception ex) {
            omrollbackOperation();      
        }       
    }
}    
}

这样做,如果我调用BL1.highMethod,下面的每一个(从内部收集的方法)将在它开始的转换下:调用方法的om.beginOperation(),只返回由BL1启动的会话.highMethod,以及所调用方法的om.commitOperation或om.rollBackOperation,根本不做任何事情,留下在BL1.highMethod中创建的om实例提交/回滚和关闭会话的责任。 但是如果我们直接调用BL2.midMethod,那么管理会话和事务的责任就是它自己的。如果我们直接调用DAL1.lowMethod1,也会发生同样的情况。 模式是:如果我(作为一种方法)没有调用beginOperation,我用来完成工作的所有东西都将自己处理会话/事务;如果我开始操作,那么我将负责管理会话/转换。 我希望我说清楚。

现在,在我看来,这是一种非常聪明的方法......直到出现问题。 出了什么问题,真的很难说。 简单的应用程序开始变得怪异:有时应用程序在更新期间(数据库谈论锁定行,但查看刷新到db的sql语句的序列,在没有意义的情况下)在db上创建锁定;有时没有锁定,也没有记录错误,但是应用程序似乎读取了“旧数据”...就像我们几秒前做的操作一样,不存在......而且它似乎就在那里30秒之后(或10或60 ......这取决于)。我的意思是,非常剧烈的行为。我们只看到日志上的一些错误,当我们点击db上的rowlock时,这会导致一个长时间的事务,否则似乎没有错。为了使事情变得更复杂,应用程序上的用户操作的相同确切顺序(在同一记录上,由同一用户等...)有时可以工作,有时不工作。 这种行为发生在我们使用OperationManager类重构引入上面显示的模式的代码时。

那里的任何人都知道我们做错了什么,为什么这种模式不能按预期工作? 我对发布的问题的长度表示道歉......

2 个答案:

答案 0 :(得分:0)

  1. 除了kludge之外,SessionFactoryUtil不是“经典”的东西。它如此普遍的唯一原因是因为它被用作first chapter of the Hibernate reference guide教程的一部分。它只是 tutorial 代码,即使参考指南从未说过,我也会:此代码永远不会用于生产。
  2. 你陷入了许多人试图重写已被数千名开发人员编写,验证和重用的代码的陷阱。见this answer to a similar question

答案 1 :(得分:0)

实际上我解决了输入上述问题的问题! 在代码的最后一部分,调用OperationManager的类和方法,我声明并在方法内部实现了OperationManager对象。这种方式将标志isInternalTransaction绑定到方法本身。 在我的实际代码中,在类中我在类级别声明了OperationManager:这意味着调用同一个类实例的不同方法,打破了模式,因为isInternalTransaction falg从owner方法外部修改了! 所以这是我唯一的错误:我使用我为模式创建的服务,而不是模式本身。 现在它很好用,即使没有Spring。

这对我有好处,不仅仅是因为我不需要学习春天,而是因为我更喜欢在问题/模式中深入挖掘,并理解为什么以及如何运作......不只是采取有效的方法,并认为它是神奇的;)