4

SINGLE_TABLEとSecondaryTablesを使用して継承階層を実装しました。

これは別の方法でも機能しますが、セカンダリテーブルのフィールドが空の場合(= Oracleではnull)、Hibernateは更新する必要があるときにテーブルに挿入する必要があると考えているため、エンティティへの次の更新は失敗します。例:

CREATE TABLE TASK 
(
   ID NUMBER(10) NOT NULL 
   , TYPE NUMBER(1) NOT NULL 
   , STATUS NUMBER(1) NOT NULL 
, CONSTRAINT TASK_PK PRIMARY KEY  (ID) ENABLE);

CREATE TABLE SUB_TASK 
(
  ID NUMBER(10) NOT NULL 
, TEXT VARCHAR2(4000 CHAR) 
, CONSTRAINT SUB_TASK_PK PRIMARY KEY 
(ID) ENABLE);

ALTER TABLE SUB_TASK
ADD CONSTRAINT SUB_TASK_FK1 FOREIGN KEY
(ID)REFERENCES TASK(ID)ENABLE;

Task.java:

@Entity
@Table(name="TASK")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE",discriminatorType=DiscriminatorType.INTEGER)
public abstract class Task implements Serializable, Comparable<Task> {

@Id
@Column(nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TASK_GEN")
@SequenceGenerator(name = "TASK_GEN", sequenceName = "SEQ_TASK", allocationSize = 1)
private Long id;

@Column(name = "TYPE", nullable = false, insertable=false, updatable=false)
private int typeCode;

SubTask.java

@Entity
@DiscriminatorValue("7")
@SecondaryTable(name = "SUB_TASK", pkJoinColumns = {@PrimaryKeyJoinColumn(name = "id", referencedColumnName = "id")})
public class SubTask extends Task implements Serializable {

@Column(name="TEXT", length = 4000, table="SUB_TASK")
@Size(max = 4000)
private String text;

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

}

以前にnullのSubTask.textを持っていたエンティティを保存するときの例外:

原因:org.hibernate.exception.ConstraintViolationException:ORA-00001:yksikäsitteistärajoitetta(SUB_TASK_PK)loukattu

at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:128)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
at $Proxy75.executeUpdate(Unknown Source)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2965)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3028)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3350)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:140)

関連するHibernateコードAbstractEntityPersister.javaをデバッグしました。

    if ( !isInverseTable( j ) ) {

        final boolean isRowToUpdate;
        if ( isNullableTable( j ) && oldFields != null && isAllNull( oldFields, j ) ) {
            //don't bother trying to update, we know there is no row there yet
            isRowToUpdate = false;
        }
        else if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
            //if all fields are null, we might need to delete existing row
            isRowToUpdate = true;
            delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null );
        }
        else {
            //there is probably a row there, so try to update
            //if no rows were updated, we will find out
            isRowToUpdate = update( id, fields, oldFields, rowId, includeProperty, j, oldVersion, object, sql, session );
        }

        if ( !isRowToUpdate && !isAllNull( fields, j ) ) {
            // assume that the row was not there since it previously had only null
            // values, so do an INSERT instead
            //TODO: does not respect dynamic-insert
            insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
        }

    }

そして、最初のif( "更新しようとしないでください")が実行され、isRowToUpdate = falseであり、最後のif(if(!isRowToUpdate &&!isAllNull(fields、j)))も実行されているように見えるので、実行されます。

ダミーの非nullフィールドをテーブルSUB_TASKに追加することでこれを回避できると思いますが、これが私の唯一の選択肢ですか?

Hibernate4.17.finalの使用

編集:うまくいけば私がしていることを明確にするために:

SubTask s = taskRepository.findOne(42l);
s.setText("dasdsa");
taskRepository.save(s); // OK
s.setText(null); 
taskRepository.save(s); // OK
s.setText("update after null value");
taskRepository.save(s); // exception 

私は永続性のためにSpringDataを使用していますが、問題がそれに関連しているようには見えません(そして、バニラJPAを使用するときに同じことが起こると思います)

4

2 に答える 2

1

@ManyToOneセカンダリテーブルで定義されたリレーションシップで、やや似た問題がありました。セカンダリ テーブルにプライマリ テーブルのエンティティを参照する行があり、リレーションシップ カラムが null で値を設定しようとすると、Hibernate は既存の行を更新する代わりに新しい行を挿入しようとし、制約違反の例外をスローしました。

@org.hibernate.annotations.Table(appliesTo="secondary_table", optional=false)解決策は、 Hibernate に行を更新させる注釈を使用することでした。

セカンダリ テーブルの行が存在しない場合の動作についてはわかりませんが、問題が解決する可能性があります。

于 2013-03-07T13:08:00.330 に答える