Hibernate的优化方案

  使用参数绑定
  
  使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用生成好的执行计划,这样做节省CPU时间和内存。
  
  避免SQL注入。
  
  尽量少使用NOT
  
  如果where子句中包含not关键字,那么执行时该字段的索引失效。
  
  尽量使用where来替换having
  
  having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销。
  
  减少对表的查询
  
  在含有子查询的HQL中,尽量减少对表的查询,降低开销。
  
  使用表的别名
  
  当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列连接上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。
  
  实体的更新与删除
  
  在Hibernate3以后支持hql的update与delete操作。可参考度娘。
  
  一级缓存优化
  
  一级缓存也叫做session缓存,在一个hibernate session有效,这级缓存的可干预性不强,大多于hibernate自动管理,但它提供清除缓存的方法,这在大批量增加(更新)操作是有效果的,例如,同时增加十万条记录,按常规进行,很可能会出现异常,这时可能需要手动清除一级缓存,session.evict以及session.clear。
  
  检索策略(抓取策略)
  
  延迟加载
  
  延迟加载是Hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。load方法采用的策略是延迟加载;get方法采用的策略是立即加载。
  
  检索策略分为两种:
  
  类级别检索
  
  关联级别检索
  
  类级别检索
  
  类级别检索是通过session直接检索某一类对应的数据,例如:
  
  Customer c = session.load(Customer.class, 1);
  
  1
  
  1
  
  或
  
  session.createQuery("from Order");
  
  1
  
  1
  
  类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过<class>元素的lazy属性来设置,默认值是true。所以我们可在hbm映射配置文件中设置如下:
  
  <class name="Customer" table="t_customer" catalog="hibernateTest" lazy="true">
  
  ...
  
  </class>
  
  1
  
  2
  
  3
  
  1
  
  2
  
  3
  
  除此之外,我们也可在PO类中使用@Proxy注解,例如:
  
  @Proxy(lazy = true)
  
  public class Customer {
  
  ...
  
  }
  
  1
  
  2
  
  3
  
  4
  
  1
  
  2
  
  3
  
  4
  
  现在我着重来讲一下在PO类中@Proxy注解的使用。提示,以下所有案例代码的编写都是建立在Hibernate检索方式概述一文案例基础之上的。首先将@Proxy(lazy = true)这样的注解加在PO类——Customer类上,这样Customer类的代码就变成:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  @NamedQuery(name="myHql", query="from Customer")
  
  // @SqlResultSetMapping注解才真正帮我们去规定执行sql语句如何将结果封装到Customer对象
  
  @SqlResultSetMapping(name="customerSetMapping",entities={ @EntityResult(entityClass=Customer.class,fields={
  
  @FieldResult(name="id",column="id"),@FieldResult(name="name",column="name") }) })
  
  // fields指定类里面的每一个属性跟表中的列是如何对应的
  
  @NamedNativeQuery(name="findCustomer",query="select * from t_customer",resultSetMapping="customerSetMapping")
  
  // resultSetMapping需要指定一个名字,它用来指定结果如何封装的操作
  
  @Proxy(lazy = true)
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String www.xyseo.net name; // 姓名
  
  // 描述客户可以有多个订单
  
  /*
  
  * targetEntity="...":相当于<one-to-many class="...">
  
  * mappedBy="...":相当于inverse=true,即放弃关联关系的维护,不然会生成一个中间表
  
  */
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString(www.baqist.cn) {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  52
  
  53
  
  54
  
  55
  
  56
  
  57
  
  58
  
  59
  
  60
  
  61
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  52
  
  53
  
  54
  
  55
  
  56
  
  57
  
  58
  
  59
  
  60
  
  61
  
  为了便于演示延迟加载,在cn.itheima.test包下编写一个LoadTest单元测试类,并在该类中编写如下测试方法:
  
  // 演示延迟加载
  
  public class LoadTest {
  
  @Test
  
  public void test1() {
  
  Session session = HibernateUtils.openSession();
  
  session.beginTransaction();
  
  Customer c1 = session.load(Customer.class, 1);
  
  // Customer c1 = session.get(Customer.class, 1);
  
  String name = c1.getName();
  
  System.out.println(name);
  
  session.getTransaction().commit();
  
  session.close();
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  在String name = c1.getName();这句代码上加上一个断点,然后以Debug的方式调试该程序,就能得到你想要的东西哟!!!
  
  如果将lazy设置为false,代表类级别检索也使用立即检索,这时load与get方法就完全一样了,都是立即检索。
  
  虽然我们是知道了load方法采用的策略是延迟加载;get方法采用的策略是立即加载,但是什么时候用get方法,什么时候用load方法呢?——如果你查询的数据非常大,例如说它里面有一些大的字段,这个时候建议你采用load方法,不要一上来就立即加载,把我们的内存占满,这样可以让我们的性能得到一部分的提升;如果你查询的数据非常少,直接get就无所谓了,因为它不会占用我们很多的内存。
  
  还有一个问题:Hibernate这个框架是在Dao层进行操作的,如果说我现在采用了一个load的方案去获取了一个对象,我们最终会把Session关闭再返回,那么我们就要把这个对象返回到Service层,再返回到Web层,这个时候load出来的代理对象其实还没有对数据进行初始化,也即它里面还没有真正有数据,返回的时候就出问题了,那如何对一个延迟的代理对象进行初始化呢?以码明示,在LoadTest单元测试类中编写如下测试方法:
  
  public class LoadTest {
  
  // 如果对一个延迟的代理对象进行初始化?
  
  @Test
  
  public void test2() {
  
  Session session = HibernateUtils.openSession();
  
  session.beginTransaction();
  
  Customer c1 = session.load(Customer.class, 1);
  
  Hibernate.initialize(c1);
  
  session.getTransaction().commit();
  
  session.close();
  
  // return c1;
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  在Customer c1 = session.load(Customer.class, 1);这句代码上加上一个断点,然后以Debug的方式调试该程序,就能得到你想要的东西哟!!!
  
  关联级别检索
  
  查询到某个对象,获得其关联的对象或属性,这种就称为关联级别检索,例如:
  
  c.getOrders().size()
  
  c.getName()
  
  1
  
  2
  
  1
  
  2
  
  对于关联级别检索我们就要研究其检索策略(抓取策略)了。
  
  检索策略(抓取策略)
  
  抓取策略介绍
  
  抓取策略指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。Hibernate中对象之间的关联关系有:
  
  一对一:<one-to-one>
  
  一对多(多对一):<set>下有<one-to-many>,与<many-to-one>
  
  多对多:<set>下有<many-to-many>
  
  此处我们主要讲的是在<set>与<many-to-one>或<one-to-one>标签上设置fetch、lazy这两个属性。
  
  fetch主要描述的是SQL语句的格式(例如是多条,子查询,多表联查)
  
  lazy用于控制SQL语句何时发送
  
  例如,查询一个客户,要关联查询它的订单。客户代表一的一方,在客户中有set集合来描述其订单,在配置中我们是使用的:
  
  <set>
  
  <one-to-many>
  
  </set>
  
  1
  
  2
  
  3
  
  1
  
  2
  
  3
  
  此时就可以在set标签上设置这两个属性fetch、lazy。
  
  再比如,查询一个订单时,要查询关联的客户信息。订单代表多的一方,在订单中有Customer对象来描述其关联的客户,在配置中我们是使用<many-to-one>标签,此时也可以在该标签上设置这两个属性fetch、lazy。当然了,也可在标签<one-to-one>上设置这两个属性fetch、lazy。
  
  注解配置抓取策略
  
  以一个问题来引出该小节的讲解,如何使用注解来配置抓取策略?
  
  在<set>标签上设置的fetch与lazy可以使用下面注解来描述:
  
  @Fetch(FetchMode.SUBSELECT)
  
  @LazyCollection(LazyCollectionOption.EXTRA)
  
  private Set<Order> orders = new HashSet<Order>();
  
  1
  
  2
  
  3
  
  1
  
  2
  
  3
  
  若是在映射配置文件中进行设置,则如下:
  
  这里写图片描述
  
  在<many-to-one>或<one-to-one>标签上设置的fetch与lazy可以使用下面注解来描述:
  
  @Fetch(FetchMode.SELECT)
  
  @LazyToOne(LazyToOneOption.FALSE)
  
  private Customer c; // 描述订单属于某一个客户
  
  1
  
  2
  
  3
  
  1
  
  2
  
  3
  
  若是在映射配置文件中进行设置,则如下:
  
  这里写图片描述
  
  set上的fetch与lazy
  
  set上的fetch与lazy主要是用于设置关联的集合信息的抓取策略。
  
  fetch可取值有:
  
  SELECT:多条简单的sql(默认值)
  
  JOIN:采用迫切左外连接
  
  SUBSELECT:将生成子查询的SQL
  
  lazy可取值有:
  
  TURE:延迟检索(默认值)
  
  FALSE:立即检索
  
  EXTRA:加强延迟检索(及其懒惰)
  
  这样说来,fetch与lazy的组合就有九种了,其实不然,fetch与lazy的组合实际上只有七种,且听我娓娓道来。
  
  第一种组合
  
  首先修改cn.itheima.domain包下的两个PO类,如下:
  
  客户类
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SELECT)
  
  @LazyCollection(LazyCollectionOption.TRUE)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  订单类
  
  // 订单 ---- 多的一方
  
  @Entity
  
  @Table(name="t_order")
  
  public class Order {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id;
  
  private Double money;
  
  private String receiverInfo; // 收货地址
  
  // 订单与客户关联
  
  @ManyToOne(targetEntity=Customer.class)
  
  @JoinColumn(name="c_customer_id") // 指定外键列
  
  @Cascade(CascadeType.SAVE_UPDATE)
  
  private Customer c; // 描述订单属于某一个客户
  
  public Customer getC() {
  
  return c;
  
  }
  
  public void setC(Customer c) {
  
  this.c = c;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public Double getMoney() {
  
  return money;
  
  }
  
  public void setMoney(Double money) {
  
  this.money = money;
  
  }
  
  public String getReceiverInfo() {
  
  return receiverInfo;
  
  }
  
  public void setReceiverInfo(String receiverInfo) {
  
  this.receiverInfo = receiverInfo;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Order [id=" + id + ", money=" + money + ", receiverInfo=" + receiverInfo + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  接着在cn.itheima.test包下编写一个SetFetchTest单元测试类,并在该类中编写如下测试方法:
  
  public class SetFetchTest {
  
  @Test
  
  public void test1() {
  
  Session session = HibernateUtils.openSession();
  
  session.beginTransaction();
  
  // 1.得到id=1的Customer
  
  Customer customer = session.get(Customer.class, 1);
  
  // 2.得到id=1的Customer关联的Order信息
  
  int size = customer.getOrders().size();
  
  System.out.println(size);
  
  session.getTransaction().commit();
  
  session.close();
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  在int size = customer.getOrders().size();这句代码上加上一个断点,然后以Debug的方式调试该程序,就能得出结论:会首先查询客户信息,当需要订单信息时,才会关联查询订单信息,并在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_0_,
  
  customer0_.name as name2_0_0_
  
  from
  
  t_customer customer0_
  
  where
  
  customer0_.id=?
  
  Hibernate:
  
  select
  
  orders0_.c_customer_id as c_custom4_1_0_,
  
  orders0_.id as id1_1_0_,
  
  orders0_.id as id1_1_1_,
  
  orders0_.c_customer_id as c_custom4_1_1_,
  
  orders0_.money as money2_1_1_,
  
  orders0_.receiverInfo as receiver3_1_1_
  
  from
  
  t_order orders0_
  
  where
  
  orders0_.c_customer_id=?
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  第二种组合
  
  首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SELECT)
  
  @LazyCollection(LazyCollectionOption.FALSE)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  然后以Debug的方式运行SetFetchTest单元测试类中的test1方法,就能得出结论:当查询客户信息时,就会将订单信息也查询,也就是说订单信息没有进行延迟查询。并在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_0_,
  
  customer0_.name as name2_0_0_
  
  from
  
  t_customer customer0_
  
  where
  
  customer0_.id=?
  
  Hibernate:
  
  select
  
  orders0_.c_customer_id as c_custom4_1_0_,
  
  orders0_.id as id1_1_0_,
  
  orders0_.id as id1_1_1_,
  
  orders0_.c_customer_id as c_custom4_1_1_,
  
  orders0_.money as money2_1_1_,
  
  orders0_.receiverInfo as receiver3_1_1_
  
  from
  
  t_order orders0_
  
  where
  
  orders0_.c_customer_id=?
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  第三种组合
  
  首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SELECT)
  
  @LazyCollection(LazyCollectionOption.EXTRA)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  然后以Debug的方式运行SetFetchTest单元测试类中的test1方法,就能得出结论:当查询客户信息时,不会查询订单信息,当需要订单的个数时,也不会查询订单信息,只会通过count来统计订单个数,当我们使用size()、contains()或isEmpty()方法时也不会查询订单信息。并在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_0_,
  
  customer0_.name as name2_0_0_
  
  from
  
  t_customer customer0_
  
  where
  
  customer0_.id=?
  
  Hibernate:
  
  select
  
  count(id)
  
  from
  
  t_order
  
  where
  
  c_customer_id =?
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  第四种组合
  
  如果fetch选择的是join方案,那么lazy它就会失效。生成SQl语句采用的是迫切左外连接(left outer join fetch),也即这个时候会多表联查,既然是多表联查,就会把信息都查询出来,它既然是一个迫切左外连接,会根据你的需求把信息封装到你指定的对象里面,所以lazy它就会失效。
  
  为了测试这第四种组合,首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.JOIN)
  
  @LazyCollection(LazyCollectionOption.FALSE)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  然后以Debug的方式运行SetFetchTest单元测试类中的test1方法,在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_0_,
  
  customer0_.name as name2_0_0_,
  
  orders1_.c_customer_id as c_custom4_1_1_,
  
  orders1_.id as id1_1_1_,
  
  orders1_.id as id1_1_2_,
  
  orders1_.c_customer_id as c_custom4_1_2_,
  
  orders1_.money as money2_1_2_,
  
  orders1_.receiverInfo as receiver3_1_2_
  
  from
  
  t_customer customer0_
  
  left outer join
  
  t_order orders1_
  
  on customer0_.id=orders1_.c_customer_id
  
  where
  
  customer0_.id=?
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  第五种组合
  
  首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SUBSELECT)
  
  @LazyCollection(LazyCollectionOption.TRUE)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", www.120xh.cn name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  接着在SetFetchTest单元测试类中编写如下测试方法:
  
  public class SetFetchTest {
  
  @SuppressWarnings("unchecked")
  
  @Test
  
  public void test2() {
  
  Session session = HibernateUtils.openSession();
  
  session.beginTransaction();
  
  // 1.查询出所有的客户信息
  
  List<Customer> list = session.createQuery("from Customer").list();
  
  for (Customer customer : list) {
  
  System.out.println(customer.getOrders().size());
  
  }
  
  session.getTransaction().commit();
  
  session.close();
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  在List<Customer> list = session.createQuery("from Customer").list();这句代码上加上一个断点,然后以Debug的方式调试该程序,就能得出结论:会生成子查询,但是我们在查询订单时采用的是延迟加载。并在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_,
  
  customer0_.name as name2_0_
  
  from
  
  t_customer customer0_
  
  Hibernate:
  
  select
  
  orders0_.c_customer_id as c_custom4_1_1_,
  
  orders0_.id as id1_1_1_,
  
  orders0_.id as id1_1_0_,
  
  orders0_.c_customer_id as c_custom4_1_0_,
  
  orders0_.money as money2_1_0_,
  
  orders0_.receiverInfo as receiver3_1_0_
  
  from
  
  t_order orders0_
  
  where
  
  orders0_.c_customer_id in (
  
  select
  
  customer0_.id
  
  from
  
  t_customer customer0_
  
  )
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  第六种组合
  
  首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SUBSELECT)
  
  @LazyCollection(LazyCollectionOption.FALSE)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer() {
  
  }
  
  public Customer(Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  然后以Debug的方式运行SetFetchTest单元测试类中的test2方法,就能得出结论:会生成子查询,在查询客户信息时,就会将订单信息也查询出来。并在Eclipse控制台打印如下sql语句:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_,
  
  customer0_.name as name2_0_
  
  from
  
  t_customer customer0_
  
  Hibernate:
  
  select
  
  orders0_.c_customer_id as c_custom4_1_1_,
  
  orders0_.id as id1_1_1_,
  
  orders0_.id as id1_1_0_,
  
  orders0_.c_customer_id as c_custom4_1_0_,
  
  orders0_.money as money2_1_0_,
  
  orders0_.receiverInfo as receiver3_1_0_
  
  from
  
  t_order orders0_
  
  where
  
  orders0_.c_customer_id in (
  
  select
  
  customer0_.id
  
  from
  
  t_customer customer0_
  
  )
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  第七种组合
  
  首先将客户类的代码改为:
  
  // 客户 ---- 一的一方
  
  @Entity
  
  @Table(name="t_customer")
  
  public class Customer {
  
  @Id
  
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  private Integer id; // 主键
  
  private String name; // 姓名
  
  // 描述客户可以有多个订单
  
  @OneToMany(targetEntity=Order.class,mappedBy="c")
  
  @Fetch(FetchMode.SUBSELECT)
  
  @LazyCollection(LazyCollectionOption.EXTRA)
  
  private Set<Order> orders = new HashSet<Order>();
  
  public Customer(http://www.gbpcci.cn ) {
  
  }
  
  public Customer(http://027yeshenghuowang.com/ Integer id, String name) {
  
  super();
  
  this.id = id;
  
  this.name = name;
  
  }
  
  public Set<Order> getOrders() {
  
  return orders;
  
  }
  
  public void setOrders(Set<Order> orders) {
  
  this.orders = orders;
  
  }
  
  public Integer getId() {
  
  return id;
  
  }
  
  public void setId(Integer id) {
  
  this.id = id;
  
  }
  
  public String getName() {
  
  return name;
  
  }
  
  public void setName(String name) {
  
  this.name = name;
  
  }
  
  @Override
  
  public String toString() {
  
  return "Customer [id=" + id + ", name=" + name + "]";
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  然后以Debug的方式运行SetFetchTest单元测试类中的test2方法,就能得出结论:在查询订单时,只会根据情况来确定是否要订单信息,如果不需要,例如我们程序中的size操作,那么就会发出select count(*) from Order where c_customer_id=?这样的语句。这时Eclipse控制台会打印:
  
  Hibernate:
  
  select
  
  customer0_.id as id1_0_,
  
  customer0_.name as name2_0_
  
  from
  
  t_customer customer0_
  
  Hibernate:
  
  select
  
  count(id)
  
  from
  
  t_order
  
  where
  
  c_customer_id =?
  
  10
  
  Hibernate:
  
  select
  
  count(id)
  
  from
  
  t_order
  
  where
  
  c_customer_id =?
  
  10
  
  Hibernate:
  
  select
  
  count(id)
  
  from
  
  t_order
  
  where
  
  c_customer_id =?
  
  0
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  many-to-one或one-to-one上的fetch与lazy
  
  set上的fetch与lazy主要是设置在获取到代表一的一方时,如何去查询代表多的一方。那么在<many-to-one>或<one-to-one>标签上如何设置fetch与lazy,然后去查询对方。对于我们的程序来说,就是在代表多的一方如何查询代表一的一方的信息。例如,获取到一个订单对象,要查询客户信息。
  
  fetch可取值有:
  
  select:默认值,代表发送一条或多条简单的select语句
  
  join:发送一条迫切左外连接
  
  lazy可取值有:
  
  false:不采用延迟加载
  
  proxy:默认值,是否采用延迟不由本方说了算,而是需要由另一方的类级别延迟策略来决定
  
  no-proxy:在此不讨论
  
  第一种组合
  
  首先修改Order类的代码为:
  
  // 订单 ---- 多的一方
  
  @Entity
  
  @Table(name="t_order")
  
  public class Order {

posted @ 2017-04-26 14:50  王二狗的人生  阅读(362)  评论(0编辑  收藏  举报