9 Stimmen

Hibernate generiert SQL-Abfragen beim Zugriff auf die ID der zugehörigen Entität

Ich habe Hibernate-Entitäten, die ungefähr so aussehen (Getter und Setter ausgelassen):

@Entity
public class EntityA {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private EntityB parent;
}

@Entity
public class EntityB extends SuperEntity {
    @OneToMany(mappedBy = "parent")
    @Fetch(FetchMode.SUBSELECT)
    @JoinColumn(name = "parent_id")
    private Set children;
}

@MappedSuperclass
public class SuperEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private long itemId;
}

Wenn ich nach EntityA abfrage, wird es ordnungsgemäß geladen, wobei die Elternbeziehung durch einen Hibernate-Proxie ersetzt wird (da sie Lazy ist). Wenn ich auf die Id des Elternteils zugreifen möchte, führe ich den folgenden Aufruf durch:

EntityA entityA = queryForEntityA();
long parentId = entityA.getParent().getItemId();

Meines Wissens nach sollte dieser Aufruf KEINEN Datenbankabfrage verursachen, da die Id in der EntityA-Tabelle gespeichert ist und der Proxie nur diesen Wert zurückgeben sollte. In meinem Fall generiert dies jedoch eine SQL-Anweisung, die EntityB abruft und erst dann die Id zurückgibt.

Wie kann ich das Problem untersuchen? Was sind einige wahrscheinliche Ursachen für dieses inkorrekte Verhalten?

9voto

Pascal Thivent Punkte 548176

Meines Verständnisses nach sollte dieser Aufruf KEINEN Datenbank-Roundtrip machen, da die ID in der Tabelle EntityA gespeichert ist und der Proxy nur diesen Wert zurückgeben sollte.

Verwenden Sie property access type. Das Verhalten, das Sie erleben, ist eine "Einschränkung" des Feldzugriffstyps. So hat Emmanuel Bernard es erklärt:

Das ist bedauerlich, aber zu erwarten. Das ist eine der Einschränkungen des Zugriffs auf Felderebene. Im Grunde haben wir keine Möglichkeit zu wissen, dass getId() tatsächlich nur auf das id-Feld zugreift. Deshalb müssen wir das gesamte Objekt laden, um sicher zu sein.

Ändern Sie also Ihren Code in:

@Entity
public class EntityA {
    private EntityB parent;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    public EntityB getParent() {
        return parent; 
    }
    ...
}

@MappedSuperclass
public class SuperEntity {
    private long itemId;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    public long getItemId() { 
        return itemId;
    }
    ...
}

Verwandte Frage

Referenzen

0voto

taj Punkte 521

Was du sagst, macht Sinn - dass es keinen DB-Zugriff geben würde, da EntityA die Eltern-ID enthält. Ich bin mir nur nicht sicher, ob der getParent() Aufruf tatsächlich das EntityB-Objekt lädt, unabhängig davon, ob du nur an der ID interessiert bist. Du könntest versuchen, die children-Kollektion (und andere Felder) als Lazy zu markieren, wenn du den DB-Zugriff sparen möchtest.

@Entity
public class EntityB : SuperEntity {
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
    @Fetch(FetchMode.SUBSELECT)
    @JoinColumn(name = "parent_id")
    private Set children;
}

0voto

Leonid Dashko Punkte 3089

Was Hibernate betrifft:
Dieses Verhalten wurde seit Hibernate 5.2.12 geändert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X