2017-02-17 137 views
0

那麼,這個標題有很多問題,但沒有一個有正確的答案,或者他們不完全一樣。JPA /休眠FetchType.LAZY不工作

我有兩個實體:

人:

@Entity 
@Table(name = "Person") 
@Inheritance(strategy = InheritanceType.JOINED) 
@Access(AccessType.FIELD) 
public class Person { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @Column(name = "firstname") 
    private String firstName; 

    @Column(name = "lastname", length = 100, nullable = false, unique = false) 
    private String lastName; 

    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.MERGE, mappedBy="owner") 
    private Set<Car> cars; 

    public Long getId() { 

     return id; 
    } 

    public void setId(Long id) { 

     this.id = id; 
    } 

    public String getFirstName() { 

     return firstName; 
    } 

    public void setFirstName(String firstName) { 

     this.firstName = firstName; 
    } 

    public String getLastName() { 

     return lastName; 
    } 

    public void setLastName(String lastName) { 

     this.lastName = lastName; 
    } 

    public Set<Car> getCars() { 

     return cars; 
    } 

    public void setCars(Set<Car> cars) { 

     this.cars = cars; 
    } 

    @Override 
    public String toString() { 

     return String.format("(%d, %s, %s)",id, firstName, lastName); 
    } 
} 

以及汽車:

@Entity 
@Table(name = "Car") 
@Inheritance(strategy = InheritanceType.JOINED) 
@Access(AccessType.FIELD) 
public class Car { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE) 
    @JoinColumn(name="id_person", columnDefinition="BIGINT") 
    private Person owner; 

    @Column(name="name") 
    private String name; 

    @Column(name="model") 
    private String model; 

    public Long getId() { 

     return id; 
    } 

    public void setId(Long id) { 

     this.id = id; 
    } 

    public Person getOwner() { 

     return owner; 
    } 

    public void setOwner(Person owner) { 

     this.owner = owner; 
    } 

    public String getName() { 

     return name; 
    } 

    public void setName(String name) { 

     this.name = name; 
    } 

    public String getModel() { 

     return model; 
    } 

    public void setModel(String model) { 

     this.model = model; 
    } 

    @Override 
    public String toString() { 

     return String.format("(%d, %s, %s, %s)", id, name, model, owner); 
    } 
} 

而且我在人與車之間的表我的MySQL數據庫中定義的外鍵約束在id_person列

我的persistence.xml文件如下:

<?xml version="1.0" encoding="UTF-8" ?> 
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> 

    <persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL"> 

     <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> 

     <properties> 
      <!-- Configuring JDBC properties --> 
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/hibernatedb" /> 
      <property name="javax.persistence.jdbc.user" value="root" /> 
      <property name="javax.persistence.jdbc.password" value="DA_PASSWORD" /> 
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> 

      <!-- Hibernate properties --> 
      <property name="hibernate.show_sql" value="true" /> 
      <property name="hibernate.format_sql" value="true" /> 
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> 
      <property name="hibernate.hbm2ddl.auto" value="validate" /> 

      <!-- Configuring Connection Pool --> 
      <property name="hibernate.c3p0.min_size" value="5" /> 
      <property name="hibernate.c3p0.max_size" value="20" /> 
      <property name="hibernate.c3p0.timeout" value="500" /> 
      <property name="hibernate.c3p0.max_statements" value="50" /> 
      <property name="hibernate.c3p0.idle_test_period" value="2000" /> 
     </properties> 
    </persistence-unit> 
</persistence> 

裏面我的代碼,我儘量選擇使用標準查詢汽車如下:

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 
CriteriaQuery<Car> q = cb.createQuery(Car.class); 
Root<Car> root = q.from(Car.class); 

q.select(root); 

當我得到的結果,並打印出來

TypedQuery<Car> typedQuery = entityManager.createQuery(q); 
List<Car> cars = typedQuery.getResultList(); 
log.info("*****************************{}", cars); 

令我驚訝的是,它打印:

*****************************[(1, Hiundai, 2016, (1, Homer1530962140, Simpson)), (2, Benz, 2016, (1, Homer1530962140, Simpson)), (3, Benz, 
2017, (2, Homer12935192, Simpson))] 

這意味着每個汽車用品,業主也熱切期盼!

這裏是數據庫查詢日誌:

2017-02-17T14:02:58.324926Z 391 Query /* mysql-connector-java-5.1.13 (Revision: ${bzr.revision-id}) */SELECT @@session.auto_increment_increment 
2017-02-17T14:02:58.325405Z 391 Query SHOW COLLATION 
2017-02-17T14:02:58.335552Z 391 Query SET NAMES latin1 
2017-02-17T14:02:58.335772Z 391 Query SET character_set_results = NULL 
2017-02-17T14:02:58.336160Z 391 Query SET autocommit=1 
2017-02-17T14:02:58.336349Z 391 Query SET autocommit=0 
2017-02-17T14:02:58.720821Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Car' 
2017-02-17T14:02:58.724527Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Car' 
2017-02-17T14:02:58.725337Z 391 Query SHOW FULL COLUMNS FROM `Car` FROM `hibernatedb` LIKE '%' 
2017-02-17T14:02:58.729899Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Person' 
2017-02-17T14:02:58.730468Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Person' 
2017-02-17T14:02:58.730887Z 391 Query SHOW FULL COLUMNS FROM `Person` FROM `hibernatedb` LIKE '%' 
2017-02-17T14:02:59.022835Z 391 Query select car0_.id as id1_0_, car0_.model as model2_0_, car0_.name as name3_0_, car0_.id_person as id_perso4_0_ from Car car0_ 
2017-02-17T14:02:59.041016Z 391 Query SHOW WARNINGS 
2017-02-17T14:02:59.045266Z 391 Query select person0_.id as id1_1_0_, person0_.firstname as firstnam2_1_0_, person0_.lastname as lastname3_1_0_ from Person person0_ where person0_.i 
d=1 
2017-02-17T14:02:59.059184Z 391 Query SHOW WARNINGS 
2017-02-17T14:02:59.064163Z 391 Query select person0_.id as id1_1_0_, person0_.firstname as firstnam2_1_0_, person0_.lastname as lastname3_1_0_ from Person person0_ where person0_.i 
d=2 
2017-02-17T14:02:59.065827Z 391 Query SHOW WARNINGS 
2017-02-17T14:02:59.070262Z 391 Query rollback 
2017-02-17T14:02:59.070468Z 391 Quit 

這是很明顯的,一個單獨的查詢是爲了獲得個人信息,而我似乎沒有問這樣的事情在我的代碼。

這是怎麼發生的?

回答

2

當您將汽車轉換爲字符串時,您請求了車主信息。

@Override 
public String toString() { 

    return String.format("(%d, %s, %s, %s)", id, name, model, owner); 
} 

此時,它必須檢索所有者才能執行toString()。

+0

謝謝@John Holmer。我自己正在考慮這部分代碼,但說實話,我很難理解JPA如何能夠檢測訪問實體提交的內容並基於此創建查詢。 – madz

2
log.info("*****************************{}", cars); 

當您執行這一點:

  1. 日誌框架列表
  2. toString()被稱爲在汽車類的每個Car
  3. toString方法的調用toString()調用PersontoString()方法。這是「魔法」發生的部分。結果Hibernate使用生成的代理類爲一些關聯啓用延遲加載(一對一,多對一)。換句話說,Hibernate通過引用擴展Person類的代理類來初始化字段Car.owner。此代理類包含處理延遲加載的其他邏輯。
+0

+1瞭解更多詳情。我一直在尋找一些時間來看看hibernate如何能夠攔截使用javassist的字段訪問 – madz