在Transactional方法中并发读取/保存对象时出错

时间:2013-05-20 10:10:55

标签: mysql spring hibernate concurrency locking

使用并发请求线程保存数据时遇到问题。细节如下:

  1. Spring 3.0.5
  2. Tomcat 1.6
  3. Hibernate 3
  4. 通过示例应用程序模拟问题:

    数据库明细:

    1. 用户
    2. 用户信息
    3. 一个用户可以包含多个 userinfo 表示这些表之间存在oneToMany关系。

      域类

      用户类

      @Entity
      @Table(name = "user")
      public class User implements java.io.Serializable {
      
          private static final long serialVersionUID = 5753658991436258019L;
          private Integer idUser;
          private String Name;    
          private Set<UserInfo> userInfos = new HashSet<UserInfo>(0);
      
          public User() {
          }
      
          @Id
          @Column(name = "iduser", unique = true, nullable = false)
          public Integer getIdUser() {
              return idUser;
          }
      
          public void setIdUser(Integer idUser) {
              this.idUser = idUser;
          }
      
          @Column(name = "name", nullable = false, length = 256)
          public String getName() {
              return this.Name;
          }
      
          public void setName(String tenantName) {
              this.Name = tenantName;
          }
      
          @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
          public Set<UserInfo> getUserInfos() {
              return userInfos;
          }
      
          public void setUserInfos(Set<UserInfo> userInfos) {
              this.userInfos = userInfos;
          }
      }
      

      UserInfo类

      @Entity
      @Table(name = "userInfo")
      public class UserInfo implements java.io.Serializable {
      
          private static final long serialVersionUID = 5753658991436258019L;
          private Integer iduserInfo;
          private User user;
          private String address; 
      
          public UserInfo() {
          }
      
          @Id
          @GeneratedValue(strategy = IDENTITY)
          @Column(name = "iduserInfo", unique = true, nullable = false)
          public Integer getIduserInfo() {
              return iduserInfo;
          }
      
          public void setIduserInfo(Integer iduserInfo) {
              this.iduserInfo = iduserInfo;
          }
      
          @ManyToOne(fetch = FetchType.LAZY)
          @JoinColumn(name = "userId", nullable = false)
          public User getUser() {
              return user;
          }
      
          public void setUser(User user) {
              this.user = user;
          }
      
          @Column(name = "address", nullable = false, length = 256)
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      }
      

      所以我想通过hibernate在db中保存 UserInfo ,但在保存UserInfo之前 创建用户并尝试从中获取如果找到DB,则在UserInfo对象中设置,否则保存它(User)然后再从DB中获取(对于持久用户obejct的stat)。然后在UserInfo中设置并在DB中保存UserInfo对象。

      注意:这一切在顺序请求中都能正常工作

      但是当同一个用户对象需要多个UserInfo的并发请求时,即使在保存后也无法加载User对象。

      ServiceLayer方法 saveUserAndInfo 如下:

      @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
      @Override
      public void saveUserAndInfo(String tenantName) throws Exception {
      
          User user = new User();
          user.setName("Name - " + tenantName + " - "
                  + System.currentTimeMillis());
      
      
          UserInfo userInfo = new UserInfo();
          userInfo.setAddress("India - " + tenantName + " - "
                  + System.currentTimeMillis());
          Set<UserInfo> setUserInfos = new HashSet<UserInfo>();
          setUserInfos.add(userInfo);
      
          user.setIdUser(1);
      
          user = findByCriteria(user);
          userInfo.setUser(user);
      
          saveUserInfo(userInfo);
      }
      
      /**
       * This method will return <code>User</code> object in persistent state, 
       * 
       * If exist fetch from DB, it NOT then insert and again fetch from db 
       * @param user
       * @return
       * @throws Exception
       */
      public User findByCriteria(User user) throws Exception {
      
          List<Criterion> criterionList = new ArrayList<Criterion>();
          criterionList.add(Restrictions.eq("idUser", user.getIdUser()));
      
          Criteria crit = openSession().createCriteria(User.class);
      
          for (Criterion c : criterionList) {
              crit.add(c);
          }
          List<User> entityList = null;
          try {
              entityList = crit.list();
          } catch (Exception ex) {
              ex.printStackTrace();
          }
      
          if (entityList == null || entityList.isEmpty()) {
              log.error("No User Found - " + user.getIdUser() + " now adding...");
              saveUser(user);
              try {
                  crit = openSession().createCriteria(User.class);
                  entityList = crit.list();
              } catch (Exception ex) {
                  ex.printStackTrace();
              }
              if (entityList != null && !entityList.isEmpty()) 
                  return entityList.get(0);
              else {
                  log.error("No User Found - " + user.getIdUser() + " Even after adding");
                  throw new Exception("No User Found - " + user.getIdUser() + " Even after adding");
              }
          }
          else {
              return entityList.get(0);
          }
      }
      

      请帮助!!!

0 个答案:

没有答案