Hibernate - 锁定问题

时间:2014-11-29 12:26:12

标签: java hibernate

我在各种情况下质疑我的锁定实现,我想让用户提出一些比我更专业的建议。 我正在使用两个支持类,名为HibernateUtil和StorageManager。

的HibernateUtil

只返回会话工厂的单例实例;显然,它会在第一次调用时创建会话工厂。

StorageManager

包含各种实体之间的常见操作。在创建时,它从HibernateUtil获取会话工厂并将其存储到静态变量中。 这个类实现了每个请求的会话(或者每个操作的会话)模式,因此对于每种请求,它基本上按顺序执行这些操作:

  1. 打开一个新会话(来自先前存储的会话工厂)
  2. 开始新的交易
  3. 执行特定请求(取决于调用的StorageManager的具体方法)
  4. 提交交易
  5. close session
  6. 当然,非常感谢对这种风格的评论。

    然后,基本上有3类操作实现了第3点

    插入,更新或删除实体

    session.save(entity); 
    // OR session.update(entity) OR session.delete(entity)
    session.buildLockRequest(LockOptions.UPGRADE).lock(entity);
    

    获取实体

    T entity = (T) session.byId(type).with(LockOptions.READ).load(id);
    // There are other forms of get, but they are pretty similar
    

    获取列表

    List<T> l = session.createCriteria(type).list();
    // Same here, various but similar forms of get list
    

    同样,不知道这是否是实施各种行动的正确方法。 此外,这是真正的问题,每当发生错误时,即使从命令行,也无法以任何方式访问数据存储区,直到我手动停止导致问题的应用程序。我该如何解决这个问题呢?

    提前致谢。


    修改

    更多代码 这是上面列出的部件的代码。

    private void createTransaction()  // Parts 1 and 2 of the above list
    {
        session = sessionFactory.openSession();
        transaction = null;
        try
        {
            transaction = session.beginTransaction();
        }
        catch (HibernateException exception)
        {
            if (transaction != null) transaction.rollback();
            exception.printStackTrace();
        }
    }
    
    private void commitTransaction() // Part 4 of the above list
    {
        try
        {
            transaction.commit();
        }
        catch (HibernateException exception)
        {
            if (transaction != null) transaction.rollback();
            exception.printStackTrace();
        }
    }
    
    private void closeSession() // Part 5 of the above list
    {
        try
        {
            // if (session != null)
            {
                session.clear();
                session.close();
            }
        }
        catch (HibernateException exception)
        {
            exception.printStackTrace();
        }
    }
    
    public void update(T entity) // Example usage for part 3 of the above list
    {
        try
        {
            this.createTransaction();
            session.update(entity);
            // session.buildLockRequest(LockOptions.UPGRADE).lock(entity);
            this.commitTransaction();
        }
        catch (HibernateException exception)
        {
            exception.printStackTrace();
        }
        finally
        {
            this.closeSession();
        }
    }
    

2 个答案:

答案 0 :(得分:2)

您的错误案例(真正的问题)表明您没有遵循典型的交易使用习惯用法(来自Session Javadoc):

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    //do some work, point 3
    tx.commit();
} catch (Exception e) {
    if (tx!=null) tx.rollback();
    throw e;
} finally {
    sess.close();
}

请注意catch和finally块,以确保在发生错误时释放任何数据库资源。 (*)

我不确定为什么要在更改数据库记录(插入,更新或删除实体)后锁定数据库记录(LockOptions.UPGRADE)。您通常在(读取)更新数据库记录之前锁定数据库记录,以便确保获取最新数据,并且使用相同数据库记录的其他打开事务不会干扰(读取和)更新。

锁定对插入操作没有多大意义,因为默认事务隔离级别是&#34;读取已提交&#34;(1)这意味着当事务插入记录时,该记录仅对其他数据库事务可见。插入记录提交的事务。即在交易提交之前,其他交易无法选择和/或更新新的&#34;尚未评估的&#34;插入记录。

(1)仔细检查以确保。搜索&#34; hibernate.connection.isolation&#34;在Hibernate configuration章节中。价值应该是&#34; 2&#34;如Java Connection常量字段值所示。

(*)在锁定数据库记录后数据库连接丢失时,存在令人讨厌的极端情况。在这种情况下,客户端无法提交或回滚(因为连接已断开),并且数据库服务器可能会永久锁定记录。一个好的数据库服务器将解锁由被破坏和丢弃的数据库连接锁定的记录(并回滚该断开的数据库连接的任何打开的事务),但是不能保证(例如,数据库服务器如何以及何时发现数据库连接中断? )。这是稀疏地使用数据库记录锁的原因之一:只有当使用数据库记录的应用程序无法阻止对同一数据库记录的并发/同时更新时,才尝试使用它们。

答案 1 :(得分:0)

为什么不使用Spring Hibernate / JPA支持。然后,您可以使用单个SessionFactory,并使用@Transactional显式设置事务边界。

会话由Transactioninterceptor自动管理,因此无论您从服务调用多少DAO,所有这些都将使用相同的线程绑定Session。

实际的Spring配置要比实现当前的解决方案容易得多。

如果您不打算使用Spring,那么您必须确保实际实现了每个请求的会话模式。如果您使用的是session-per-operation anti-pattern,那么您将无法将两项或多项操作纳入单个工作单元。

session-per-request pattern需要外部拦截器/ AOP方面来打开/关闭并将当前会话绑定到当前调用线程。您可能还想配置此属性:

hibernate.current_session_context_class=thread

这样Hibernate就可以将当前Session绑定到当前线程的本地存储中。