Hibernate高级
Hibernate的查询方式
OID查询
根据主键进行检索
类名 对象 = session.get(类名.class,主键(Long));
类名 对象 = session.load(类名.class,主键(Long));
对象导航检索
Hibernate根据一个已经查询到的对象,获取其关联的对象的一种查询方式
LinkMan linkMan = session.get(LinkMan.class,1L);
Customer customer = linMan.getCustomer();
HQL单表查询(重点)
Hibernate的查询语言,是一种面向对象的方式的查询语言,语法类似SQL。通过session.createQuery(),用于接收一个HQL进行查询方式
HQL的简单查询
格式: from 类名(可以全名,也可以不全名,只要映射配置好) 别名
Query query = session.createQuery("from Customer c");
List<Customer> list = query.list();
HQL的排序查询
默认升序
格式: from 类名 order by 属性名 排序规则
Query query = session.createQuery("from Customer order by cust_id");
List<Customer> list = query.list();
HQL的条件查询
- 按位置绑定参数(从0开始)
Query query = session.createQuery("from Customer where cust_name = ? "); //从0开始 query.setParameter(0,"1张三"); List<Customer> list = query.list();
- 按名称绑定参数
Query query = session.createQuery("from Customer where cust_name = :aaa "); query.setParameter("aaa","1张三");
HQL的投影查询
-
查询对象的某个属性
List<Object> list = session.createQuery("select c.cust_name from Customer c").list(); for (Object obj:list){ System.out.println(obj); }
-
查询某些属性
List<Object[]> list = session.createQuery("select c.cust_name,c.cust_source from Customer c").list(); for (Object[] objs:list){ System.out.println(Arrays.toString(objs)); }
-
将查询的某些属性装入对象中
前提:需要在对象中为需要查询的属性添加构造方法,注意别把无参构造弄没了
List<Customer> list = session.createQuery("select new Customer(cust_name,cust_source) from Customer").list(); for (Customer customer:list){ System.out.println(customer); }
HQL的分组统计查询
List<Object[]> list = session.createQuery("select cust_source,count(*) from Customer group by cust_source having count(*) >= 2").list();
for (Object[] obj:list){
System.out.println(Arrays.toString(obj));
}
HQL的分页查询
Query query = session.createQuery("from LinkMan");
//从0开始
query.setFirstResult(0);
query.setMaxResults(10);
List<LinkMan> list = query.list();
for (LinkMan linkMan:list){
System.out.println(linkMan);
}
HQL的聚合函数查询
sql语句的函数在HQL中都可以使用
//uniqueResult获取唯一结果
Object object = session.createQuery("select count(*) from Customer").uniqueResult();
System.out.println(object);
HQL的多表查询
内连接
原sql语句:
显示内连接:
select * from cst_customer c inner join cst_linkman l on c.cust_id = l.lkm_cust_id;
HQL的内连接:
List<Object[]> list = session.createQuery("from Customer c inner join c.linkMans").list();
for (Object[] obj:list){
System.out.println(Arrays.toString(obj));
}
HQL的迫切内连接
HQL的普通内连接查询接收到的对象是一个数组,我们可以使用迫切内连接将查询对象封装到对象中去
List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMans").list();
for (Customer customer:list){
System.out.println(customer);
}
QBC单表查询(重点)
条件查询,是一种更加面向对象化的查询方式
简单查询
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer);
}
分页查询
Criteria criteria = session.createCriteria(LinkMan.class);
criteria.setFirstResult(0);
criteria.setMaxResults(10);
List<LinkMan> list = criteria.list();
for (LinkMan linkMan:list){
System.out.println(linkMan);
}
排序查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(Order.desc("cust_id"));
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer);
}
条件查询
Criteria criteria = session.createCriteria(Customer.class);
/**
* = eq
* > gt
* >= ge
* < lt
* <= le
* <> ne
* like
* in
* and(默认是并列的)
* or
*/
criteria.add(Restrictions.eq("cust_source","江苏"));
criteria.add(Restrictions.or(Restrictions.like("cust_name","1张%")));
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer);
}
分组统计查询
Criteria criteria = session.createCriteria(Customer.class);
//count(*)
criteria.setProjection(Projections.rowCount());
Object o = criteria.uniqueResult();
System.out.println(o);
离线条件查询(重点)
适用于sql条件比较多的项目,离线条件查询一般写在web层,拼接条件,然后传给service,调用dao,省去dao需要拼接许多sql的麻烦
//这下面两行代码写在web层,然后将DetachedCriteria对象传给service,调用dao
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.like("cust_name","1张%"));
//Dao代码
Session session = HibernateUtil.getSession();
Transaction transaction = session.beginTransaction();
//绑定session
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer);
}
SQL查询
通过使用基本sql语句查询
SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = sqlQuery.list();
for (Object[] obj:list){
System.out.println(Arrays.toString(obj));
}
将查询结果封装到对象中
SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
sqlQuery.addEntity(Customer.class);
List<Customer> list = sqlQuery.list();
for (Customer customer:list){
System.out.println(customer);
}
Hibernate的抓取策略(优化)
延迟加载
延迟加载(懒加载):执行该行代码时,不会发送语句去查询,在真正使用这个对象的属性时才会去发送sql语句进行查询
延迟加载的分类
-
类级别的延迟加载
指的是通过load方法查询某个对象多的时候,是否采用延迟
可以在需要查询的对象的映射配置的class标签上配置lazy属性,默认值为true,如果为false,则load方法的效果会与get方法一样<class name="com.hibernate.pojo.Customer" table="cst_customer" lazy="false">
-
关联级别的延迟加载(重点:抓取策略往往会和关联级别的延迟加载一起使用,优化sql语句)
指的是在查询到某个对象的时候,查询其关联的对象的时候,是否采用了延迟加载
例如:Customer customer = session.get(Customer.class, 1L); customer.getLinkMans();//通过客户获取关联对象联系人,联系人对象是否采用了延迟加载,称为关联级别的延迟
抓取策略
概述:通过一个对象抓取到关联对象需要发送的sql语句,sql语句如何发送,发成什么样的格式通过策略进行配置,可以通过set或者many to one 标签上的fetch属性和lazy属性进行设置
set 标签上的fetch和lazy
- fetch:抓取策略,控制SQL语句格式
- select:(默认值) 发送普通的select语句,查询关联对象
- join:发送一条迫切左外连接查询关联对象
- subselect:发送一条子查询查询其关联对象
- lazy:延迟加载,控制关联对象的时候是否采用延迟
- true:(默认值) 查询关联对象的时候,延迟加载
- false:查询关联对象的时候,不采用延迟加载
- extra:极其懒惰
-
默认设置fetch=“select” lazy=“true”
//查询1号客户 Customer customer = session.get(Customer.class, 1L);//发送一条根据客户id查询客户信息的sql,不关联查询联系人 //查询1号客户每个联系人的信息 for (LinkMan linkMan:customer.getLinkMans()){//发送一条根据客户id查询联系人的sql System.out.println(linkMan.getLkm_name()); }
当客户需要使用关联对象的时候,他才会去查询关联对象
-
fetch=“select” lazy=“false”
//查询1号客户 Customer customer = session.get(Customer.class, 1L);//发送两条sql语句根据客户id查询客户信息的sql,根据客户id查询联系人 //查询1号客户每个联系人的信息 for (LinkMan linkMan:customer.getLinkMans()){ System.out.println(linkMan.getLkm_name()); }
即便不使用,他也会查询
-
fetch=“select” lazy=“extra”
//查询1号客户 Customer customer = session.get(Customer.class, 1L);//发送一条根据客户id查询客户信息的sql System.out.println(customer.getLinkMans().size());//发送一条根据客户id查询联系人数目的sql
如果用不到对象中的属性,都不会发送查询信息的sql语句
-
fetch=“join”
//查询1号客户 Customer customer = session.get(Customer.class, 1L);//发送一条迫切左外连接的sql语句 //查询1号客户每个联系人的信息 for (LinkMan linkMan:customer.getLinkMans()){ System.out.println(linkMan.getLkm_name()); } System.out.println(customer.getLinkMans().size());
如果fetch设置为join,他会发送一条迫切左外连接将客户和对应的联系人都查出来,这个时候不会发送第二条sql语句,所以lazy失效
-
fetch=“subselect” lazy=“true”
List<Customer> list = session.createQuery("from Customer").list();//查询所有客户的sql for (Customer customer:list){ System.out.println(customer.getCust_name()); System.out.println(customer.getLinkMans().size());//发送一条子查询查询联系人信息 }
当客户需要使用关联对象的时候,他会使用子查询去查询关联对象的信息
many-to-one 标签上的fetch和lazy
- fetch:抓取策略,控制SQL语句格式
- select:(默认值) 发送普通的select语句,查询关联对象
- join:发送一条迫切左外连接查询关联对象
- lazy:延迟加载,控制关联对象的时候是否采用延迟
- proxy:(默认值) 取决于一的一端class上的lazy属性的值
- false:查询关联对象的时候,不采用延迟加载
批量抓取
问题:
List<Customer> list = session.createQuery("from Customer").list();
for (Customer customer:list){
System.out.println(customer.getCust_name());
for (LinkMan linkMan:customer.getLinkMans()){
System.out.println(linkMan.getLkm_name());
}
}
上面的代码运行后,首先会查询customer客户,然后在根据客户的id去一条一条的发送sql查询联系人,有多少客户就会发送多少sql,效率非常低
优化:
在set 标签上配置属性batch-size,一次查询多少条,就可以减少与数据库的交互
<set name="linkMans" cascade="delete,save-update" batch-size="5">
<key column="lkm_cust_id"></key>
<one-to-many class="com.hibernate.pojo.LinkMan"></one-to-many>
</set>
联系人查询客户的时候也会这样,有多少联系人就会发送多少sql,可以在客户映射配置的class 标签上配置属性batch-size