繁体   English   中英

Java:在多线程和事务性环境中运行DAO类

[英]Java: Running DAO class in multithreaded and transactional environment

我正在从多线程环境中调用DAO类,该类将触发选择查询以从表中获取行(获取ticketId),然后更新同一行(带有customerId)。 这在同一事务中发生。 我的数据库是SQL Server。 当我触发选择查询时,我尝试放置一个行级别的锁(WITH ROWLOCK),以便其他线程不会得到同一行。 我的DAO类如下(此处仅显示重要的代码):

public void saveCustomerTicketUsingJDBC(String customerId) {
  Session session = getSession();
  //Session session = SessionFactoryUtils.getSession(getSessionFactory(), true);// Have tried this too
  try {
         session.getTransaction().begin();



        Query query1 = session.createSQLQuery("select TOP 1 * from CustomerTicket WITH (ROWLOCK) where customerId is null");
        Object[] customerTicket = (Object[])query1.uniqueResult();

             Integer id = (Integer)customerTicket[0];
             ticketId = (String)customerTicket[1];
             logger.debug("Got ticket id -->"+ticketId);



        Query query2 = session.createSQLQuery("update CustomerTicket " +
                                                "set customerId = :customerId " +
                                                "where ticketId = :ticketId");
        query2.setParameter("customerId", customerId);
        query2.setParameter("ticketId", ticketId);
        logger.debug("QUery 2 executeUpdate : customerId : "+customerId+", ticketId :"+ticketId);
        int result = query2.executeUpdate();
        logger.debug("result >"+result +", customerTicketId ----------->"+customerId+", ticketId ------------>"+ticketId);
        //session.flush();
        //session.clear();
        session.getTransaction().commit();
  } catch (Exception e) {
        logger.error("Exception while saving customer ticket-->"+e,e);
    } finally {
        if (session != null) {
            session.close();
        }
    }
}

我产生了4个线程。 我在日志文件中看到的是,所有四个线程都到达了数据库表中的同一条记录。

2014-02-26 22:41:29.183 DEBUG [pool-3-thread-2] CustomerTicketDAO.java:83 Got ticket id -->4
2014-02-26 22:41:29.183 DEBUG [pool-3-thread-4] CustomerTicketDAO.java:83 Got ticket id -->4
2014-02-26 22:41:29.184 DEBUG [pool-3-thread-3] CustomerTicketDAO.java:83 Got ticket id -->4
2014-02-26 22:41:29.184 DEBUG [pool-3-thread-1] CustomerTicketDAO.java:83 Got ticket id -->4

首先,这不应该发生,对吗? 我期望看到每个线程应该获得不同的行。

然后,我看到只有一个线程能够成功更新数据库。

2014-02-26 22:41:29.408 DEBUG [pool-3-thread-1] CustomerTicketDAO.java:93 result >1, customerTicketId ----------->CustomerId_0, ticketId ------------>4

该行的其他三个线程死亡:

int result = query2.executeUpdate();

我不了解其他三个线程的情况,因为我在日志文件中看不到任何内容。

有人请在这里帮助我。

谢谢拉吉

目前尚不清楚(至少对我来说),在3个失败的TX的情况下,究竟Sql Server和hibernate会返回什么错误。 但是他们失败了也就不足为奇了。

(行)锁不是队列,也不是过滤器。 四个选择查询将返回同一行也就不足为奇了,因为锁的存在不会更改数据,因此也不会更改查询结果-它只是保护对数据的访问 我怀疑但不知道,通过休眠缓存查询可以绕过锁定。

根本问题是,您有四个进程争用一个资源(第一个未分配的票证)。 尽管正确实施的行锁定方案可以工作,但它是一个糟糕的选择,因为它的伸缩性不及其他方案。

您最好使用同步块来编写dao。 这将处理appserver中的多个线程同时争用资源的情况。 最简单的方法是:

public synchronized void saveCustomerTicketUsingJDBC(String customerId) {
    Session session = getSession();
    ...
}

尽管您需要知道并不能保证线程的执行顺序,但这可以很好地处理单个appserver的情况。 根据您的变量名,我怀疑这可能对您来说是个问题,但是即使这样,此解决方案也不会使问题变得更糟。

如果您有多个应用服务器,那么仍然可以有多个进程争用同一资源。 同样,这可以通过(悲观的)行锁来解决,但是我怀疑使用乐观的锁解决方案会更好。 乐观锁如下所示:

while (true) {
  session.getTransaction().begin();

  try {  
    Query query1 = session.createSQLQuery("select TOP 1 * from CustomerTicket where customerId is null");
    Object[] customerTicket = (Object[])query1.uniqueResult();

    Integer id = (Integer)customerTicket[0];
    ticketId = (String)customerTicket[1];
    logger.debug("Got ticket id -->"+ticketId);

    Query query2 = session.createSQLQuery("update CustomerTicket " +
                                            "set customerId = :customerId " +
                                            "where ticketId = :ticketId AND customerId is NULL");  
                                            // Notice the AND clause!!!
    query2.setParameter("customerId", customerId);
    query2.setParameter("ticketId", ticketId);
    logger.debug("QUery 2 executeUpdate : customerId : "+customerId+", ticketId :"+ticketId);
    int updateCount = query2.executeUpdate();
    logger.debug("updateCount >"+updateCount +", customerTicketId ----------->"+customerId+", ticketId ------------>"+ticketId);

    //  Did someone beat us to it? 
    if (updateCount == 0) {
        session.getTransaction().rollback();
        continue;
    }

    // Nope - we're winning so far, but the race isn't over yet...
    session.getTransaction().commit();
  } catch (OptimisticLockException ex) {
      logger.debug("Darn, someone DID beat us to it");
      session.getTransaction().rollback();
      continue;
  } catch (Exception ex) {
      ...
  }

  break;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM