2015-04-21 4 views
4

Baur & King a dit dans son livre:Pourquoi Hibernate génère-t-il un CROSS JOIN pour une jointure implicite d'une association @ManyToOne?

Implicite sont toujours dirigées jointures le long many-to-one ou one-to-one, jamais par une association valeur est une collection.

[P 646, Ch 14]

Mais quand je fais cela dans le code qu'il génère un CROSS JOIN au lieu d'une INNER JOIN.

Le mappage est à partir de Member2 (plusieurs-à-un) ->CLub.

Mais Club2 n'a aucune information sur les membres et Member2 possède une clé étrangère de Club2.

Ma requête est

// Implicit: Find all UK club member who is female 
Transaction t1 = HibernateUtil.begin(); 
Query query = 
    HibernateUtil.getSession().createQuery("From Member2 m where m.club2.country = 'UK' "); 
List<Member2> memList = query.list(); 
for (Member2 m : memList) 
    System.out.println(m); 
HibernateUtil.end(t1); 

Et, Hibernate génère la requête SQL suivante:

Hibernate: 
    select 
     member2x0_.member_id as member_i1_1_, 
     member2x0_.club_id as club_id5_1_, 
     member2x0_.member_age as member_a2_1_, 
     member2x0_.member_name as member_n3_1_, 
     member2x0_.member_sex as member_s4_1_ 
    from 
     TBL_MEMBER2 member2x0_ cross 
    join 
     TBL_CLUB2 club2x1_ 
    where 
     member2x0_.club_id=club2x1_.club_id 
     and club2x1_.country='UK' 
Hibernate: 
    select 
     club2x0_.club_id as club_id1_0_0_, 
     club2x0_.club_name as club_nam2_0_0_, 
     club2x0_.country as country3_0_0_ 
    from 
     TBL_CLUB2 club2x0_ 
    where 
     club2x0_.club_id=? 
aaa 25 m 
bbb 28 f 

Club2.java

package com.lilu.de.onetomany.uni.other; 

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.SequenceGenerator; 
import javax.persistence.Table; 

@Entity 
@Table(name = "TBL_CLUB2") 
public class Club2 { 

    @GeneratedValue(generator = "pkey_Club2", strategy = GenerationType.SEQUENCE) 
    @SequenceGenerator(name = "pkey_Club2", initialValue = 1000, allocationSize = 10, 
     sequenceName = "seq_pkey_Club2") 
    @Id 
    private int club_id; 

    private String club_name; 

    private String country; 

    // private Set Member2 = new HashSet(); 

    public Club2() { 
    super(); 
    } 

    public Club2(String cname, String ccountry) { 
    this.club_name = cname; 
    this.country = ccountry; 
    } 

    @Override 
    public String toString() { 
    String temp = club_name + " " + country + " "; 
    // Iterator<Member2> iter = Member2.iterator(); 
    // String mems = null; 
    // while (iter.hasNext()) { 
    // mems += iter.next(); 
    // } 
    // temp += "\n" + mems; 
    return temp; 
    } 

    public int getClub_id() { 
    return club_id; 
    } 

    public void setClub_id(int club_id) { 
    this.club_id = club_id; 
    } 

    public String getClub_name() { 
    return club_name; 
    } 

    public void setClub_name(String club_name) { 
    this.club_name = club_name; 
    } 

    public String getCountry() { 
    return country; 
    } 

    public void setCountry(String country) { 
    this.country = country; 
    } 

    /* 
    * public Set<Member2> getMember2() { return Member2; } 
    * 
    * public void setMember2(Set<Member2> Member2) { this.Member2 = Member2; } 
    */ 
} 

Member2.java

package com.lilu.de.onetomany.uni.other; 

import javax.persistence.CascadeType; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.SequenceGenerator; 
import javax.persistence.Table; 

@Entity 
@Table(name = "TBL_MEMBER2") 
public class Member2 { 

    @GeneratedValue(generator = "pkey_member2", strategy = GenerationType.SEQUENCE) 
    @SequenceGenerator(name = "pkey_member2", sequenceName = "seq_pkey_member2") 
    @Id 
    private int member_id; 

    private String member_name; 

    private int member_age; 

    private char member_sex; 

    @ManyToOne(cascade = CascadeType.ALL) 
    @JoinColumn(name = "club_id") 
    private Club2 club2; 

    public Member2() { 
    super(); 
    } 

    public Member2(String mname, int age, char sex) { 
    this.member_name = mname; 
    this.member_age = age; 
    this.member_sex = sex; 
    } 

    public Club2 getClub2() { 
    return club2; 
    } 

    public void setClub2(Club2 club2) { 
    this.club2 = club2; 
    } 

    @Override 
    public String toString() { 
    return member_name + " " + member_age + " " + member_sex; 
    } 

    public String getMember_name() { 
    return member_name; 
    } 

    public void setMember_name(String member_name) { 
    this.member_name = member_name; 
    } 

    public int getMember_age() { 
    return member_age; 
    } 

    public void setMember_age(int member_age) { 
    this.member_age = member_age; 
    } 

    public char getMember_sex() { 
    return member_sex; 
    } 

    public void setMember_sex(char member_sex) { 
    this.member_sex = member_sex; 
    } 

    public int getMember_id() { 
    return member_id; 
    } 

    public void setMember_id(int member_id) { 
    this.member_id = member_id; 
    } 

} 

hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?> 
<!-- 
    ~ Hibernate, Relational Persistence for Idiomatic Java 
    ~ 
    ~ Copyright (c) 2010, Red Hat Inc. or third-party contributors as 
    ~ indicated by the @author tags or express copyright attribution 
    ~ statements applied by the authors. All third-party contributions are 
    ~ distributed under license by Red Hat Inc. 
    ~ 
    ~ This copyrighted material is made available to anyone wishing to use, modify, 
    ~ copy, or redistribute it subject to the terms and conditions of the GNU 
    ~ Lesser General Public License, as published by the Free Software Foundation. 
    ~ 
    ~ This program is distributed in the hope that it will be useful, 
    ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
    ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 
    ~ for more details. 
    ~ 
    ~ You should have received a copy of the GNU Lesser General Public License 
    ~ along with this distribution; if not, write to: 
    ~ Free Software Foundation, Inc. 
    ~ 51 Franklin Street, Fifth Floor 
    ~ Boston, MA 02110-1301 USA 
    --> 
<!DOCTYPE hibernate-configuration PUBLIC 
     "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
     "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 

<hibernate-configuration> 

    <session-factory> 

     <!-- Database connection settings --> 
     <property name="connection.driver_class">org.postgresql.Driver</property> 
     <property name="connection.url">jdbc:postgresql://localhost:5432/hibernatedb1</property> 
     <property name="connection.username">postgres</property> 
     <property name="connection.password">ani155</property> 

     <!-- JDBC connection pool (use the built-in) --> 
     <property name="connection.pool_size">1</property> 

     <!-- SQL dialect --> 
     <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property> 

     <!-- Disable the second-level cache --> 
     <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property> 

     <!-- Echo all executed SQL to stdout --> 
     <property name="show_sql">true</property> 
     <property name="format_sql">true</property> 

     <!-- Drop and re-create the database schema on startup --> 
     <property name="hbm2ddl.auto">update</property> 

     <!-- Names the annotated entity class --> 
     <mapping class="com.lilu.de.onetomany.uni.other.Club2"/> 
     <mapping class="com.lilu.de.onetomany.uni.other.Member2"/> 
     <!-- <mapping class="com.apal.mapping.onetoone.User"/> --> 
    </session-factory> 

</hibernate-configuration> 

Test.java

package com.lilu.de.onetomany.uni.other; 

import java.util.List; 

import org.hibernate.Query; 
import org.hibernate.Session; 
import org.hibernate.SessionFactory; 
import org.hibernate.Transaction; 
import org.hibernate.cfg.Configuration; 

public class Test { 
    public static void main(String[] args) { 

    Test test = new Test(); 
    // test.setup(); 
    test.SelectQuery1Implicit(); 
    // test.SelectQuery2ExplicitFromClause(); 
    // test.SelectQuery3JoinFetch(); 
    // test.SelectQuery4ThetaJoin(); 
    } 

    private void SelectQuery1Implicit() { 
    // Implicit: Find all UK club member who is female 
    Transaction t1 = HibernateUtil.begin(); 
    Query query = 
     HibernateUtil.getSession().createQuery("From Member2 m where m.club2.country = 'UK' "); 
    List<Member2> memList = query.list(); 
    for (Member2 m : memList) 
     System.out.println(m); 
    HibernateUtil.end(t1); 
    } 

    private void setup() { 
    Transaction t1 = HibernateUtil.begin(); 
    Club2 c1 = new Club2("MulaRougne", "UK"); 
    Member2 m1 = new Member2("aaa", 25, 'm'); 
    Member2 m2 = new Member2("bbb", 28, 'f'); 
    m1.setClub2(c1); 
    m2.setClub2(c1); 
    HibernateUtil.getSession().save(c1); 
    HibernateUtil.getSession().save(m1); 
    HibernateUtil.getSession().save(m2); 
    Club2 c2 = new Club2("Queen's Club", "UK"); 
    Club2 c3 = new Club2("Disney", "USA"); 
    Member2 m3 = new Member2("ccc", 32, 'm'); 
    Member2 m4 = new Member2("ddd", 23, 'm'); 
    m3.setClub2(c3); 
    m4.setClub2(c3); 
    HibernateUtil.getSession().save(m3); 
    HibernateUtil.getSession().save(m4); 
    HibernateUtil.getSession().save(c2); 
    HibernateUtil.getSession().save(c3); 


    /* 
    * Club2 c1 = new Club2("MulaRougne", "UK"); Club2 c2 = new Club2("Queen's Club", "UK"); Club2 
    * c3 = new Club2("Disney", "USA"); 
    * 
    * Member2 m1 = new Member2("aaa", 25, 'm'); Member2 m2 = new Member2("bbb", 28, 'f'); Member2 
    * m3 = new Member2("ccc", 32, 'm'); Member2 m4 = new Member2("ddd", 23, 'm'); Member2 m5 = new 
    * Member2("ee", 30, 'f'); 
    * 
    * c1.getMember2().add(m1); c1.getMember2().add(m2); c1.getMember2().add(m3); 
    * 
    * c2.getMember2().add(m4); c2.getMember2().add(m5); 
    * 
    * HibernateUtil.getSession().save(c2); HibernateUtil.getSession().save(m4); 
    * HibernateUtil.getSession().save(m5); 
    * 
    * HibernateUtil.getSession().save(c1); HibernateUtil.getSession().save(m1); 
    * HibernateUtil.getSession().save(m2); HibernateUtil.getSession().save(m3); 
    */ 
    HibernateUtil.end(t1); 
    } 


    private static class HibernateUtil { 
    private static SessionFactory factory; 
    private static Session session; 
    static { 
     factory = new Configuration().configure().buildSessionFactory(); 
    } 

    public static Session getSession() { 
     return session; 
    } 

    public static Transaction begin() { 
     session = factory.openSession(); 
     return session.beginTransaction(); 
    } 

    public static void end(Transaction tran) { 
     tran.commit(); 
    } 
    } 
} 
+0

avez essayé HQL comme: 'De Member2 comme m interne rejoindre m.club2 comme c où c.country = 'UK'' – Amogh

+0

Quel est le problème? La clause where a 'où member2x0_.club_id = club2x1_.club_id', donc Hibernate récupère correctement du club du membre. Tout va bien. –

+0

Oui Hib récupère correctement les données mais il effectue une jointure croisée à la place d'une jointure interne. Nous savons que cross join fait un produit croisé qui est lent et consomme de la mémoire. Ne devrait pas faire une jointure interne ?? C'est ma préoccupation .. Merci pour vos réponses. – anirban

Répondre

4

La plupart des moteurs de base de données permettra d'optimiser la CROSS JOIN avec une clause WHERE à une jointure de toute façon, mais je préfère toujours utiliser explicitement JOIN au lieu.

La CROSS JOIN est généré par le implicit JOIN:

where m.club2.country = 'UK' 

Pour éviter la deuxième requête du club vous pouvez écrire la requête comme suit:

Query query = HibernateUtil.getSession().createQuery(
     "select m " + 
     "from Member2 m " + 
     "join fetch m.club2 c" + 
     "where " + 
     " c.country = :country " 
) 
.setParameter("country", "UK"); 

Cette requête supprimera la CROSS JOIN et la sélection secondaire lors de l'utilisation de paramètres de liaison au lieu des paramètres codés en dur.

+0

Hey Merci pour votre réponse. Je connaissais la recherche de jointure. Mais vous avez effacé que hib ne peut pas générer de jointure interne, c'est surprenant. Va donner un autre essai.Vu ton blog, c'est un petit bijou. – anirban

+0

Merci de votre appréciation. J'espère que vous avez du plaisir à le lire. –

+0

Bien sûr. Je vais tout imprimer. – anirban