私は単純な Hibernate エンティティを持っています:
@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 つのスレッドが同時に同じキーワードでキーワード サービスを呼び出します。どちらも最初に既存のキーワードをフェッチしようとしますが、どちらも見つからず、両方とも新しいキーワードを作成しようとします。最初のものは成功し、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)
この問題を解決するには?