[英]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.