2015-05-06 7 views
5

ich einige Probleme habe eine @ManyToOne Vereinigung machen lazilly geladen werden. Ich verwende das Fetch = LAZY, aber es funktioniert nicht, wenn Join nicht von der Primärschlüsselspalte gemacht wird.@ManyToOne (Fetch = FetchType.LAZY) funktioniert nicht auf Nicht-Primärschlüssel referenzierte Spalte

Ich weiß, diese Frage war bereits asked, aber ich denke, es wurde nicht richtig beantwortet, so dass ich detaillierte Informationen zur Verfügung stellen, um das Problem zu klären.

Das ist mein Modell:

DummyB -> DummyA 

Dies sind die Tabellen:

create table dummyA (
    id number(18,0), --pk 
    name varchar2(20) -- unique field 
); 

create table dummyB (
    id number(18,0), 
    dummya_id number(18,0), 
    dummya_name varchar2(20) 
); 

Und das sind die Einheiten sind:

@Entity 
public class DummyA implements Serializable { 

    private Long id; 
    private String name; 

    @Id 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

@Entity 
public class DummyB implements Serializable { 

    private Long id; 
    private DummyA dummyA; 

    @Id 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    /* Case 1: mapping DummyB -> DummyA by DummyA NON primary key (field name) */ 
    // @ManyToOne(fetch = FetchType.LAZY) 
    // @JoinColumn(name = "dummya_id") 
    // public DummyA getDummyA() { 
    // return dummyA; 
    // } 

    /* Case 2: mapping DummyB -> DummyA by DummyA primary key */ 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "dummya_name", referencedColumnName = "name") 
    @LazyToOne(LazyToOneOption.PROXY) 
    public DummyA getDummyA() { 
     return dummyA; 
    } 

    public void setDummyA(DummyA dummyA) { 
     this.dummyA = dummyA; 
    } 

} 

Hinweis getDummyA Methode in Einheit DummyB, ist zu duplizieren Probieren Sie zwei Fälle aus, um den Entitäten beizutreten.

Fall 1: Mapping DummyB -> DummyA von DummyA Primärschlüssel

@ManyToOne (Fetch = FetchType.LAZY) @JoinColumn (name = "dummya_id")

Dies funktioniert gut, nur Eine Abfrage wird ausgeführt, um DummyB-Objekte abzurufen.

Fall 2: Mapping DummyB -> DummyA durch DummyA NON Primärschlüssel (Feldname)

@ManyToOne (fetch = FetchType.LAZY) @JoinColumn (name = "dummya_name", referencedColumnName = "name ")

Gleiches dummyB select ist ausführen, aber direkt danach wird ein dummyA select ausgeführt filter by name =? um das verwandte A-Objekt abzurufen.

Ich bin ein wirklich einfaches jUnit mit Filterung auszuführen:

public class DummyTest { 

    @Autowired 
    HibernateTransactionManager transactionManager; 

    @Test 
    @Transactional 
    public void testFindDummyB() throws DAOException { 
     Long idDummyB = 2L; 

     Session session = getCurrentHibernateSession(); 

     List lst = session.createCriteria(DummyB.class) 
       .add(Restrictions.eq("id", idDummyB)).list(); 

     assertTrue(lst.size() > 0); 
    } 

    private Session getCurrentHibernateSession() { 
     return this.transactionManager.getSessionFactory().getCurrentSession(); 
    } 

} 

Meine Bibliotheken:

  • org.hibernate: Hibernate-Kern: jar: 4.2.17.Final: kompilieren
  • org.hibernate.common: hibernate-commons-Anmerkungen: jar: 4.0.2.Final:
  • org.hibernate.javax.persistence kompilieren: hibernate-JPA-2.0-api: jar: 1.0.1.Final: kompilieren
  • org.hibernate: Hibernate-Validator: jar: 4.3.2.Final: bereitgestellt

Andere Dinge, die ich schon versucht habe:

  • hiberante des @LazyToOne zu getDummyA() -Methode Hinzufügen Doesn Habe keine Wirkung.

    @LazyToOne (LazyToOneOption.PROXY) @ManyToOne (fetch = FetchType.LAZY, optional = true) @JoinColumn (name = "dummya_name", referencedColumnName = "name") @LazyToOne (LazyToOneOption.PROXY)

  • Das Erstellen eines Fremdschlüssels von der DummyB-Tabelle zu dummyA (und einer eindeutigen Integritätsbedingung im Feld dummya.name) hat keine Auswirkung.

  • Hinzufügen @Column (unique = true) auf DummyA getName() -Methode hat es nicht geschafft.
  • Set optional = true oder false wie vorgeschlagen here hat auch keine Wirkung.
  • Der Versuch, die träges Laden zu zwingen, die setFetchMode in den Kriterien funktionierten nicht, DummyA wählen hält Ausführung.

    Liste lst = session.createCriteria (DummyB.class) .add (Restrictions.eq ("id", idDummyB)). setFetchMode ("dummyA", FetchMode.SELECT) .list();

Ich kann nicht einen Punkt in Hibernate docs finden, wo es zu diesem Verhalten bezieht, so frage ich mich, ob es etwas falsch in meinen Anmerkungen oder stieß ich auf einem Hibernate Fehler.

Kann jemand erzählen?

AKTUALISIERT von md-dev Anfrage: So stellen es klar:

Ist dies das erwartete Verhalten oder ist ein Fehler? Wenn dies das erwartete Verhalten ist, wo ist es dokumentiert?

Vielen Dank.

Antwort

0

Sie können den Namen nicht als Join-Spalte verwendet werden, da es keine eindeutige Einschränkung für das ist. Es ist also möglich, dass es zu einem ManyToMany-Mapping anstelle von ManyToOne führt. Ich habe keine Ahnung, ob Hibernate dies akzeptiert, aber es würde auf lange Sicht in etwas unerwartetes enden. Zusätzlich sehe ich den Anwendungsfall dafür nicht. Ich rate Ihnen, immer eine Long-ID als Primärschlüssel zu verwenden und automatisch über dieses Feld abzubilden. Eine solche spezielle Behandlung ist nur notwendig, wenn Sie einen ungewöhnlichen Anwendungsfall haben oder mit einer älteren Datenbank kompatibel sein müssen.

+0

Der Fall oben ausgesetzt hinzuzufügen, ist eine Vereinfachung meiner Problem, in meinem Fall ist die Spalte, zu der ich komme, eine eindeutige Spalte. Wenn es einen Sinn ergibt, ist es kompliziert zu erklären und es ist nicht der Punkt meiner Frage, ich frage "Ist das das erwartete Verhalten? Wenn ja, wo ist es dokumentiert?" – kothvandir

+0

Dann bitte korrigieren Sie Ihre Frage, denn die obige ist falsch. –

+0

Das ist genau das, was ich in meinem letzten Absatz verlangt habe, aber als du darum gebeten hast, habe ich es deutlicher gemacht. – kothvandir

3

Das gleiche Verhalten mit Hibernate 5.0.4 zu sehen. @ManyToOne (mit einem reziproken OneToMany) und Lazy holen funktioniert perfekt, wenn die Join-Spalte der Primärschlüssel ist. Wenn dies nicht der Fall ist, bricht das Lazy Loading ab und Hibernate ruft eifrig alle ManyToOne jedes Mal ab, wenn ein Objekt instanziiert wird. Dies kann katastrophal langsam sein, wenn Sie eine für, sagen wir, 1000 Datensätze machen. Was als eine einzige Abfrage für 1000 Datensätze begann, kann in 5000 Abfragen übergehen, um eifrig eine Vielzahl von @ManyToOne mit individuellen Selects abzurufen.

Absolut nichts, was ich testen/ändern konnte, hat dies in irgendeiner Weise erklärt und ich kann es zuverlässig reproduzieren. Die Lösung, die ich in meiner App implementieren musste, die Nicht-PKs für Joins verwendet, bestand darin, nur @ManyToOne/@OneToMany Annotationspaare in den Papierkorb zu ziehen und Sammlungsabrufe manuell zu schreiben (Zwischenspeichern der Ergebnisse mit einer transienten Variablen). Es ist viel mehr Arbeit, aber die Leistung ist wesentlich höher, da einige meiner Objekte 5 oder 6 @ManyToOne Objekte haben und alle diese wurden eifrig mit individuellen Selects von Hibernate geholt.

Leider kann ich reorganisieren mein Schema nicht diese Marotte in Hibernate aufzunehmen. Ich führe ein Projekt mit Heroku Connect und den Joins zwischen Tabellen durch, wenn Daten aus Salesforce.com zusammengeführt werden, indem eine Spalte "sfid" in der Tabelle verwendet wird, die nicht der Primärschlüssel ist.Der Primärschlüssel ist ein separater Wert, der für den Datensatz in der Heroku Postgres-Datenbank eindeutig ist und nicht für Joins verwendet werden kann, da keine anderen Tabellen in der Datenbank auf diesen Primärschlüssel verweisen.

Ich nehme an, dass dies ein Fehler in Hibernate ist; Nichts, was ich gelesen oder geändert habe, hat dieses Verhalten in irgendeiner Weise beeinflusst und, wie ich bereits erwähnt habe, kann ich das System genauso wie erwartet funktionieren lassen, wenn die Join-Spalten die Primärschlüssel sind.

1

Falls jemand hat immer noch Probleme mit diesem Wir haben es die folgende Art und Weise zu arbeiten:

@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "dummya_name", referencedColumnName = "name", insertable = false, updatable = false), 
@LazyToOne(LazyToOneOption.NO_PROXY) 
public DummyA getDummyA() { 
    if (fieldHandler != null) { 
     return (DummyA) fieldHandler.readObject(this, "dummyA", dummyA); 
    } 
    return dummyA; 
} 

public void setDummyA(DummyA dummyA) { 
    if (fieldHandler != null) { 
     this.dummyA = (DummyA) fieldHandler.writeObject(this, "dummyA", this.dummyA, dummyA); 
     return; 
    } 
    this.dummyA= dummyA; 
} 

@Override 
public void setFieldHandler(FieldHandler fieldHandler) { 
    this.fieldHandler = fieldHandler; 
} 

@Override 
public FieldHandler getFieldHandler() { 
    return fieldHandler; 
} 

Es erklärt wurde gut in Hibernate lazy loading for reverse one to one workaround - how does this work?

0

@peach Lösung für mich gearbeitet. Nur wenige Dinge nicht erwähnt wurde:

@Entity 
public class DummyB implements Serializable { 

@Entity 
public class DummyB implements Serializable, FieldHandled { 

sein sollte, und wenn Sie @JsonIgnoreProperties verwenden, sollten Sie fieldHandler

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"}) 
Verwandte Themen