当两个事务同时保存数据时,Hibernate集合数据丢失

时间:2015-01-25 20:32:03

标签: mysql hibernate concurrency transactions jta

我面临着一个奇怪的问题,即失去"使用Hibernate更新数据时的数据。

应用程序部署在JBoss AS 7上,数据库连接隔离级别 MySql 数据库设置为READ_COMMITED

我通过继承来定义实体,如下所示:

@Entity
@Table(name = "element")
@Inheritance(strategy=InheritanceType.JOINED)
public class ElementEntity {

  @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="i_element", nullable=false)
    private Integer iElement;

    @Column(name="hash")
    private String hash;

  ...
}

@Entity
@Table(name = "element_a")
@PrimaryKeyJoinColumn(name="i_element")
public class AElementEntity extends ElementEntity {
   ...  // some primitive fields
}

@Entity
@Table(name = "element_b")
@PrimaryKeyJoinColumn(name="i_element")
public class BElementEntity extends ElementEntity {
      ...  // some primitive fields
}

@Entity
@Table(name = "element_c")
@PrimaryKeyJoinColumn(name="i_element")
public class CElementEntity extends ElementEntity {
      ...  // some primitive fields

      @OneToMany(mappedBy="cElement", cascade=CascadeType.ALL)
        private List<NodeEntity> nodes;
}


@Entity
@Table(name="node")
public class NodeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "i_node")    
    private Integer iNode;

    @ManyToOne
  @JoinColumn(name="i_element", referencedColumnName="i_element")
    private CElementEntity cElement;

  ...  // some primitive fields
}
像这样的

和dao实现:

public class ElementDaoImpl implements ElementDao {
    private static final Logger logger = Logger.getLogger(ElementDaoImpl.class);

    private EntityManager entityManager;

    public ElementDaoImpl(EntityManager entityManager) {
        super();
    }

    @Override
    public List<ElementEntity> load(String hash) {

        // nacist vsechny elementy pro SGL
        Query query = entityManager.createQuery("FROM " + ElementEntity.class.getSimpleName() + " WHERE hash = :hash");
        query.setParameter("hash", hash);
    List<ElementEntity> elements  = (List<ElementEntity>) query.getResultList();
    //.. log C element
        return elements;
    }

    @Override
    public void save(String hash, List<ElementEntity> elements) {
        removeElementsByHash(hash);

        for (ElementEntity element : elements) {
            if (logger.isDebugEnabled()) {
                if (element instanceof CElement) {
                    CElementEntity cElementEntity = (CElementEntity) element;
                    logger.debug("Storing for hash " + hash + " nodes " + cElementEntity.getNodes());
                }
            }
            entityManager.persist(entity);
            entityManager.flush();
        }

        entityManager.clear();

    }

    private void removeElementsByHash(String hash) {
        Query query = getEntityManager().createQuery("FROM " + ElementEntity.class.getSimpleName() + " WHERE hash = :hash");
        query.setParameter("hash", hash);
        List<ElementEntity> entities = (List<ElementEntity>) query.getResultList();

        if (entities != null && !entities.isEmpty()) {
            for (ElementEntity entity : entities) {
                entityManager.remove(entity);
            }
        }
        entityManager.flush();
        entityManager.clear();
    }
}

dao在EJB无状态bean中创建:

@Stateless(name=ElementServiceBean.NAME, mappedName=ElementServiceBean.NAME)
@Local({ElementService.class})
@LocalBinding(jndiBinding=ElementServiceBean.NAME)
public class ElementServiceBean implements ElementService {

  public static final String NAME = "ElementServiceBean";

    @PersistenceContext(unitName = "coredb_em")
    protected EntityManager entityManager;

  private ElementDao elementDao;

  @PostConstruct
  public void init() {
     this.elementDao = new ElementDaoImpl(entityManager);
  } 

  @Override
    public List<ElementEntity> load(String hash) {
      return elementDao.load(hash);
  }

  @Override
    public void save(String hash, List<ElementEntity> elements) {
      elementDao.save(hash, elements);
  }
}

现在,在我的应用程序中,有时会发生几乎同时由两个不同的线程同时调用save方法的情况,这会导致CElementEntity中的集合中的数据以某种方式丢失。

我没有使用任何形式的Hibernate锁定。没有异常被抛出。只是来自&#34;节点的数据&#34;收集丢失了。

所以现在我的问题是,它怎么可能?我是否错误地在dao图层中使用了flush和clear方法?

或者我真的需要任何类型的hibernate /数据库锁定(但是为什么在我保留它们之前删除具有给定哈希的所有元素行时我是否需要它?) 如果我在CMT中理解得很清楚,则在交易结束时执行提交。

这是log4j日志:

[2015-01-22 17:02:49,944] 9023579 [Thread-208] DEBUG ElementDaoImpl  Storing for hash ABCDE nodes nodes=[NodeEntity[1], NodeEntity[2]]
[2015-01-22 17:02:50,028] 9023663 [Thread-216] DEBUG ElementDaoImpl  Storing for hash ABCDE nodes nodes=[NodeEntity[1], NodeEntity[2]]
[2015-01-22 17:02:50,727] 9024362 [Thread-208] DEBUG ElementDaoImpl  CElementEntity nodes: []]

感谢您提供有关如何改进代码的每条提示或建议。

1 个答案:

答案 0 :(得分:4)

您需要向实体添加@Version以启用optimistic locking,从而阻止lost updates

为此,您的实体需要包含以下字段:

@Version
private int version;

Hibernate将负责其余部分。