这是Hibernate会话/事务管理的正确模式吗? [英] Is this a right pattern for Hibernate session/transaction management?

查看:241
本文介绍了这是Hibernate会话/事务管理的正确模式吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  public class OperationManager实现IOperationManager {

事务tx = null;
boolean isInternalTransaction = false;

/ *(非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;
}

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

/ *(非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);

/ ** hibernate的单实例SessionFactory * /
private static org.hibernate.SessionFactory sessionFactory;

/ **
*禁用引用以保证单个实例
* /
private SessionFactoryUtil(){
}

static {
// Annotation and XML
// sessionFactory = new
// AnnotationConfiguration()。configure()。buildSessionFactory();
//仅XML
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);
返回null;
}
}

/ **
*打开会话,不会将其绑定到会话上下文
*
* @return会话
* /
public Session openSession(){
return sessionFactory.openSession();
}

/ **
*从会话上下文返回会话。如果
*上下文中没有会话,它会打开会话,将其存储在上下文中并返回。这个
* factory旨在与hibernate.cfg.xml一起使用,包括
*以下属性< property
* name =current_session_context_class> thread< / property>这将返回
*当前打开的会话,或者如果这不存在,将创建一个新的
*会话
*
* @返回会话
* /
public Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}

/ **
*关闭会话工厂
* /
public static void close(){
if(sessionFactory! = null)
sessionFactory.close();
sessionFactory = null;

}
}

现在,我们如何使用经营经理?
简单来说,无论谁负责在数据库上进行原子操作,请调用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();

//在会话上做一些工作
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();

//在会话上做一些工作
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();

//在会话上做一些工作
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();

//在会话上做一些工作
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,我用来完成我的工作的所有东西都将自己处理会话/事务;如果我开始运行,那么管理会话/转移就是我的责任。
我希望我清楚。



现在,在我看来,似乎是一个非常聪明的方法,直到出现问题。
出了什么问题,真的很难说。
简单的应用程序开始行动很奇怪:有时应用程序在数据库上创建一个锁,在更新期间(数据库谈论一个锁定行,但是查看sql语句的顺序刷新到db,它没有感);有时没有锁,没有错误记录,但应用程序似乎读取旧数据...像我们几秒钟前完成的操作,不在那里...似乎在30秒之后(或10,或60 ...这取决于)。我的意思是,非常有力的行为。我们只在日志上看到一些错误,我们点击了db上的rowlock,这导致了一个长时间的实时交易,否则似乎没有错误。为了使事情变得更复杂,与应用程序(同一记录,同一用户等)上的用户操作相同的精确顺序有时候会有效。
这个行为发生在我们重构引入上面显示的模式的代码时,使用OperationManager类。



任何人都有一个想法,做错了,为什么这种模式不按需要工作?
我对发布的问题的长度表示歉意...

解决方案

其实我解决了我在上面输入问题的问题!
在代码的最后一部分,调用了OperationManager的类和方法,我在方法内部声明和说明了OperationManager对象。这样标志isInternalTransaction就被绑定到方法本身。
在我的真实代码中,在类中我在类级别声明了OperationManager:这意味着调用同一个类实例的不同方法,破坏了模式,因为isInternalTransaction falg从外部改变了所有者的方法!
这是唯一的bug:我使用我为模式创建的服务,而不是模式本身。
现在它的效果很好,即使没有Spring。



这对我有好处,不仅仅是因为我不必学习春天只是因为这也是因为我更喜欢深入挖掘一个问题/模式,并且明白为什么和如何运作...不仅仅是采取一些有用的东西,而且考虑到它的魔力;)


we're developing an application using JSF 2.0 (primefaces) and Hibernate 3.6.1 We're following the approch of letting the higher level of the application be agnostic about the DAL framework.... that means that we don't use the session-per-request approach, but we configured Hibernate to handle the sessione on a per-thread basis. We've implemented a class whose role is to handle an "atomic operation", taking care of open the session and the transaction when they don't exixt already, and to simply hook other db operation to the existing transaction, to make them part of the main operation, witount letting them know if they're executing independently or as part of a bigger operation. This is the code for our 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();
    }
}

The SessionFactoryUtil class, is the "classic" factory util for hibernate, suggested everywhere... we picked up this implementation:

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;

}
}

Now, how do we use the operation manager? Simply, whoever is intended to be responsable for an atomic operation on the db, call the method on 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();      
        }       
    }
}    
}

Doing so, if I call the BL1.highMethod, everyting below (al the method colled from inside) will be under the transiction it started: the om.beginOperation() on the method called, simply returns the session started by BL1.highMethod, and the om.commitOperation or om.rollBackOperation of the method called, simply do nothing at all, leaving at the om instance created in BL1.highMethod the responsability to commit/rollback and close the session. But if we call directly BL2.midMethod, the responsability for managing session and transaction, will be its own. The same happen if we call directly DAL1.lowMethod1. The pattern is: if I (as a method) didn't called beginOperation, everything I use to get my job done, will handle the session/transaction on its own; if I beginOperation, it'll be my responsability to manage session/transiction. I hope I made it clear.

Now, it seemed to me to be a very smart approach... until something goes wrong. What is going wrong, it's really hard to tell. Simply the application start to act weird: sometimes the application create a lock on the db, during an update (the database talk about a locking row, but looking at the sequence of sql statement flushed to the db, it has no sense); sometimes there's no lock, and no error logged, but the application seems to read "old data"... like if the operation we did couple of seconds ago, is not there... and it appear to be there just after 30 seconds (or 10, or 60... it depends). I mean, very strage behaviour. We only see some error on the log, whne we hit the rowlock on the db, that brings to a long live transaction, otherwise nothing seems wrong. To make things more complicated, the same exact sequence of user operation on the application (on the same record, byy the same user, etc...) sometimes work, sometimes not. And this behaviour happen from when we refactor the code introducing the pattern I showed above, using the OperationManager class.

Anyone out there has an idea of what we did wrong, why this pattern is not working as desired? I apologies for the lenght of the question posted...

解决方案

Actually I solved my issues typing the question above! In the last part of code, the class and method that called the OperationManager, I declared and istantiate the OperationManager object right inside the method. This way the flag isInternalTransaction is bound to the method itself. In my real code, in a class I declared OperationManager at class level: this means that calls to different methods of the same class instance, broke the pattern, because the isInternalTransaction falg where modified from outside the owner method! So that's the only bug I had: the use I made of the service I created for the pattern, not the pattern itself. Now it works great, even without Spring.

That's good for me, not just for the reason that I don't have to learn spring just beacause of this, but also because I prefer to dig deeper in a problem/pattern, and understand why and how it works... not just taking something that works, and consider it magic ;)

这篇关于这是Hibernate会话/事务管理的正确模式吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆