JPA/Hibernate FetchType.LAZY 无效

JPA/Hibernate FetchType.LAZY is not working

好吧,有很多问题与这个标题完全相同,但其中 none 个有正确的答案,或者与我的不完全相同。

我有两个实体:

人:

@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 列

上的 Person 和 Car table 之间定义了一个外键约束

我的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>

在我的代码中,我尝试 select 汽车使用条件查询如下:

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

很明显,发出了一个单独的查询来获取人员信息,而我似乎没有在我的代码中问这样的事情。

为什么会这样?

您在将汽车转换为字符串时请求车主信息。

@Override
public String toString() {

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

那时,它必须检索所有者才能执行您的 toString()。

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

当您执行此操作时:

  1. 在列表中记录框架调用 toString()
  2. toString() 在每个 Car
  3. 上被调用 Car class 的
  4. toString 方法调用 Person class 的 toString() 方法。这是 "magic" 发生的部分。事实证明,Hibernate 使用生成的代理 classes 为某些关联(one-to-one、many-to-one)启用延迟加载。换句话说,Hibernate 使用对扩展 Person class 的代理 class 的引用来初始化字段 Car.owner。此代理 class 包含处理延迟加载的附加逻辑。