2

すでに存在する場合は置き換える必要があるエントリでデータベースを埋めています。これを行うには、最初に検索でエントリが既に存在するかどうかを確認します (これは読み取り専用トランザクションにラップされます)。戻り値は既存のエントリです。最初にそれを削除し (トランザクションにラップ)、次に新しいエントリを追加します。これらは単一のトランザクション内で実行できる多くのトランザクションであるという事実に加えて、エントリが既に存在する場合になぜ例外が発生するのか疑問に思っています。フラッシュ モードを確認したところ、AUTO に設定されているため、トランザクションの最後にフラッシュする必要があります。これは、例外のスタック トレースです。

Exception in thread "main" java.lang.NullPointerException
    at org.hibernate.engine.internal.NaturalIdXrefDelegate.validateNaturalId(NaturalIdXrefDelegate.java:175)
    at org.hibernate.engine.internal.NaturalIdXrefDelegate.cacheNaturalIdCrossReference(NaturalIdXrefDelegate.java:85)
    at org.hibernate.engine.internal.StatefulPersistenceContext$1.cacheNaturalIdCrossReferenceFromLoad(StatefulPersistenceContext.java:1817)
    at org.hibernate.engine.internal.StatefulPersistenceContext.getNaturalIdSnapshot(StatefulPersistenceContext.java:340)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkNaturalId(DefaultFlushEntityEventListener.java:110)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:199)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:480)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:392)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy31.save(Unknown Source)
    at controller.FileParserCore.update(FileParserCore.java:39)
    at controller.Core.runCore(Core.java:122)
    at controller.Core.staticRunCore(Core.java:74)
    at controller.Core.main(Core.java:33)

私は休止状態と春を使用しています。

これは、エントリが既に存在する場合に失敗するコードの一部です。

Entry entry = getNewEntry();
if (entryDAO.find(entry.getName()) != null) {
    entryDAO.remove(entry.getName());
}
entryDAO.save(entry);

これは、対応する DAO です。

@Repository
public class EntryDAO extends GenericDAO<Entry> implements IEntryDAO {
    private static final Logger log = LoggerFactory.getLogger(EntryDAO.class);

    @Override
    @Transactional
    public void save(Entry entry) {
        makePersistent(entry);
        log.info("Saved: {}", entry);
    }

    @Override
    @Transactional
    public void remove(String name) {
        Entry entry = find(name);
        if (entry != null) {
            makeTransient(entry);
            log.info("Removed: {}", entry);
        } else {
            log.warn("Could not remove: {}, entry not found", name);
        }
    }

    /**
     * find an entry by its name and return it
     */
    @Override
    @Transactional(readOnly = true)
    public Entry find(String name) {
        return (Entry) createCriteria(Restrictions.eq("name",name)).uniqueResult();
    }
}

これが Entry ドメイン オブジェクトです。

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false, of = { "name" })
public class Entry extends DomainObject implements Serializable {
    @NaturalId
    @NotEmpty
    @Length(max = 16)
    @Index(name = "ix_name")
    private String name;
}

これは、例外までの出力です。

INFO 2013-03-01 10:22:17,899 メイン - 削除: someEntry
INFO 2013-03-01 10:22:18,117 メイン - 保存: someEntry

データベースを確認すると、古いエントリは削除されていますが、新しいエントリは保存されていません。また、古いエントリと新しいエントリの ID は異なり、名前だけが同じです。スタックトレースから、NaturalID と関係があるように見えます。おそらく、フラッシュが古いエントリの削除を完了していないため、新しいエントリの名前がこれと衝突していますか? しかし、save() メソッドに入った時点で、古いエントリはすでに消えてしまっているのではないでしょうか?

更新: 単一のトランザクション内にすべてを配置しても機能するかどうかも確認しましたが、もちろん、エントリを削除してすぐに同じ NaturalID (名前) を持つエントリを追加しようとすると、泣き始めました。だから私は考えました: getCurrentSession().flush() を間に入れましょう。結果: 上記と同じ例外。

更新: DomainObject クラスは次のとおりです。

@MappedSuperclass
@EqualsAndHashCode
public abstract class DomainObject implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long    id;

    public Long getId() {
        return id;
    }

    @Override
    public abstract String toString();
}
4

2 に答える 2

0

私の場合、EntityListener によって引き起こされた休止状態操作のネストがありました。一部のエンティティの保存操作中に、監査インターセプター (リスナー) が呼び出され、DB から内容を読み取り、セッションを台無しにします。

解決策は、インターセプターの新しいセッションとトランザクションを開くことでした。

于 2014-08-12T14:56:05.847 に答える
0

申し訳ありませんが、私はすでに解決しました。これは、ここで言及されていない多対多の関係での偶発的な削除のカスケードに関係していました (問題とは関係ないと思ったので言及しませんでした)。複数のエントリが多対多のカスケードによって相互にリンクされていたため、1 回の削除ですべてを削除していたことが判明しました。カスケードを削除した後、すべての問題が解決されました。

于 2013-03-11T09:53:22.023 に答える