我有一個簡單的Hibernate實體:休眠:ConstraintViolationException並行插入
@Entity
@Table(name = "keyword",
uniqueConstraints = @UniqueConstraint(columnNames = { "keyword" }))
public class KeywordEntity implements Serializable {
private Long id;
private String keyword;
public KeywordEntity() {
}
@Id
@GeneratedValue
@Column(unique = true, updatable=false, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name="keyword")
public String getKeyword() {
return this.keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
}
DAO吧:
@Component
@Scope("prototype")
public class KeywordDao {
protected SessionFactory sessionFactory;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public KeywordEntity findByKeyword(String keyword) throws NotFoundException {
Criteria criteria = sessionFactory.getCurrentSession()
.createCriteria(KeywordEntity.class)
.add(Restrictions.eq("keyword", keyword));
KeywordEntity entity = (KeywordEntity) criteria.uniqueResult();
if (entity == null) {
throw new NotFoundException("Not found");
}
return entity;
}
public KeywordEntity createKeyword(String keyword) {
KeywordEntity entity = new KeywordEntity(keyword);
save(entity);
return entity;
}
}
和服務,這使@Transactional
下的一切:
@Repository
@Scope("prototype")
public class KeywordService {
@Autowired
private KeywordDao dao;
@Transactional(readOnly = true)
public KeywordEntity getKeyword(String keyword) throws NotFoundException {
return dao.findByKeyword(keyword);
}
@Transactional(readOnly = false)
public KeywordEntity createKeyword(String keyword) {
return dao.createKeyword(keyword);
}
@Transactional(readOnly = false)
public KeywordEntity getOrCreateKeyword(String keyword) {
try {
return getKeyword(keyword);
} catch (NotFoundException e) {
return createKeyword(keyword);
}
}
}
在單線程環境中,此代碼運行得很好。我在多線程環境中使用它時遇到了問題。當有多個並行線程時,使用相同的關鍵字,其中一些使用相同的關鍵字同時調用getOrCreateKeyword
,並且發生以下情況:
2個線程同時使用相同的關鍵字調用關鍵字服務,兩者都先嚐試獲取現有的關鍵字,但都沒有找到,並且都嘗試創建新的關鍵字。第一個成功,第二個 - 導致ConstraintViolationException
被拋出。
所以,我曾嘗試提高getOrCreateKeyword
方法一點:
@Transactional(readOnly = false)
public KeywordEntity getOrCreateKeyword(String keyword) {
try {
return getKeyword(keyword);
} catch (NotFoundException e) {
try {
return createKeyword(keyword);
} catch (ConstraintViolationException ce) {
return getKeyword(keyword);
}
}
}
所以理論上它應該解決的問題,但在實踐中,一旦ConstraintViolationException
被拋出,調用getKeyword(keyword)
導致另一個Hibernate的異常:
AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate,
but is more likely due to unsafe use of the session)org.hibernate.AssertionFailure:
null id in KeywordEntity entry (don't flush the Session after an exception occurs)
如何解決這個問題?
這是一個多機環境,所以'synchronized'不會幫助...你能指點我一些關於悲觀鎖機制的資源嗎? – Laimoncijus
我發現了兩篇文章。第一個是冬眠文檔,第二個來自objectdb顯示瞭如何從查詢結果中鎖定特定實體和所有實體,這可能會對您有所幫助。 http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-locking http://www.objectdb.com/java/jpa/persistence/lock –