1.Hibernate的查询方式
2.Hibernate的抓取策略
Hibernate查询方式
-
OID检索:根据ID查询对象,Get/Load方法
session.get(Customer.class,1L) session.load(Customer.class, 2L)
-
对象导航检索:根据已查询对象,获得其关联对象,例如用户和角色
Customer cus = session.get(Customer.class,1L) LinkMan lkm = cus.getLinkMan()
-
HQL检索:使用HQL语句,进行查询;Hibernate Query Language,面向对象的查询
-
简单查询
Query query = session.createQuery("from Customer"); //查询的是类,不是表 List<Customer> list = query.list(); //list 链式显示获取 for (Customer customer : list) { System.out.println(customer); } //支持别名,不支持select * session.createQuery("select c from Customer c")
-
排序查询:支持Order by 属性名,默认升序
List<Customer> list = session.createQuery("from Customer order by cust_id desc").list();
-
条件查询:1.按位置绑定; 2.按类型绑定
//1.按位置绑定 Query query = session.createQuery("from Customer where cust_id = ? and cust_name like ?"); query.setParameter(0, 1L); query.setParameter(1, "c%"); List<Customer> list = query.list(); //2.按名称绑定 //冒号后必须紧跟名字,名字可以任意取定 Query query2 = session.createQuery("from Customer where cust_id = :id and cust_name like :name"); query2.setParameter("id", 2L); query2.setParameter("name", "c%"); List<Customer> list2 = query2.list();
-
投影查询:查询某些列或某些字段的行
//单个属性,返回的Object Query query = session.createQuery("select c.cust_name from Customer c"); List<Object> list = query.list(); for (Object object : list) { System.out.println(object); } //多个列,Object数组 Query query2 = session.createQuery("select c.cust_id, c.cust_name from Customer c"); List<Object[]> list2 = query2.list(); for (Object[] objects : list2) { System.out.println(Arrays.toString(objects)); } //多列,封装成对象 Query query3 = session.createQuery("select new Customer(c.cust_id, c.cust_name) from Customer c"); List<Customer> list3 = query3.list();
-
分页查询
Query query = session.createQuery("from LinkMan"); query.setFirstResult(10); //limit ?,? query.setMaxResults(10);
-
分组统计查询
//count,max,min,avg等 Query query = session.createQuery("select min(cust_id) from Customer"); Object result = query.uniqueResult(); System.out.println(result); //也可以使用list,输出第一个元素 //分组-----注意:lkm_cust_id不是LinkMan的属性,所以选择不了 Query query2 = session.createQuery("select customer, count(*) from LinkMan group by lkm_cust_id having count(*) > 1"); List<Object[]> list = query2.list(); for (Object[] objects : list) { System.out.println(Arrays.toString(objects)); }
-
-
QBC检索:query by criteria,适合多条件查询
-
简单查询
//使用criteria Criteria criteria = session.createCriteria(Customer.class); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } //使用criteria ,默认升序 Criteria criteria = session.createCriteria(Customer.class); criteria.addOrder(Order.desc("cust_id")); //Order为Hibernate对象 List<Customer> list = criteria.list();
-
条件查询:add,添加普通条件;addOrder,添加顺序;setProjection,添加聚合函数等
//条件查询 Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //条件默认是与,可以改成or Criteria criteria = session.createCriteria(Customer.class); // eq = ; lt <; gt >; like,and,or,in,between // criteria.add(Restrictions.eq("cust_name", "cus3")); // criteria.add(Restrictions.le("cust_id", 2L)); //or,两个条件 //criteria.add(Restrictions.or(Restrictions.eq("cust_name", "cus3"), Restrictions.le("cust_id", 2L))); //or多个条件----可以使用between或者in Disjunction disjunction = Restrictions.disjunction(); disjunction.add(Restrictions.eq("cust_name", "cus3")); disjunction.add(Restrictions.eq("cust_name", "cus1")); disjunction.add(Restrictions.eq("cust_name", "cus1")); criteria.add(disjunction); List<Customer> list = criteria.list();
-
分页查询
Criteria criteria = session.createCriteria(Customer.class); criteria.setFirstResult(1); criteria.setMaxResults(1); List<Customer> list = criteria.list();
-
分组统计查询
Criteria criteria = session.createCriteria(Customer.class); criteria.setProjection(Projections.rowCount()); //Projections.max,min,avg等,还有having,group by等 Long num = (Long) criteria.uniqueResult(); System.out.println(num);
-
-
QBC离线检索: 适用于多表、多条件和分页的查询情况,一般在SSH模型中使用的到
离线:DetachedCriteria,脱离session使用;方便在条件查询时,减轻了JDBC中的参数查询语句的匹配重复性问题。可以用来在Web层直接操作,不需要在DAO中判断
//Web层的操作 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.like("cust_name", "cus%")); //到Service绑定session Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //执行 Criteria criteria = dc.getExecutableCriteria(session); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); }
-
HQL的多表查询:QBC也可以,支持功能一般
a) SQL的多表查询方式:连接查询和子查询
-
连接查询:
-
子查询:多表嵌套查询,相关子查询,非相关子查询
b) HQL的多表查询:
-
连接查询
- 交叉连接
- 内连接:显式、隐式、迫切
- 外链接:左外、右外、迫切左外
-
普通内连接和迫切内连接只有一个关键字fetch不同,其发送的SQL语句和查询的结果集都是一样的,fetch是Hibernate的内容,主要用来将返回结果自动封装成对象;在普通内连接中,会将连接查询的每一条结果分成两个对象来封装(下面示例代码中,能看见普通的内连接会查询两个表),而迫切内连接会将相同的封装到一个对象中。
普通查询: Transaction transaction = session.beginTransaction(); //SQL的内连接:SELECT * from cst_customer inner join cst_linkman on cst_customer.cust_id = cst_linkman.lkm_cust_id; //HQL只需关联类中对于外键的集合即可 //查询结果会匹配到对应的集合中,若去重加上distinct即可 Query query = session.createQuery("from Customer c inner join c.linkMans"); List<Object[]> list = query.list(); for (Object[] objects : list) { System.out.println(Arrays.toString(objects)); }
在console中能看见:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, linkmans1_.lkm_id as lkm_id1_1_1_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_mobile as cust_mob7_0_0_, linkmans1_.lkm_name as lkm_name2_1_1_, linkmans1_.lkm_gender as lkm_gend3_1_1_, linkmans1_.lkm_phone as lkm_phon4_1_1_, linkmans1_.lkm_mobile as lkm_mobi5_1_1_, linkmans1_.lkm_email as lkm_emai6_1_1_, linkmans1_.lkm_qq as lkm_qq7_1_1_, linkmans1_.lkm_position as lkm_posi8_1_1_, linkmans1_.lkm_memo as lkm_memo9_1_1_, linkmans1_.lkm_cust_id as lkm_cus10_1_1_ from cst_customer customer0_ inner join cst_linkman linkmans1_ on customer0_.cust_id=linkmans1_.lkm_cust_id [Customer [cust_id=1, ...] [Customer [cust_id=1, ..., linkMans=[LinkMan [...l] ... //30 行
-
若使用迫切的内连接查询,则会返回直接封装好的结果
//迫切内连接 Query query = session.createQuery("from Customer c inner join fetch c.linkMans"); List<Customer> list = query.list(); //能直接获取到Customer的对象 for (Customer customer : list) { System.out.println(customer); }
我们在console能看到的信息(若想要看到属性集合,在tostring函数中添加,否则不会出现linkman)
Hibernate: select customer0_.cust_id as cust_id1_0_0_, linkmans1_.lkm_id as lkm_id1_1_1_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_mobile as cust_mob7_0_0_, linkmans1_.lkm_name as lkm_name2_1_1_, linkmans1_.lkm_gender as lkm_gend3_1_1_, linkmans1_.lkm_phone as lkm_phon4_1_1_, linkmans1_.lkm_mobile as lkm_mobi5_1_1_, linkmans1_.lkm_email as lkm_emai6_1_1_, linkmans1_.lkm_qq as lkm_qq7_1_1_, linkmans1_.lkm_position as lkm_posi8_1_1_, linkmans1_.lkm_memo as lkm_memo9_1_1_, linkmans1_.lkm_cust_id as lkm_cus10_1_1_, linkmans1_.lkm_cust_id as lkm_cus10_1_0__, linkmans1_.lkm_id as lkm_id1_1_0__ from cst_customer customer0_ inner join cst_linkman linkmans1_ on customer0_.cust_id=linkmans1_.lkm_cust_id Customer [cust_id=1, ..., linkMans=[LinkMan [...]] Customer [cust_id=1, ..., linkMans=[LinkMan [...]] ... //30行全部的查询结果 //使用distinct的结果,只有三行
-
-
SQL查询
//SQL查询 SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer"); List<Object[]> list = sqlQuery.list(); // for (Object[] objects : list) { // System.out.println(Arrays.toString(objects)); // } //绑定到类---封装 sqlQuery.addEntity(Customer.class); List<Customer> list2 = sqlQuery.list(); for (Customer customer : list2) { System.out.println(customer); }
Hibernate抓取策略
Hibernate提供了一系列的优化方式,来提高封装性带来的性能问题,尤其在获取关联对象的时候。例如一级二级缓存(二级一般被Redis替代)机制、和抓取策略等,都是一些用来优化的手段。抓取策略主要是对查询时的语句等做的优化处理。
优化的方式:1)缓存怎么处理;2)查询的优化(抓取)
-
延迟加载:懒加载,执行语句的时候,不直接查询;当使用到关联的对象才发送查询语句。
-
类级别的延迟加载:如load方法,可以在xml的class配置文件中的
lazy = ‘true’(默认)
,若配置为false
则load方法也是直接加载,不使用延迟加载。<class name="com.leehao.hibernateLearning2.domain.Customer" table="cst_customer" lazy="true">
-
类级别延迟加载通过<class>上的lazy进行配置,如果让lazy失效
-
将lazy设置为false
-
将持久化类使用final修饰
-
Hibernate. Initialize()
-
-
-
关联级别的延迟加载:查询某个对象,获取对象的关联对象时。抓取策略往往会和关联级别的延迟加载一起使用,优化语句。
-
-
抓取策略:通过类加载其关联对象时,需要发送SQL语句,如何发送、什么时候发送,与设置的抓取策略有关。可以通过在<set>和<many-to-one>标签上的fetch属性来设置。fetch 属性和lazy属性一起使用。
<Set>上的fetch和lazy抓取
-
Fetch控制抓取策略,控制的是发送的SQL语句的格式
- select:默认,发送普通的select语句,来查询关联对象;如根据Customer来查询联系人,会发送多次SQL,每一个对象会查一次————可以使用批量查询的设置解决
- join:发送一条迫切的左外连接查询关联的对象,只发送一次查询SQL,这时lazy的控制没有作用
- subselect:发送一条子查询来查询关联的对象,和select唯一的不同就是使用子查询;也是两次SQL,第二次将所有关联的东西放入一个表。“相当于”使用了一次select和一次join。
-
lazy:延迟加载,控制查询关联对象的时候是否使用延迟
- true:默认,使用延迟加载查询关联对象
- false:不使用延迟加载
- extra:“极其懒惰?”
-
在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join
//关联对象的抓取策略----set中,也就是1的一方,如何查询它关联的多的一方的集合 //默认set中,<set name="linkMans" fetch="select" lazy="true"> Customer customer = session.get(Customer.class, 1L); //获取的时候才会发送SQL for (LinkMan linkMan : customer.getLinkMans()) { System.err.println(linkMan.getLkm_name()); } ------------------------------------------------------------- //lazy的false或者extra //<set name="linkMans" fetch="select" lazy="extra"> Customer customer = session.get(Customer.class, 1L); //获取的时候才会发送SQL System.out.println(customer.getLinkMans().size()); ------------------------------------------------------------- /**注:使用extra时,发送的SQL语句如下 Hibernate: select count(lkm_id) from cst_linkman where lkm_cust_id =?*/
<many-to-one>上的fetch和lazy抓取
-
fetch
- select:默认
- join:迫切的左外连接,此时lazy失效
-
lazy
- proxy:默认,proxy具体的取值,取决于另一端的<class>上的lazy的值
- false
- no-proxy:不使用
-
在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join
//<many-to-one name="customer" fetch="select" lazy="proxy" class=... Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); LinkMan linkMan = session.get(LinkMan.class, 1L); System.out.println(linkMan.getLkm_name()); System.out.println(linkMan.getCustomer().getCust_name());
批量抓取
获取多个用户的多个联系人的信息,可以想象成抓取两个表的内连接的结果。
1的一方中,在Set中配置batch-size=“n”即可,表示每次抓取n个,默认是1;反之,如果使用如LinkMan批量查询Customer,则需要在Customer中的class配置。
//<set name="linkMans" batch-size="3">
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
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());
}
}
---------------------------------------------------------
Hibernate:
select
customer0_.cust_id as cust_id1_0_,
customer0_.cust_name as cust_nam2_0_,
customer0_.cust_source as cust_sou3_0_,
customer0_.cust_industry as cust_ind4_0_,
customer0_.cust_level as cust_lev5_0_,
customer0_.cust_phone as cust_pho6_0_,
customer0_.cust_mobile as cust_mob7_0_
from
cst_customer customer0_
cus1
Hibernate:
select
linkmans0_.lkm_cust_id as lkm_cus10_1_1_,
linkmans0_.lkm_id as lkm_id1_1_1_,
linkmans0_.lkm_id as lkm_id1_1_0_,
linkmans0_.lkm_name as lkm_name2_1_0_,
linkmans0_.lkm_gender as lkm_gend3_1_0_,
linkmans0_.lkm_phone as lkm_phon4_1_0_,
linkmans0_.lkm_mobile as lkm_mobi5_1_0_,
linkmans0_.lkm_email as lkm_emai6_1_0_,
linkmans0_.lkm_qq as lkm_qq7_1_0_,
linkmans0_.lkm_position as lkm_posi8_1_0_,
linkmans0_.lkm_memo as lkm_memo9_1_0_,
linkmans0_.lkm_cust_id as lkm_cus10_1_0_
from
cst_linkman linkmans0_
where
linkmans0_.lkm_cust_id in (
?, ?, ?
)