2012-03-01 65 views
1

我很感谢帮助理解为什么我的应用程序无法找到一个实体,即使它存在于数据库中;我相信这个问题涉及并发的写作/阅读。我正在使用jpa2 /休眠4和弹簧3.并行写作和阅读JPA2 /休眠

我有一个方法,创建一个用户,然后将ID作为JSON对象消息发送到用户进一步处理的消息队列。当消息处理程序(UserProcessor.class)尝试使用find用户时(见下文),会发生问题。

Registration.class

@Transactional 
public Response createUser(String firstName, String lastName) { 
    User tmpUser = new User(firstName, lastName); 
    User savedUser = this.em.merge(tmpUser); 

    this.em.flush(); 

    if (savedUser != null) { 
    processUser(savedUser.getId()); // message sent to queue. 
    } else { 
    // Throw exception... 
    } 
} 

UserProcessor.class

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class}) 
public void processUser(Long id) { 
    User user = this.em.find(User.class, id); // No user entity is found, "user" is null. 
    if (user == null) { 
    // throw exception 
    } 
    ... 
} 
+0

为什么不写你的方法直接传递用户作为参数:public void processUser(final User aUser)'? – 2012-03-01 20:50:38

+0

而您面临的问题可能与事务相关。 – 2012-03-01 20:51:42

+0

@nico_ekito:因为消息系统被java和非java服务使用,所以我选择使用json数据结构而不是将消息系统绑定到java。 – Ari 2012-03-01 21:09:39

回答

0

您可以使用两种方法:

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class}) 
public void processUser(Long id) { 
    processUser(this.em.find(User.class, id)); 
} 

public void processUser(User aUser) { 
... 
} 
1

我认为你可能有一个并发问题。

据我理解你的代码,它的工作原理是这样的:

Registration.createUser:

  • 步骤A1),打开交易(I)
  • 步骤A2)创建用户和步骤A4)将用户标识放入队列
  • 步骤A4)提交交易(I)

    UserProcessor.processUser(长ID)

  • 步骤B1)开从队列

  • 步骤B2)的用户ID打开一个事务(II)
  • 步骤B3)由加载用户其交易中的ID(II)
  • 步骤B4)做的东西
  • 步骤B5)提交事务(II)

您知道(取决于您的事务隔离级别),只有在提交第一个事务(I)时,写入事务(I)的数据才可以在其他事务(II)中读取。

因此,如果UserProcessor.processUser在步骤A4中提交事务(I)之前尝试处理步骤B3,它将​​不会在数据库中看到用户。 (如果使用更高的事务隔离级别,则可能需要在B2之前执行步骤A4。)

一种解决方法是切换步骤A3和A4的顺序。重要的一点:如果在其他(外部)事务的上下文中调用方法Response.createUser,那么它将与外部事务一起提交!