2017-01-16 11 views
1

Я твердо верю, что это происходит из-за сложных идентификаторов в сущности. Сначала я покажу свой сценарий. У меня три объекта. Один использует два других в качестве идентификаторов. И оба имеют составные идентификаторы.Hibernate привязывает больше параметров к запросу, чем он должен

Сущность 1 (композитный идентификатор):

@Entity 
@Table(name="TEST_A") 
public class A implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name="column1") 
    private int column1; 

    @Id 
    @Column(name="column2") 
    private int column2; 

    @Id 
    @Column(name="column3") 
    private int column3; 

    @Id 
    @Column(name="column4") 
    private int column4; 

    @Id 
    @Column(name="column5") 
    private int column5; 

Объект 2 (композитный идентификатор):

@Entity 
@Table(name="TEST_M") 
public class M implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Column(name="column1") 
    private int column1; 

    @Id 
    @Column(name="column2") 
    private int column2; 

    @Id 
    @Column(name="column3") 
    private int column3; 

    @Id 
    @Column(name="column6") 
    private int column6; 

Сущность 3 (использует два других в качестве идентификаторов)

@Entity 
@Table(name="TEST_F") 
public class F implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @ManyToOne(fetch=FetchType.LAZY, optional=false) 
    @JoinColumns({ 
     @JoinColumn(name="column1", referencedColumnName="column1"), // shared 
     @JoinColumn(name="column2", referencedColumnName="column2"), // shared 
     @JoinColumn(name="column3", referencedColumnName="column3"), // shared 
     @JoinColumn(name="column4", referencedColumnName="column4"), 
     @JoinColumn(name="column5", referencedColumnName="column5")}) 
    private A a; 

    @Id 
    @ManyToOne(fetch=FetchType.LAZY, optional=false) 
    @JoinColumns({ 
     @JoinColumn(name="column1", referencedColumnName="column1"), // shared 
     @JoinColumn(name="column2", referencedColumnName="column2"), // shared 
     @JoinColumn(name="column3", referencedColumnName="column3"), // shared 
     @JoinColumn(name="column6", referencedColumnName="column6")}) 
    private M m; 

Уведомления что столбцы 1, 2 и 3 из таблицы TEST_F используются как внешние ключи для обоих TEST_A и TEST_M.

Таблицы:

CREATE TABLE "TEST_A" 
("COLUMN1" NUMBER NOT NULL , 
"COLUMN2" NUMBER NOT NULL , 
"COLUMN3" NUMBER NOT NULL , 
"COLUMN4" NUMBER NOT NULL , 
"COLUMN5" NUMBER NOT NULL , 
CONSTRAINT "TEST_A_PK" PRIMARY KEY ("COLUMN5", "COLUMN4", "COLUMN3", "COLUMN2", "COLUMN1")); 

CREATE TABLE "TEST_M" 
("COLUMN1" NUMBER NOT NULL , 
"COLUMN2" NUMBER NOT NULL , 
"COLUMN3" NUMBER NOT NULL , 
"COLUMN6" NUMBER NOT NULL , 
CONSTRAINT "TEST_M_PK" PRIMARY KEY ("COLUMN1", "COLUMN2", "COLUMN6", "COLUMN3")); 

CREATE TABLE "TEST_F" 
("COLUMN1" NUMBER NOT NULL , -- shared by both FKs 
"COLUMN2" NUMBER NOT NULL , -- shared by both FKs 
"COLUMN3" NUMBER NOT NULL , -- shared by both FKs 
"COLUMN4" NUMBER NOT NULL , 
"COLUMN5" NUMBER NOT NULL , 
"COLUMN6" NUMBER NOT NULL , 
CONSTRAINT "TEST_F_PK" PRIMARY KEY ("COLUMN6", "COLUMN5", "COLUMN4", "COLUMN3", "COLUMN2", "COLUMN1") 

CONSTRAINT "TEST_F_M" FOREIGN KEY ("COLUMN1", "COLUMN2", "COLUMN6", "COLUMN3") 
    REFERENCES "TEST_M" ("COLUMN1", "COLUMN2", "COLUMN6", "COLUMN3") ENABLE, 

CONSTRAINT "TEST_F_A" FOREIGN KEY ("COLUMN5", "COLUMN4", "COLUMN3", "COLUMN2", "COLUMN1") 
    REFERENCES "TEST_A" ("COLUMN5", "COLUMN4", "COLUMN3", "COLUMN2", "COLUMN1") ENABLE); 

Теперь, что я пытаюсь сделать:

public class Main { 
    public static void main(String[] args) { 
     A a = new A(); 
     a.setColumn1(1); 
     a.setColumn2(2); 
     a.setColumn3(3); 
     a.setColumn4(4); 
     a.setColumn5(5); 

     M m = new M(); 
     m.setColumn1(1); 
     m.setColumn2(2); 
     m.setColumn3(3); 
     m.setColumn6(6); 

     F f = new F(); 
     f.setM(m); 
     f.setA(a); 

     Session s = HibernateUtil.openSession(); 
     Transaction tx = s.beginTransaction(); 
     s.delete(f); 
     tx.commit(); 
    } 

Наконец, что я получаю от всего этого:

org.hibernate.exception.GenericJDBCException: could not delete: [F#component[m,a]{a=A, m=M}] 
java.sql.SQLException: Invalid column index 

Подробный журнал показывает это. Сначала он загружает е:

2017-01-16 11:15:38 DEBUG org.hibernate.SQL - 
    /* get current state F */ select 
     f_.column6, 
     f_.column3, 
     f_.column2, 
     f_.column1, 
     f_.column5, 
     f_.column4, 
     f_.column3, // unnecessary 
     f_.column2, // unnecessary 
     f_.column1 // unnecessary 
    from 
     TEST_F f_ 
    where 
     f_.column6=? 
     and f_.column3=? 
     and f_.column2=? 
     and f_.column1=? 
     and f_.column5=? 
     and f_.column4=? 
     and f_.column3=? // unnecessary 
     and f_.column2=? // unnecessary 
     and f_.column1=? // unnecessary 
2017-01-16 11:15:38 TRACE ... - ID unsaved-value strategy UNDEFINED 
2017-01-16 11:15:38 TRACE ... - binding parameter [1] as [INTEGER] - [6] 
2017-01-16 11:15:38 TRACE ... - binding parameter [2] as [INTEGER] - [3] 
2017-01-16 11:15:38 TRACE ... - binding parameter [3] as [INTEGER] - [2] 
2017-01-16 11:15:38 TRACE ... - binding parameter [4] as [INTEGER] - [1] 
2017-01-16 11:15:38 TRACE ... - ID unsaved-value strategy UNDEFINED 
2017-01-16 11:15:38 TRACE ... - binding parameter [5] as [INTEGER] - [5] 
2017-01-16 11:15:38 TRACE ... - binding parameter [6] as [INTEGER] - [4] 
2017-01-16 11:15:38 TRACE ... - binding parameter [7] as [INTEGER] - [3] // unnecessary 
2017-01-16 11:15:38 TRACE ... - binding parameter [8] as [INTEGER] - [2] // unnecessary 
2017-01-16 11:15:38 TRACE ... - binding parameter [9] as [INTEGER] - [1] // unnecessary 

Параметры связывания нормально, хотя SQL немного странно, потому что он повторяет одинаковые столбцы как выбрать и где, и должен связывать повторяющиеся параметры.

Теперь он удаляет е:

2017-01-16 11:15:38 TRACE org.hibernate.persister.entity.AbstractEntityPersister - Deleting entity: [F#component[m,a]{a=A, m=M}] 
2017-01-16 11:15:38 DEBUG org.hibernate.SQL - 
    /* delete F */ delete 
     from 
      TEST_F 
     where 
      column6=? 
      and column3=? 
      and column2=? 
      and column1=? // this SQL is ok, no column repetition 
      and column5=? 
      and column4=? 
2017-01-16 11:15:38 TRACE ... - ID unsaved-value strategy UNDEFINED 
2017-01-16 11:15:38 TRACE ... - binding parameter [1] as [INTEGER] - [6] 
2017-01-16 11:15:38 TRACE ... - binding parameter [2] as [INTEGER] - [3] 
2017-01-16 11:15:38 TRACE ... - binding parameter [3] as [INTEGER] - [2] 
2017-01-16 11:15:38 TRACE ... - binding parameter [4] as [INTEGER] - [1] 
2017-01-16 11:15:38 TRACE ... - ID unsaved-value strategy UNDEFINED 
2017-01-16 11:15:38 TRACE ... - binding parameter [5] as [INTEGER] - [5] 
2017-01-16 11:15:38 TRACE ... - binding parameter [6] as [INTEGER] - [4] 
2017-01-16 11:15:38 TRACE ... - binding parameter [7] as [INTEGER] - [3] // trying to bind nonexistent parameter 7 
2017-01-16 11:15:38 INFO ... - HHH000010: On release of batch it still contained JDBC statements 

В отличие от SELECT, запроса, ВЕЬЕТЕ не повторяет столбцы. Но процесс привязки пытается связываться так же, как и ранее, вызывая исключение.

Это похоже на ошибку, ребята?

ответ

0

Я вижу 2 проблемы в вашем отображении, отсутствие в @IdClass аннотацию и репликацию имен столбцов в отображении

@Id 
@ManyToOne(fetch=FetchType.LAZY, optional=false) 
@JoinColumns({ 
    @JoinColumn(name="column1", referencedColumnName="column1"), 
    @JoinColumn(name="column2", referencedColumnName="column2"), 
    @JoinColumn(name="column3", referencedColumnName="column3"), 
    @JoinColumn(name="column4", referencedColumnName="column4"), 
    @JoinColumn(name="column5", referencedColumnName="column5")}) 
private A a; 

@Id 
@ManyToOne(fetch=FetchType.LAZY, optional=false) 
@JoinColumns({ 
    @JoinColumn(name="column1", referencedColumnName="column1"), 
    @JoinColumn(name="column2", referencedColumnName="column2"), 
    @JoinColumn(name="column3", referencedColumnName="column3"), 
    @JoinColumn(name="column6", referencedColumnName="column6")}) 
private M m; 

поэтому правильное отображение

@Entity 
@IdClass(value = EntityAId.class) 
@Table(name = "TEST_A") 
public class A implements Serializable { 

    @Id 
    @Column(name = "column1") 
    private int column1; 

    @Id 
    @Column(name = "column2") 
    private int column2; 

    @Id 
    @Column(name = "column3") 
    private int column3; 

    @Id 
    @Column(name = "column4") 
    private int column4; 

    @Id 
    @Column(name = "column5") 
    private int column5; 

} 

public class EntityAId implements Serializable{ 
    private int column1; 
    private int column2; 
    private int column3; 
    private int column4; 
    private int column5; 
} 

@Entity 
@IdClass(value = EntityMId.class) 
@Table(name = "TEST_M") 
public static class M implements Serializable { 

    @Id 
    @Column(name = "column1") 
    private int column1; 

    @Id 
    @Column(name = "column2") 
    private int column2; 

    @Id 
    @Column(name = "column3") 
    private int column3; 

    @Id 
    @Column(name = "column6") 
    private int column6; 
} 

public class EntityMId implements Serializable { 
    private int column1; 
    private int column2; 
    private int column3; 
    private int column6; 
} 

public class EntityFId implements Serializable { 
    private A a; 
    private M m; 
} 

@Entity 
@Table(name = "TEST_F") 
@IdClass(value = EntityFId.class) 
public class F implements Serializable { 

    @Id 
    @ManyToOne(fetch = FetchType.LAZY, optional = false) 
    @JoinColumns({ 
      @JoinColumn(name = "column1", referencedColumnName = "column1"), // shared 
      @JoinColumn(name = "column2", referencedColumnName = "column2"), // shared 
      @JoinColumn(name = "column3", referencedColumnName = "column3"), // shared 
      @JoinColumn(name = "column4", referencedColumnName = "column4"), 
      @JoinColumn(name = "column5", referencedColumnName = "column5") 
    }) 
    private A a; 

    @Id 
    @ManyToOne(fetch = FetchType.LAZY, optional = false) 
    @JoinColumns({ 
      @JoinColumn(name = "column6", referencedColumnName = "column1"), // shared 
      @JoinColumn(name = "column7", referencedColumnName = "column2"), // shared 
      @JoinColumn(name = "column8", referencedColumnName = "column3"), // shared 
      @JoinColumn(name = "column9", referencedColumnName = "column6") 
    }) 
    private M m; 
} 
+0

Как это может работать? 'имя = "column6"' 'Name = "column7"' 'Name = "column8"' 'Name = "column9"' Таблица TEST_F не имеет стойки 6, 7, 8 и 9. – Gustavo

+0

_I можно увидеть 2 проблем в вашем сопоставлении_ Спящий режим не жалуется в журналах. – Gustavo

0

ответ Андреа является хорошо, но @IdClass - это не лучший способ сопоставить составной первичный ключ с гибернацией.

Как описано в this article, вы должны инкапсулировать составной идентификатор в @Embeddable и ссылаться на него с помощью @EmbeddedId.

В вашем случае, вы бы иметь два типа: @Embeddable

@Embeddable 
public class AId implements Serializable { 

    @Column(name="column1") 
    private int column1; 

    @Column(name="column2") 
    private int column2; 

    @Column(name="column3") 
    private int column3; 

    @Column(name="column4") 
    private int column4; 

    @Column(name="column5") 
    private int column5; 

    //getters and setters omitted 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof A)) return false; 
     A that = (A) o; 
     return Objects.equals(column1, that.column1) && 
       Objects.equals(column2, that.column2) && 
       Objects.equals(column3, that.column3) && 
       Objects.equals(column4, that.column4) && 
       Objects.equals(column5, that.column5); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(column1, column2, column3, column4, column5); 
    } 
} 

@Embeddable 
public class MId implements Serializable { 

    @Column(name="column1") 
    private int column1; 

    @Column(name="column2") 
    private int column2; 

    @Column(name="column3") 
    private int column3; 

    @Column(name="column6") 
    private int column6; 

    //getters and setters omitted 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof A)) return false; 
     A that = (A) o; 
     return Objects.equals(column1, that.column1) && 
       Objects.equals(column2, that.column2) && 
       Objects.equals(column3, that.column3) && 
       Objects.equals(column6, that.column6); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(column1, column2, column3, column6); 
    } 
} 

@Embeddable 
public class FId implements Serializable { 

    @Column(name="column1") 
    private int column1; 

    @Column(name="column2") 
    private int column2; 

    @Column(name="column3") 
    private int column3; 

    @Column(name="column4") 
    private int column4; 

    @Column(name="column5") 
    private int column5; 

    @Column(name="column6") 
    private int column6; 

    //getters and setters omitted 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof A)) return false; 
     A that = (A) o; 
     return Objects.equals(column1, that.column1) && 
       Objects.equals(column2, that.column2) && 
       Objects.equals(column3, that.column3) && 
       Objects.equals(column4, that.column4) && 
       Objects.equals(column5, that.column5) &&; 
       Objects.equals(column6, that.column6); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(column1, column2, column3, column4, column5, column6); 
    } 
} 

И тогда ваши лица выглядят так просто:

@Entity 
@Table(name="TEST_A") 
public class A { 

    @EmbeddedId 
    private AId id; 
} 

@Entity 
@Table(name="TEST_M") 
public class M { 

    @EmbeddedId 
    private MId id; 
} 

@Entity 
@Table(name="TEST_F") 
public class F { 

    @EmbeddedId 
    private FId id; 
} 

@IdClass требует, чтобы повторить @Id amppings в то время как @EmbeddedId инкапсулирует их намного лучше.

+0

Используя этот подход '@ EmbeddedId', я могу по-прежнему иметь столбцы1, 2, 3, 4 ... как обычные' @ column' в A и M и иметь атрибуты A и M в F как '@ ManyToOne'? – Gustavo

+0

Вы можете смешать '@ Column' и' @ ManyToOne', просто убедитесь, что вы используете '@ MapsId.' Посмотрите эту статью на примере того, как [' @ MapId' работает вообще] (https: // vladmihalcea .com/2016/07/26 /-лучший способ к карте-а-onetoone-связь-с-JPA-и-спящий режим /). –

+0

Что я прошу, так как я переместил все свои атрибуты в '@ Embeddable', мой POJO« потерял »все« данные ». Могу ли я по-прежнему иметь атрибуты в POJO без '@ id', но с' @ column' и иметь их в то же время внутри '@ Embeddable'? – Gustavo