框架 day33 Hibernate,组件映射,继承映射,抓取(检索)策略-优化,检索方式总结

来源:互联网 发布:淘宝客服绩效是什么 编辑:程序博客网 时间:2024/04/28 01:45

组件(组成)映射


例1:

public class Person {private Integer pid;//OID 值private String name;//第一种方案private String homeAddr;private String homeTel;private String companyAddr;private String companyTel;

↑一般项目都都采用此方案()


*通过面向对象角度,使用设计模式(组件|组合),将数据都抽取到一个对象中。将多个对象组合在一起,达到重复利用目的。

例2:抽取数据(地址)到一个对象中, 这个值对象既为组件,组件不支持共享引用(没有OID)

public class Address {//值对象 (vo 值对象/po 持久对象/bo 业务对象)private String addr;private String tel;


public class Person {private Integer pid;//OID 值private String name;//第二种方案:组合private Address homeAddress;private Address companyAddress;

组件映射配置

<hibernate-mapping><class name="com.itheima.b_component.Person" table="t_person"><id name="pid"><generator class="native"></generator></id><property name="name"></property><component name="homeAddress" class="com.itheima.b_component.Address"><property name="addr" column="homeAddr"></property><property name="tel" column="homeTel"></property></component><component name="companyAddress" class="com.itheima.b_component.Address"><property name="addr" column="companyAddr"></property><property name="tel" column="companyTel"></property></component></class></hibernate-mapping>

注意:必须确定组合javabean类型  <component class="">

每一个对象属性必须在表中都存在独有列名。<property column="...">



继承映射



应用场景:
公司的员工,保存:小时工和正式员工

员工类

public class Employee {private Integer eid;private String name;
小时工

public class HourEmployee extends Employee {private int rate;//小时薪水
正式员工

public class SalaryEmployee extends Employee {private int salary; //正式薪金

简单方式

public class Employee {private Integer eid;private String name;private String temp;//标志 1;小时工2;正式工private int money;  //金额


继承方式1:<sub-class>


*所有内容保存一张表,给表提供表示字段,每一个子类具有独有字段



缺点:存在null值。


配置



继承方式2:<joined-subclass> (掌握)



*将提供三种表,主表存放基本信息,两个从表存放独有字段内容。



表之间存在一对一关系




一对一:
方式1:主表的主键,与从表的外键(唯一),形成主外键关系
方式2:主表的主键,与从表都主键,形成主外键关系



继承方式3:<union-class>


*将生成3张表,彼此之间没有关系,但主键值逐渐递增,需要hibernate自动维护三张表之间的主键唯一


*缺点: 主键生成策略 increment,不能通过数据库自动生成。存在并发问题
每一次都执行select语句,从三张表中查询oid的最大值。


配置




抓取(检索)策略--优化



*开发中,使用都是默认值,最后进行优化,根据业务要求进行相应设置



3.1检索方式



*立即检索:在执行查询方法之后,立即执行select语句,进行查询。


//class 标签中lazy=false 立即检索public void loadCustomerfalse(){Session session=sessionFacoty.openSession();Transaction tx=session.beginTransaction(); //该行代码让hibernate执行select语句,查询数据库Customer c=(Customer)session.load(Customer.class, 1);c.getId();c.getAge();tx.commit();session.close();  }



*延迟检索:在执行查询方法之后,没有进行查询,底层生成代理对象,直到需要相应的数据,再进行查询。


//class 标签中lazy=true 延迟检索public void loadCustomertrue(){       Session session=sessionFacoty.openSession();       Transaction tx=session.beginTransaction();        Customer c=(Customer)session.load(Customer.class, 1);       //该行代码让hibernate执行select语句,       //查询数据库(需要用的时候查数据库)      c.getId();      c.getAge();       tx.commit();      session.close();  }


-相应的数据:除OID之外的值,关联数据。
-延迟加载在开发中,主要好处,延缓数据加载,在使用时才进行加载 (缩短数据在内存中时间)

-理解延迟检索中的代理





3.2检索类型



*类级别检索:当前对象所有属性值。例如:Customer自己数据


  Customer c=(Customer)session.load(Customer.class, 1);


  session的方法直接检索Customer对象,对Customer对象到底采用立即检索 还是延迟检索方式
  通过class元素的lazy属性设定


*关联级别检索:当前对象关联对象数据。例如:Customer 关联 Order 数据


     Customer c=(Customer)session.load(Customer.class, 1);

     Set set=c.getOrders()//检索Order对象的set集合


     在这个例子中
     session.load(Customer.class, 1):查询的主体表

     c.getOrders().size():查询客体表的集合大小


     查询客体表是否发生,以何种方式发生(立即检索、延迟检索和迫切左外连接检索),就是关联级别检索

      通过set元素lazy属性设定


3.3类级别检索策略



*类级别:查询当前类的所有内容,只查询一次。优化指的是查询时机优化,让空闲时间服务器做出其他处理。
*session.get(Customer.class ,oid) 通过OID立即检索(查询),如果数据不存在返回null。
*session.load(Customer.class , oid ) 默认通过OID延迟检索,如果数据不存在将抛异常。
         类级别配置,只对load方法有效。
         Customer.hbm.xml <class name="" table=""lazy="true|false">
                   true:默认值,延迟检索
                   false:立即检索。

*无论 <class>元素的 lazy 属性是 true 还是 false,
-Session 的 get() 方法Query 的 list() 方法在类级别总是使用立即检索策略


*若 <class> 元素的 lazy 属性为 true 或取默认值, 
-Session 的 load() 方法不会执行查询数据表的 SELECT 语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:
         >由 Hibernate 在运行时采用 javassist 工具动态生成
         >Hibernate创建代理类实例时, 仅初始化其 OID 属性
         >在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例

*应用:
-如果程序加载一个对象的目的是为了访问它的属性, 可以采取立即检索. 
-如果程序加载一个持久化对象的目的是仅仅为了获得它的引用, 可以采用延迟检索


 

3.4一对多和多对多关联的检索策略

*共同特点都有集合<set> 通过集合进行配置。
*通过 fetch 和 lazy 属性 进行配置

*lazy: (确定什么时候执行sql语句。) 
-主要决定 orders 集合被初始化的时机.
-即到底是在加载 Customer 对象时就被初始化, 
-还是在程序访问 orders 集合时被初始化


*fetch:(确定hibernate 生成 sql样子 )
-取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式;  
-若取值为”join”, 则决定 orders 集合被初始化的时机
-若把 fetch 设置为 “join”, lazy 属性将被忽略

 

*对比
         fetch="join",lazy无效,hibernate 将使用“迫切左外连接”,底层执行sql语句就是“左外连接”
                   只执行一条select,将当前对象及关联对象数据一次性查询出来。
         fetch="select",默认值,执行多条select语句
                   lazy="false"立即执行,在执行get方法时,立即执行多条select语句。
                   lazy="true"延迟执行,在执行get方法时先查询客户Customer,直到使用Order数据时才查询Order
                   lazy="extra"极其懒惰,在执行get方法时先查询客户Customer(select t_customer),
                                     如果需要order 订单数,将执行聚合函数只查询数量(select count()t_order),
                                     如果需要详情再查询详情(select t_order))。
                   fetch="subselect",采用子查询
                   lazy取值与 fetch="select"相同。
                   注意:必须使用Query进行查询。

 



↑测试(子查询使用query测试)

*延迟检索和增强延迟检索


-在延迟检索(lazy 属性值为 true) 集合属性时, Hibernate 在以下情况下初始化集合代理类实例 

         >应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains() 等方法

         >通过 Hibernate.initialize() 静态方法显式初始化


-增强延迟检索(lazy 属性为 extra): 与 lazy=“true” 类似.
主要区别是增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机:

         >当程序第一次访问 orders 属性的 iterator() 方法时, 会导致 orders 集合代理类实例的初始化

         >当程序第一次访问 order 属性的 size(), contains() 和 isEmpty() 方法时, Hibernate 不会初始化 orders 集合类的实例, 仅通过特定的     select 语句查询必要的信息, 不会检索所有的 Order 对象

 

*用带子查询的 select 语句整批量初始化 orders 集合(fetch属性为“subselect”)
<set> 元素的 fetch 属性: 取值为“select”或“subselect”时, 决定初始化 orders 的查询语句的形式;


--若取值为”join”, 则决定 orders 集合被初始化的时机.默认值为 select 
--当 fetch 属性为 “subselect” 时
         >假定 Session 缓存中有 n 个 orders 集合代理类实例没有被初始化,
          Hibernate 能够通过带子查询的 select 语句, 来批量初始化 n 个 orders 集合代理类实例

 

*迫切左外连接检索(fetch 属性值设为 “join”)

-当 fetch 属性为 “join” 时:
>检索 Customer 对象时, 会采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的 Order 对象
>lazy 属性将被忽略
>Query 的list() 方法(因为hql是自己指定的)会忽略映射文件中配置的迫切左外连接检索策略(fetch=join无效), 而依旧采用立即检索还是延迟加载策略由set集合的lazy属性决定(这时候lazy重新启动应用)

 



3.5多对一和一对一关联的检索策略



*共同特点拥有一方(<many-to-one> /<one-to-one>)

-和 <set> 一样,<many-to-one> 元素也有一个lazy 属性和 fetch 属性.

    •若fetch 属性设为join, 那么lazy 属性被忽略

    •迫切左外连接检索策略的优点在于比立即检索策略使用的 SELECT 语句更少.

    •无代理延迟检索需要增强持久化类的字节码才能实现

 *Query 的list() 方法(因为hql是自己指定的)会忽略映射文件中配置的迫切左外连接检索策略(fetch=join无效), 而依旧采用立即检索还是延迟加载策略由set集合的lazy属性决定(这时候lazy重新启动应用)

*如果在关联级别使用了延迟加载或立即加载检索策略, 可以设定批量检索的大小,以帮助提高延迟检索或立即检索的运行性能



\

fetch="join" , lazy 无效,hibernate使用“迫切左外连接”,一次性查询订单和关联客户
                  
fetch="select" 查询多条select语句
         lazy="false"立即 ,执行两条select语句
         lazy="proxy"代理,order关联客户Customer,客户是否延迟,取决客户类级别检查策略。
                   Customer.hbm.xml<class lazy="false"> 立即客户
                   Customer.hbm.xml<class lazy="true"> 延迟客户
         lazy="no-proxy"不研究

--------------------------------------------------------------------

批量检索


问题:什么叫n+1问题?
有五个客户,要查询出它的订单,我们正常执行会产生6条sql语句。这就是n+1问题.
解决方案:使用in来解决。

在hibernate中可以合适batch-size(批量检索)来解决。


*批量检索  从一的一端查询  查询所有的客户


-<set> 元素有一个 batch-size 属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量. 
   批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能.  默认值是1
注:query.list()属于hql检索,hql检索忽略关联级别的迫切左外连接检索,只与lazy属性有关.

配置

 



*批量检索  从多的一端查询  查询所有的订单
-在Customer.hbm.xml文件中增加batch-size属性



*比较三种检索策略





检索方式总结


1、检索方式

1.使用OID获取

       >session.get(User.class,1);立即加载如果不存在,就返回null

       >session.load(User.class,2); 延迟加载 如果不存在,就抛异常

PS:使用以上两个方法进行查询,结果都为持久态对象,持久对象就在一级缓存中。

2.导航对象图检索方式

>customer.getOrderSet().size()

3.使用HQL

       >session.createQuery("FROMEmployee e WHERE e.id>? ORDER BY e.id DESC").list();

4.原生的SQL方式

       >session.createSQLQuery(Stringsql);

5.QBC 检索方式: 使用 QBC(QueryBy Criteria) API 来检索对象.

Query By Criteria提供 纯面向对象查询语句


2、 HQL 是 Hibernate最常用检索方式

 

*与SQL语法基本一致,不同的是HQL是面象对象的查询,查询的是对象和对象中的属性

*HQL的关键字不区分大小写,但类名和属性名区分大小写



支持 所有 SQL支持检索方式

 

步骤 :

   1) 获得Session

   2) 编写HQL

   3) 通过 session.createQuery(hql) 创建Query对象

   4) 为Query对象 设置条件参数

   5) 执行查询 list() ---- 返回一个集合列表 、 uniqueResult();--- 返回一个查询结果  

 

* Qurey 接口支持方法链编程风格  ,将上面所有步骤写入一句程序代码中

 

3、 编写测试用例,创建初始数据

创建4个Customer , 每个Customer 创建 10个 Order

 

4、 简单查询, hibernate 企业开发主流查询 HQL 和 QBC

 * 查询所有数据

              //HQL              String hql = "from Customer";              Queryquery = session.createQuery(hql);              List<Customer>list = query.list();              System.out.println(list);               //QBC              Criteriacriteria = session.createCriteria(Customer.class);              List<Customer>list2 = criteria.list();              System.out.println(list2);

  *HQL 和 QBC 都支持链式编程写法

            List<Customer>list3 = session.createQuery("from Customer").list();


5、 本地SQL 检索

       *编写及其复杂的查询,企业内部大多使用 SQL语句

//内连接 写法一 : select *from A inner join B on A.id = B.A_id;//内连接 写法二 (隐式): select* from A,B where A.id = B.A_id ;String sql = "select * from customers ,orders where customers.id =orders.customer_id and customers.name = ?";SQLQuerysqlQuery = session.createSQLQuery(sql);//设置参数sqlQuery.setParameter(0,"mary");Listlist = sqlQuery.list();System.out.println(list);

       *当返回很多列时,默认将每条记录保存在 Object[]中, 返回 List<Object[]>

       *将返回结果 与实体类 绑定,将每条数据 转换实体类对象

String sql = "select orders.* from customers ,orders where customers.id = orders.customer_id and customers.name = ?";sqlQuery.addEntity(Order.class);


6、 编写HQL时,通过as关键字 为类 起别名

  from Customer as c where c.name=:custname             

   原来写法: from Customer wherename=:custname

   使用别名时 ,as可以省略 fromCustomer c where c.name=:custname

 

* 别名主要使用关联复杂查询时

 

7、 多态查询

   hibernate 检索一个类 对应数据时, 将类所有子类(PO类) 对应数据表记录返回

 session.createQuery("fromjava.lang.Object").list();

 * 将Object 所有子类 对应数据表的 数据查询返回

 

from 关键字后面,如果PO类,省略包名, 如果不是PO类,必须写完整包名类名

 

8、 查询结果排序

//HQLString hql = "from Customer order by name asc";Listlist = session.createQuery(hql).list();System.out.println(list); //QBCListlist2 =session.createCriteria(Customer.class).addOrder(org.hibernate.criterion.Order.asc("name")).list();System.out.println(list2);

9、 分页查询

Query 接口和 Criteria 接口 都提供 setFirstResult 、setMaxResults 两个方法,用于分页查询

       *  setFirstResult 起始记录索引,第一条数据 索引 0

       *  setMaxResults 查询返回记录条数

       案例:

              //分页查询,返回 25-34条订单              //HQL              String hql = "from Order";              Queryquery = session.createQuery(hql);              //设置分页参数              query.setFirstResult(24);// 索引 是 起始记录 -1              query.setMaxResults(10);   
  

10、 检索单一对象  query.uniqueResult() 、 criteria.uniqueResult()

   * 该方法主要用于,只有1条数据结果

   * 什么情况只有一条结果 : 用户登录、使用聚集函数 sum、count、avg、max、min

//查询mary的信息Customercustomer = (Customer) session.createQuery("from Customer where name ='mary'").uniqueResult();System.out.println(customer); //使用聚集函数 -- 查询客户最大年龄Integerage = (Integer) session.createQuery("select max(id) fromCustomer").uniqueResult();System.out.println(age);  
           

如果查询结果 只有一条记录或者 无记录,使用uniqueResult 是没问题的, 但是如果查询结果大于 一条记录,报错

org.hibernate.NonUniqueResultException:query did not return a unique result: 4

 

===============================================================================================================================

11、 带有参数条件的查询 (重点)

1) 单表条件查询

HQL写法:

Customer customer1 = (Customer)session.createQuery("from Customer where name = ?").setParameter(0,"tom").uniqueResult();Customer customer2 = (Customer)session.createQuery("from Customer where name =:cname").setParameter("cname", "tom").uniqueResult();

QBC写法:

Customer customer3 = (Customer)session.createCriteria(Customer.class).add(Restrictions.eq("name","tom")).uniqueResult();

       *Restrictions 用来添加查询条件 ,面向对象条件查询

 

将参数绑定到一个持久化对象

Customer customer = new Customer();customer.setId(1);Listlist2 = session.createQuery("from Order where customer =?").setEntity(0, customer).list(); // 通过customer_id 查询

* 简化为

List list = session.createQuery("from Order where customer.id =?").setParameter(0, 1).list(); 

* setEntity 关联对象 ,必须要有OID ,否则会报错

 

使用QBC 为参数绑定 持久化对象

Listlist3 =session.createCriteria(Order.class).add(Restrictions.eq("customer",customer)).list(); // 通过customer_id 查询

2) 多表条件查询


hibernate  HQL 支持7种 连接写法

       *(SQL标准)内连接 inner join  可以省略 inner,直接 join

       *迫切内连接 inner joinfetch  ------ 不是SQL写法,是hibernate 提供

       *隐式内连接 不写任何关键字,完成表关联

      

       *(SQL标准)左外连接 left outer join ,可以省略 outer ,直接 left join

       *迫切左外连接 left outerjoin fetch ----- 不是SQL语法

       *(SQL标准)右外连接 right outer join

      

       *(SQL标准)交叉连接 (笛卡尔积 )

 

问题: 区分内连接和迫切内连接,左外连接和迫切左外连接

       *左外连接

List list =session.createQuery( "from Customer c left outer joinc.orders").list(); 
返回 List<Object[]>

       每个数组两个元素 ,一个Customer 一个Order

       *迫切左外连接  

List list =session.createQuery("select distinct c from Customer c left outer joinfetch c.orders").list();

       返回 List<Customer> 保存所有Customer对象,需要distinct 排重重复

 

问题:多表关联条件查询,隐式内连接 和 QBC方式  

//隐式内连接 o.customer 当做Customer类数据表List<Order>list = session.createQuery("from Order o where o.customer.name =?").setParameter(0, "mary").list(); //QBC 连接查询,必须使用 criteria.createAlias() Criteriacriteria = session.createCriteria(Order.class);criteria.createAlias("customer","c"); // 为订单关联Customer属性起了别名criteria.add(Restrictions.eq("c.name","mary"));Listlist2 = criteria.list();

       *  在 createAlias 默认使用 inner join 内连接

              criteria.createAlias("customer","c", Criteria.LEFT_JOIN); 在关联时使用左外连接

      

12 、投影查询

       查询结果仅包含实体的部分属性

       *  查询Customer的所有 name,age 属性

HQL方式

       

session.createQuery("select name,age from Customer"); 
返回 List<Object[]>

       *将结果保存Customer对象中,提供name和age 构造方法

       session.createQuery("select new Customer(name,age) from Customer"); 
返回 List<Customer>

可以将查询结果 保存List 或者 Map集合     

       *select new list(name,age) from Customer

       *select new map(name,age) from Customer

      

QBC方式 (开发中不用,非常麻烦)

       Listlist3 = session                            .createCriteria(Customer.class)                            .setProjection(                                          Projections.projectionList()                                                        .add(Property.forName("name"))                                                        .add(Property.forName("age"))).list(); 

13、 分组统计查询

       count  sum avg max min

       *Long count = (Long) session.createQuery("select count(*) fromOrder").uniqueResult();       *List list2 = session.createQuery("select count(*) from Order group bycustomer").list(); 

14、 命名查询语句

   在开发中 hql语句 写到代码中,不方便维护, 将HQL定义到配置文件中 ------------ 命名查询语句

 

在hbm映射文件 (也可以用注解配置)

       <!--这里可以定义命名查询 -->       <!--       定义 HQL 语句 <query name=""></query> -->       <!--       定义 SQL 语句 <sql-queryname=""></sql-query> -->       <query name="findCustomerByName">              <![CDATA[from Customer where name = ?]]>       </query>

* 为hql语句 起了一个名字

 

程序代码

       Query query = session.getNamedQuery("findCustomerByName");       query.setParameter(0,"tom");       Customer customer = (Customer) query.uniqueResult();


15、 离线Criteria对象 --- DetachedCriteria

       *主要用于javaee分层开发,可以在web层封装查询条件,传递数据层 关联Session进行查询

       DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);       detachedCriteria.add(Restrictions.eq("name","kitty"));        //传递数据层       Session session = HibernateUtils.openSession();       Transaction transaction = session.beginTransaction();        //将离线查询对象 关联到Session       Criteria criteria = detachedCriteria.getExecutableCriteria(session);       Customer customer = (Customer) criteria.uniqueResult();


HQL和QBC比较: 两种查询功能类似, 简单查询建议编写HQL,对于复杂查询,多条件组合查询 建议编写 QBC方式

 

============================================================================================================================

 




0 0
原创粉丝点击