簡體   English   中英

Hibernate noob獲取連接問題

[英]Hibernate noob fetch join problem

我有兩個類,Test2和Test3。 Test2有一個屬性test3,它是Test3的一個實例。 換句話說,我有一個單向的OneToOne關聯,test2引用了test3。

當我從db中選擇Test2時,我可以看到正在進行單獨的選擇以獲取相關test3類的詳細信息。 這是着名的1 + N選擇問題。

要修復此問題以使用單個選擇,我嘗試使用fetch = join注釋,我理解為@Fetch(FetchMode.JOIN)

但是,如果將fetch設置為join,我仍會看到單獨的選擇。 以下是我的設置的相關部分..

hibernate.cfg.xml中:

<property name="max_fetch_depth">2</property>

測試2:

public class Test2 {
 @OneToOne (cascade=CascadeType.ALL , fetch=FetchType.EAGER)
 @JoinColumn (name="test3_id")
 @Fetch(FetchMode.JOIN)
 public Test3 getTest3() {
  return test3;
}

NB我將FetchType設置為EAGER絕望,即使它無論如何都默認為EAGER OneToOne映射,但它沒有任何區別。

謝謝你的幫助!

編輯:我幾乎放棄了嘗試使用FetchMode.JOIN - 任何人都可以確認他們已經讓它工作,即產生左外連接? 在文檔中,我看到“通常,映射文檔不用於自定義提取。相反,我們保留默認行為,並使用HQL中的左連接提取覆蓋特定事務”

如果我做一個左連接取代:

query = session.createQuery(“來自Test2 t2 left join fetch t2.test3”);

然后我確實得到了我想要的結果 - 即查詢中的左外連接。

編輯2:

伙計們,非常感謝您的回復。 現在我想深究這一點。 我經常發現,當我調查某些東西時,我最終學到的東西比我想象的要多得多。

我已經學到了一件事 - 我正在運行舊的hibernate構建,因為我沒有意識到maven存儲庫已經過時了。 現在我也連接到jboss存儲庫,我有最新版本的hibernate和hibernate注釋 - 在兩種情況下都是3.5.1-Final。

我已經設置了一個小的測試用例,盡可能地簡化它 - 我仍然在3.5.1-Final中看到問題,因為'我99%肯定這只是一些愚蠢我沒有設置是的,特別是羅斯,因為你得到它的工作(感謝花時間嘗試順便說一句)

所以我有這些課程(這次是全文)

A級

package com.play.hibernate2;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

@Entity
public class A {

    private Integer id;
    private B b;

    public A() {
        super();
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return id;
    }

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

    @OneToOne (cascade=CascadeType.ALL)
    @Fetch(FetchMode.JOIN)
    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

B級

package com.play.hibernate2;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class B {

    private Integer id;

    public B() {
        super();
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return id;
    }

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

我的整個hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <!-- <property name="connection.driver_class">com.p6spy.engine.spy.P6SpyDriver</property> -->
        <property name="connection.url">jdbc:mysql://localhost:3306/play</property>
        <property name="connection.username">play</property>
        <property name="connection.password">play</property>

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

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

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

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

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <property name="generate_statistics">true</property>
        <!--
        <property name="cache.use_structured_entries">true</property>
        <property name="cache.use_query_cache">true</property>
        -->
        <property name="format_sql">true</property>
        <property name="use_sql_comments">true</property>

        <!-- I think this may fix my individual requests for OneToOne problem -->
        <property name="max_fetch_depth">2</property>
        <!-- <property name="default_batch_fetch_size">10</property> -->

    </session-factory>    

</hibernate-configuration>

測試課

package com.play.hibernate2;

import java.util.List;
import java.util.Map;


import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class RunTests4 {
    private SessionFactory sessionFactory;

    public static void main(String[] args){
        RunTests4 d = new RunTests4();
        d.run3();
    }
    public void run3(){

        Session session = getSession();
        session.beginTransaction();

        createEntities(session);

        session.getTransaction().commit();

        System.out.println("NOW WITH A NEW TRANSACTION");
        session = getSession();
        session.beginTransaction();

        Query query = session.createQuery("from A");
        List results = query.list();
        for (int i=0; i<results.size(); i++){
            System.out.println("Row "+i+" was:");
            A a = (A)results.get(i);
            System.out.println("Result "+i);
            System.out.println(a.toString());
        }

        session.getTransaction().commit();


    }
    public void createEntities(Session session){
        for (int i=0; i<2; i++){
            A a = new A();

            B b = new B();

            a.setB(b);

            session.save(a);

        }

    }
    public Session getSession(){
        if (sessionFactory == null){
            AnnotationConfiguration config = new AnnotationConfiguration();
            config.addAnnotatedClass(A.class);
            config.addAnnotatedClass(B.class);
            config.configure();
            new SchemaExport(config).create(true,true);

            sessionFactory = config.buildSessionFactory();
        }
        Session session = sessionFactory.getCurrentSession();

        return session;
    }

}

最后,日志輸出顯示額外的選擇以返回關聯的類

2 [main] INFO org.hibernate.cfg.annotations.Version - Hibernate Annotations 3.5.1-Final
23 [main] INFO org.hibernate.cfg.Environment - Hibernate 3.5.1-Final
28 [main] INFO org.hibernate.cfg.Environment - hibernate.properties not found
32 [main] INFO org.hibernate.cfg.Environment - Bytecode provider name : javassist
37 [main] INFO org.hibernate.cfg.Environment - using JDK 1.4 java.sql.Timestamp handling
160 [main] INFO org.hibernate.annotations.common.Version - Hibernate Commons Annotations 3.2.0.Final
176 [main] INFO org.hibernate.cfg.Configuration - configuring from resource: /hibernate.cfg.xml
176 [main] INFO org.hibernate.cfg.Configuration - Configuration resource: /hibernate.cfg.xml
313 [main] INFO org.hibernate.cfg.Configuration - Configured SessionFactory: null
338 [main] INFO org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.MySQLDialect
462 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: com.play.hibernate2.Test2
545 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity com.play.hibernate2.Test2 on table Test2
649 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: com.play.hibernate2.Test3
650 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity com.play.hibernate2.Test3 on table Test3
651 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: com.play.hibernate2.A
651 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity com.play.hibernate2.A on table A
653 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: com.play.hibernate2.B
653 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity com.play.hibernate2.B on table B
678 [main] INFO org.hibernate.cfg.AnnotationConfiguration - Hibernate Validator not found: ignoring
687 [main] INFO org.hibernate.tool.hbm2ddl.SchemaExport - Running hbm2ddl schema export
688 [main] INFO org.hibernate.tool.hbm2ddl.SchemaExport - exporting generated schema to database
691 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Using Hibernate built-in connection pool (not for production use!)
691 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Hibernate connection pool size: 1
698 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - autocommit mode: false
711 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://localhost:3306/play
711 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - connection properties: {user=play, password=****}

    alter table A 
        drop 
        foreign key FK412E010759

    alter table Test2 
        drop 
        foreign key FK4CF5DC04B7E1B79

    drop table if exists A

    drop table if exists B

    drop table if exists Test2

    drop table if exists Test3

    create table A (
        id integer not null auto_increment,
        b_id integer,
        primary key (id)
    )

    create table B (
        id integer not null auto_increment,
        primary key (id)
    )

    create table Test2 (
        id integer not null auto_increment,
        name varchar(255),
        value integer not null,
        test3_id integer,
        primary key (id)
    )

    create table Test3 (
        id integer not null auto_increment,
        name varchar(255),
        value integer not null,
        primary key (id)
    )

    alter table A 
        add index FK412E010759 (b_id), 
        add constraint FK412E010759 
        foreign key (b_id) 
        references B (id)

    alter table Test2 
        add index FK4CF5DC04B7E1B79 (test3_id), 
        add constraint FK4CF5DC04B7E1B79 
        foreign key (test3_id) 
        references Test3 (id)
2562 [main] INFO org.hibernate.tool.hbm2ddl.SchemaExport - schema export complete
2564 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - cleaning up connection pool: jdbc:mysql://localhost:3306/play
2571 [main] INFO org.hibernate.cfg.search.HibernateSearchEventListenerRegister - Unable to find org.hibernate.search.event.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.
2575 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Using Hibernate built-in connection pool (not for production use!)
2575 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Hibernate connection pool size: 1
2575 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - autocommit mode: false
2575 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://localhost:3306/play
2575 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - connection properties: {user=play, password=****}
2622 [main] INFO org.hibernate.cfg.SettingsFactory - RDBMS: MySQL, version: 5.1.30
2622 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-5.1.9 ( Revision: ${svn.Revision} )
2633 [main] INFO org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.MySQLDialect
2635 [main] INFO org.hibernate.engine.jdbc.JdbcSupportLoader - Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
2636 [main] INFO org.hibernate.transaction.TransactionFactoryFactory - Using default transaction strategy (direct JDBC transactions)
2638 [main] INFO org.hibernate.transaction.TransactionManagerLookupFactory - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
2638 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic flush during beforeCompletion(): disabled
2638 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic session close at end of transaction: disabled
2638 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC batch size: 15
2638 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC batch updates for versioned data: disabled
2638 [main] INFO org.hibernate.cfg.SettingsFactory - Scrollable result sets: enabled
2638 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC3 getGeneratedKeys(): enabled
2638 [main] INFO org.hibernate.cfg.SettingsFactory - Connection release mode: auto
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Maximum outer join fetch depth: 2
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Default batch fetch size: 1
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Generate SQL with comments: enabled
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL updates by primary key: disabled
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL inserts for batching: disabled
2639 [main] INFO org.hibernate.cfg.SettingsFactory - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
2641 [main] INFO org.hibernate.hql.ast.ASTQueryTranslatorFactory - Using ASTQueryTranslatorFactory
2641 [main] INFO org.hibernate.cfg.SettingsFactory - Query language substitutions: {}
2641 [main] INFO org.hibernate.cfg.SettingsFactory - JPA-QL strict compliance: disabled
2641 [main] INFO org.hibernate.cfg.SettingsFactory - Second-level cache: enabled
2641 [main] INFO org.hibernate.cfg.SettingsFactory - Query cache: disabled
2644 [main] INFO org.hibernate.cfg.SettingsFactory - Cache region factory : org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge
2644 [main] INFO org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge - Cache provider: org.hibernate.cache.NoCacheProvider
2644 [main] INFO org.hibernate.cfg.SettingsFactory - Optimize cache for minimal puts: disabled
2644 [main] INFO org.hibernate.cfg.SettingsFactory - Structured second-level cache entries: disabled
2648 [main] INFO org.hibernate.cfg.SettingsFactory - Echoing all SQL to stdout
2648 [main] INFO org.hibernate.cfg.SettingsFactory - Statistics: enabled
2649 [main] INFO org.hibernate.cfg.SettingsFactory - Deleted entity synthetic identifier rollback: disabled
2649 [main] INFO org.hibernate.cfg.SettingsFactory - Default entity-mode: pojo
2649 [main] INFO org.hibernate.cfg.SettingsFactory - Named query checking : enabled
2649 [main] INFO org.hibernate.cfg.SettingsFactory - Check Nullability in Core (should be disabled when Bean Validation is on): enabled
2697 [main] INFO org.hibernate.impl.SessionFactoryImpl - building session factory
2796 [Finalizer] INFO org.hibernate.connection.DriverManagerConnectionProvider - cleaning up connection pool: jdbc:mysql://localhost:3306/play
2929 [main] INFO org.hibernate.impl.SessionFactoryObjectFactory - Not binding factory to JNDI, no JNDI name configured
Hibernate: 
    /* insert com.play.hibernate2.B
        */ insert 
        into
            B

        values
            ( )
Hibernate: 
    /* insert com.play.hibernate2.A
        */ insert 
        into
            A
            (b_id) 
        values
            (?)
Hibernate: 
    /* insert com.play.hibernate2.B
        */ insert 
        into
            B

        values
            ( )
Hibernate: 
    /* insert com.play.hibernate2.A
        */ insert 
        into
            A
            (b_id) 
        values
            (?)
NOW WITH A NEW TRANSACTION
Hibernate: 
    /* 
from
    A */ select
        a0_.id as id2_,
        a0_.b_id as b2_2_ 
    from
        A a0_
Hibernate: 
    /* load com.play.hibernate2.B */ select
        b0_.id as id3_0_ 
    from
        B b0_ 
    where
        b0_.id=?
Hibernate: 
    /* load com.play.hibernate2.B */ select
        b0_.id as id3_0_ 
    from
        B b0_ 
    where
        b0_.id=?
Row 0 was:
Result 0
com.play.hibernate2.A@351daa0e
Row 1 was:
Result 1
com.play.hibernate2.A@2e879860

編輯3:

如果我以羅斯的方式做事,加載時會創建一個左外連接。 如果我使用列表執行此操作,則會發出單獨的選擇。 這是相關的代碼。 只有改變它才能重現行為上的差異:

    /* generates the left outer join
    A a = (A)session.load(A.class,1);
    System.out.println(a.getId()+" = "+a.getB().getName());
    */

    // Creates separate selects for each object b associated with each a
    Query query = session.createQuery("from A");
    List results = query.list();
    A a = (A)results.get(0);
    System.out.println(a.getId()+" = "+a.getB().getName());

我猜它可能被稱為'bug'。 正如我之前提到的,在文檔中,他們說在HQL中而不是在映射中指定獲取模式是“通常的”,我認為這可能意味着HQL方式有更多的人流量來控制它。 。?

(順便說一下,我給A和B添加了一個額外的'name'字段,否則hibernate優化了檢索,因為它可以從A上的外鍵獲得所有B)

作為蒸餾:

如果使用Query接口(例如:session.createQuery()),將忽略@Fetch(JOIN),但如果使用Criteria接口,它將被正確使用。

這實際上是Hibernate中的一個從未解決的錯誤。 很不幸,因為許多應用程序使用Query接口,無法輕松遷移到Criteria接口。

如果使用Query接口,則必須手動將JOIN FETCH語句添加到HQL中。

當我從db中選擇Test2時,我可以看到正在進行單獨的選擇以獲取相關test3類的詳細信息。

我對其他答案的代碼非常感興趣,因為這也是我在測試你正在展示的代碼時所看到的,它為from Test2生成兩個選擇。

我正在使用以下依賴項:

  • org.hibernate作為:休眠-的EntityManager:罐子:3.4.0.GA:編譯
  • org.hibernate作為:EJB3持久性:罐子:1.0.2.GA:編譯
  • org.hibernate作為:冬眠公地的注解:罐子:3.1.0.GA:編譯
  • org.hibernate作為:冬眠的注解:罐子:3.4.0.GA:編譯
  • org.hibernate作為:休眠核心:罐子:3.3.0.SP1:編譯

我將FetchType設置為EAGER絕望,即使它無論如何都默認為EAGER OneToOne映射,但它沒有任何區別。

如果使用Hibernate注釋,這沒有任何影響,因為Hibernate注釋會覆蓋EJB3提取選項。 2.4.5.1。 懶惰選項和提取模式

我創建了一個非常簡單的應用程序來測試你得到的場景,你的代碼應該工作(它對我有用)。 我嘗試過的唯一能給我多個select語句的是將max_fetch_depth設置為0.如果設置為2(或未配置),我在查詢中得到左外連接。 您使用的是什么版本的hibernate? 我使用的是3.4.0.GA.

編輯:下面是我使用的簡單應用程序(與Pascal提到的相同版本):

CFG:

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="hibernate.connection.url">jdbc:hsqldb:hibscribs</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password"></property>
        <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">create-drop</property>
        <property name="current_session_context_class">thread</property>
        <!-- property name="max_fetch_depth">0</property--><!-- uncomment to see where 2 selects are used instead of join -->

        <mapping class="com.mydomain.bo.Person" />
        <mapping class="com.mydomain.bo.Phone" />

    </session-factory>
</hibernate-configuration>

人物實體 - 保持簡單只有@OneToOne,添加JoinColumn等沒有區別。

@Entity
@Table(name="person")
public class Person {
    private Long id;
    private String name;
    private Phone phone;

    @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;
    }

    @OneToOne(cascade=CascadeType.ALL)
    public Phone getPhone() {
        return phone;
    }

    public void setPhone(Phone phone) {
        this.phone = phone;
    }   
}

@Entity
@Table(name="phone")
public class Phone {
    private Long id;
    private String number;

    @Id
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

簡單測試:

SessionFactory session = HibernateUtil.getSessionFactory();
Session sess = session.getCurrentSession();
Transaction tx = sess.beginTransaction();

Phone phone = new Phone();
phone.setId(1L);
phone.setNumber("1234567");

Person person = new Person();
person.setId(1L);
person.setName("Bob");
person.setPhone(phone);

sess.save(person);

tx.commit(); 

sess = session.openSession();

//Person p1 = (Person)sess.load(Person.class,1L);
//System.out.println(p1.getPhone().getNumber());

// changed the above code to use the Criteria interface below: 
Criteria criteria = sess.createCriteria(Person.class);
List<Person> results = criteria.list();
for (int i=0; i<results.size(); i++){
  Person p = (Person)results.get(i);
  System.out.println(p.getPhone().getNumber());
}

輸出:

Hibernate: 
    select
        phone_.id,
        phone_.number as number1_ 
    from
        phone phone_ 
    where
        phone_.id=?
Hibernate: 
    insert 
    into
        phone
        (number, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        person
        (name, phone_id, id) 
    values
        (?, ?, ?)
Hibernate: 
    select
        person0_.id as id0_1_,
        person0_.name as name0_1_,
        person0_.phone_id as phone3_0_1_,
        phone1_.id as id1_0_,
        phone1_.number as number1_0_ 
    from
        person person0_ 
    left outer join
        phone phone1_ 
            on person0_.phone_id=phone1_.id 
    where
        person0_.id=?
1234567

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM