1

私は単純な 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)

この問題を解決するには?

4

2 に答える 2

2

データベース/休止状態を使用してある種の悲観的ロックメカニズムを使用するか、単一のマシンで実行する場合はサービスメソッド getOrCreateKeyword() を同期させることができます。

ここにいくつかの参考文献があります。

Hibernate のドキュメントhttp://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-locking

この記事では、クエリの結果から特定のエンティティとすべてのエンティティをロックする方法について説明します。 http://www.objectdb.com/java/jpa/persistence/lock#Locking_during_Retrieval_

于 2013-01-15T17:11:17.997 に答える
1

解決策は、発生した現在のセッションを破棄しConstraintViolationException、新しいセッション内でもう一度キーワードを取得することでした。Hibernate Documentationもこれを指しています:

セッションが例外をスローした場合、トランザクションをロールバックしてセッションを破棄する必要があります。例外が発生した後、セッションの内部状態がデータベースと一致しない可能性があります。

于 2013-01-29T09:21:22.917 に答える