2015-08-03 14 views
15

Jede Zeile der Tabelle Person (mit name, firstname und age) soll gelesen werden.So holen Sie ein Feld faul mit Hibernate Kriterien

EntityManager em = emf.createEntityManager(); 
Session s = (Session) em.getDelegate(); 
Criteria criteria = s.createCriteria(Person.class); 
criteria.setFetchMode("age", FetchMode.SELECT); 

Aber die SQL zeigt

Hibernate: 
    select 
     person0_.name, 
     person0_.firstname, 
     person0_.age 
    from 
     SCOPE.PERSON person0_ 

Wie das Alter lassen NUR für die Kriterien faul sein ??

+0

Ich glaube, das ist nicht möglich, aber das Gegenteil ja, machen es faul und lösen seine Initialisierung bei Bedarf. –

+0

Fügen Sie in Ihrer Entität 'Person' dem Feld 'Alter' Annotation '@Basic (fetch = FetchType.LAZY)' hinzu. Gib wenigstens einen Schuss ab. –

+0

@ PawełGłowacz Ich kenne diesen Stil, und ich mag es nicht. Es sollte Teil der Kriterien sein, nicht der Umsetzung der Entität. –

Antwort

10

Ich denke, dass Lazy-Modus nur mit Assoziationen sinnvoll ist. Wenn Sie auf eine einfache Tabelle zugreifen, werden alle Felder geladen.

Wenn Sie das age Feld nicht erscheinen in der SQL wollen und so wird nicht in den Speicher geladen dann Projektionen verwenden:

Criteria crit = session.createCriteria(Person.class); 
ProjectionList projList = Projections.projectionList(); 
projList.add(Projections.property("name")); 
projList.add(Projections.property("firstname")); 
crit.setProjection(projList); 
+1

Btw: Dieses Kriterium erzeugt eine Liste mit jedem Objekt [], das 2 Strings für 'name' und' firstname' enthält. –

+0

Mit diesem Ansatz sollten Sie sich den ResultTransformer ansehen. – gabrielgiussi

8

Einstellen der FetchMode des „Alter“ Eigenschaft auf Kriterien hat keine Auswirkung, da Die Abrufstrategie an diesem Punkt gilt nur für verknüpfte Objekte, nicht jedoch für Eigenschaften. Siehe Abschnitt 20.1. Fetching strategies der Winterschlaf-Dokumentation.

Hibernate verwendet eine Abrufstrategie zugehörigen Objekte abzurufen, wenn die Anwendung die Zuordnung navigieren muss. Abrufstrategien können in den O/R-Zuordnungsmetadaten deklariert oder von einer speziellen HQL- oder Kriterienabfrage überschrieben werden.

Der einzige Weg für verzögertes Laden einer Immobilie ist die @Basic Anmerkung zu FetchType.LAZY gesetzt. Siehe here, oder wenn Sie .hbm.xml-Dateien für die Zuordnung verwenden, verwenden Sie lazy=true, siehe this Abschnitt der Hibernate-Dokumentation.

Die @Basic Anmerkung können Sie die Abrufstrategie für eine Eigenschaft erklären. Wenn der Wert auf LAZY gesetzt ist, wird angegeben, dass diese Eigenschaft abgerufen werden soll, wenn die Instanzvariable zum ersten Mal aufgerufen wird. Es erfordert Build-Time-Bytecode-Instrumentierung, wenn Ihre Klassen nicht instrumentiert sind, wird das Lazy Load der Eigenschaftsebene still ignoriert.

Lazy Loading von Eigenschaften verwendet auch Buildtime-Bytecode Instrumentierung (Hibernate die Entitätsklassen nach der Kompilierung verändert verzögertes Laden von Eigenschaften zu ermöglichen). Lesen 20.1.8. Using lazy property fetching

Eine andere mögliche Lösung (mit Ausnahme aller anderen Lösungen), um Ihr Problem ist eine einfachere Person-Klasse zu erstellen und verwenden ein constructor query wie:

public class PersonDTO { 
    private String name; 
    private String firstname; 

    private Person(String name, String firstname) { 
     this.name = name; 
     this.firstname = firstname; 
    } 
    // getters & setters 
} 

Query q = session.createQuery("select new your.package.name.PersonDTO(" 
    + "p.name, p.firstname) from Person p"); 
q.list(); 

Sie auch Ihre bestehende Person-Klasse verwenden könnten, erweitern Sie es einfach mit einem geeigneten Konstruktor, aber ich würde explizit bevorzugen.

Alle hier vorgestellten Lösungen implementieren jedoch kein träges Laden des Attributs age.Die einzige Möglichkeit, dies zu tun, ist die @Basic Annotation, oder Sie müssen Ihr eigenes Lazy Loading implementieren.

4

Ihre Argumentation ist gültig (im Allgemeinen; wir können jedoch über das spezifische Beispiel des Feldes age diskutieren), aber leider gibt es dafür keine einfache Lösung. Tatsächlich hat Hibernate das Konzept fetch profiles, aber es ist derzeit sehr begrenzt (Sie können den Standard-Abrufplan/die Standardstrategie nur mit den Join-Style-Abrufprofilen überschreiben).

So könnte die mögliche Problemumgehung für Ihr Problem wie folgt sein.

1) Bewegen age an eine separate Einheit und assoziieren die Person Einheit mit ihm mit einem faulen einer Eins-zu-Eins-Beziehung:

@Entity 
class PersonAge { 
    private Integer age; 
} 

@Entity 
class Person { 
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, optional = false) 
    @JoinColumn(name = "PERSON_AGE_ID") 
    private PersonAge personAge; 

    public Integer getAge() { 
     return personAge.getAge(); 
    } 

    public void setAge(Integer age) { 
     personAge.setAge(age); 
    } 
} 

2) ein Profil fetch definieren, welche die Rückstellung eine überschreibt:

@FetchProfile(name = "person-with-age", fetchOverrides = { 
    @FetchProfile.FetchOverride(entity = Person.class, association = "personAge", mode = FetchMode.JOIN) 
}) 

3) Aktivieren Sie dieses Profil für jede Sitzung in der Anwendung:

session.enableFetchProfile("person-with-age"); 

Je nach verwendetem Framework sollte es einen einfachen Hook/Interceptor geben, mit dem Sie das Profil für jede Sitzung (Transaktion) aktivieren können, die craeted ist. Zum Beispiel könnte ein Ansatz im Frühjahr darin bestehen, AbstractPlatformTransactionManager.doBegin des verwendeten Transaktionsmanagers außer Kraft zu setzen.

Auf diese Weise wird die personAge eifrig in alle Sitzungen in der Anwendung geladen werden, sofern das Abrufprofil nicht explizit deaktiviert ist.

4) Disable das Abrufprofil in der Sitzung, in der Sie die gewünschten Kriterien Abfrage verwenden:

session.disableFetchProfile("person-with-age"); 

diese Weise wird der Standardplan Abruf-/Strategie verwendet wird (in den Entitätszuordnungen angegeben), das ist das faule Laden der PersonAge.

4

Wenn Ihr Alter ein Objekt wie die PersonAge von @Dragan ist, können Sie den fecth-Modus den Kriterien zuordnen und nicht die Entität wie Sie.

Also, ich glaube, Sie haben drei Möglichkeiten:

  1. Alter als primitiv und Projektion wie @Paco sagt (Person.age wird null sein und kein Proxy, verlieren Sie die lazyness, die Sie wünschen)
  2. Alter als primitiv ohne Projektion (mehr Bytes im Draht)
  3. Alter als persönlichkeit + criteria.setFetchMode (Sie erhalten die lazyness erhalten, die Sie auf Kosten eines zusätzlichen Objekt/Tabelle/Mapping wollen)

für die Projektion kann man ResultTransformer zu

Criteria crit = session.createCriteria(Person.class); 
ProjectionList projList = Projections.projectionList(); 
projList.add(Projections.property("name")); 
projList.add(Projections.property("firstname")); 
crit.setProjection(projList); 
crit.setResultTransformer(new ResultTransformer() { 

     @Override 
     public Object transformTuple(Object[] tuple, String[] aliases) { 
     String name = (Long) tuple[0]; 
     String firstName = (String) tuple[1]; 
     return new Person(name , firstName); 
     } 

     @Override 
     public List<Reference> transformList(List collection) { 
     return collection; 
     } 
    }); 

verwenden glaube ich Ihnen eine PersonProxy auf eigener Faust schaffen könnten, die eine Abfrage löst für die Alte abrufen, aber dies ist eine Art schrecklich.

@Override 
    public Object transformTuple(Object[] tuple, String[] aliases) { 
    String name = (Long) tuple[0]; 
    String firstName = (String) tuple[1]; 
    return new PersonProxy(name , firstName); 
    } 

    class PersonProxy { 
    Person realPerson; 

    public getAge(){ 
     // create a query with realPerson.id for retrieve the age. 
    } 
    } 
4

Sie können einfach eine neue Einheit SimplePerson auf die gleiche persons Datenbanktabelle abgebildet definieren, die nur enthält die folgenden Attribute:

  • id
  • Name
  • vorName

Auf diese Weise, wenn Sie eine SimplePerson mit beiden Kriterien auswählen und HQL, die Altersspalte wird nicht abgerufen.

Eine andere Alternative ist die Verwendung von lazy loading for basic attributes, aber das Zuordnen mehrerer Unterobjekte zu derselben Datenbanktabelle ist wesentlich flexibler.