JPA / Hibernate FetchType.LAZY不起作用

时间:2017-02-17 18:13:57

标签: java mysql hibernate jpa lazy-loading

嗯,这个标题有很多问题,但没有一个有正确的答案,或者它们与我的不完全相同。

我有两个实体:

人:

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

此外,我在id_person列上的Person和Car表之间的Mysql数据库中定义了外键约束

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

在我的代码中,我尝试使用Criteria查询选择Cars如下:

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

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

为什么会这样?

2 个答案:

答案 0 :(得分:2)

您在将汽车转换为字符串时请求了所有者信息。

@Override
public String toString() {

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

此时,必须检索所有者以执行toString()。

答案 1 :(得分:2)

string master_ConnectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;Database=Master;Integrated Security=True;Connect Timeout=30;";

using (SqlConnection restoreConn = new SqlConnection())
{
    restoreConn.ConnectionString = master_ConnectionString;
    restoreConn.Open();

    using (SqlCommand restoredb_executioncomm = new SqlCommand())
    {
        restoredb_executioncomm.Connection = restoreConn;
        restoredb_executioncomm.CommandText = @"RESTORE DATABASE yourdbname FROM DISK='c:\yourdbname.bak'";

        restoredb_executioncomm.ExecuteNonQuery();
    }
    restoreConn.Close();
}

执行此操作时:

  1. 记录框架在列表中调用log.info("*****************************{}", cars);
  2. 每个toString() 都会调用
  3. toString() car类的
  4. Car方法调用toString类的toString()方法。这就是&#34;魔术&#34;发生。事实证明,Hibernate使用生成的代理类来为一些关联启用延迟加载(一对一,多对一)。换句话说,Hibernate通过引用扩展Person类的代理类来初始化字段Car.owner。此代理类包含处理延迟加载的其他逻辑。