一、 Hibernate的检索方式
实际开发中,对数据进行最多的就是查询操作。数据查询在所有的ORM框架中占有重要的作用。
Hibernate的检索方式有5中,分别为对象图检索方式,OID检索方式,HQL检索方式、QBC检索方式和SQL检索方式
二、 对象图检索方式
根据已经加载的对象,导航到他的关联对象。其利用类与类之间的关系来检索对象。
如:查找一个联系人对应的客户。可以由联系人对象自动导航找到联系人所属的客户对象。
Session session = HibernateUtils.getCurrentSession();
LinkMan linkMan =session.get(LinkMan.class, 1l);
Customer customer =linkMan.getCustomer();
三、 OID检索方式
使用session的get()和load()方法加载某条记录对应的对象。如根据客户的主键获取客户对象
Session session = HibernateUtils.getCurrentSession();
Customer customer = session.get(Customer.class, 1l);
Customer customer = session.load(Customer.class, 1l);
四、 HQL单表查询
Hibernate自带的查询语言。与SQL类似
1. 基本检索
String HQL1="from com.aiit.domain.Customer";
//完整写法
String HQL2="from Customer"; //简单写法
String HQL3="from java.lang.Object";//查询所有属于
Query query = session.createQuery(HQL2);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
2. 排序检索
String HQL1="from com.aiit.domain.Customer order by cust_id desc";//根据Id倒序排列
String HQL2="from Customer order by cust_id asc"; //简单写法根据id升序
Query query = session.createQuery(HQL2);
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
3. 条件检索
//根据位置绑定参数
String HQL1="from Customer where cust_name =?";
Query query1 = session.createQuery(HQL1);
//query.setString(0, "虞俊文"); 该方法过期也有setInt()等
//根据下标索引进行设置
query1.setParameter(0, "虞俊文");
List<Customer> list1 = query1.list();
//根据名称绑定参数
String HQL2="from Customer where cust_name=:name";
Query query2 = session.createQuery(HQL2);
query2.setParameter("name", "宋进锋");
Customer customer = (Customer) query2.uniqueResult();
4. 分页检索
//根据位置绑定参数
String HQL="from Customer";
Query query = session.createQuery(HQL);
//分页页码:(当前页面-1)* 每页条数
query.setFirstResult(1);//从第一条开始查
query.setMaxResults(2);//每页最大2条数据
//根据下标索引进行设置
List<Customer> list1 = query.list();
5. 统计检索
//根据位置绑定参数
String HQL="select count(*) from Customer";
String HQL1="select sum(cust_id) from Customer";
String HQL2="select avg(cust_id) from Customer";
String HQL3="select max(cust_id) from Customer";
Query query = session.createQuery(HQL);
Number num = (Long)query.uniqueResult();
System.out.println(num);
6. 投影检索
//查询一列
String HQL="select cust_name from Customer";
//查询多列
String HQL2="select cust_id,cust_name, from Customer";
String HQL3="select new Customer(cust_id,cust_name) from Customer";
//此时我们需要在构造函数写书写一个构造
Query query = session.createQuery(HQL2);
List<Object[]> list = query.list();
五、 SQL的多表查询
1.交叉连接(避免)
返回的结果是被连接的两个表中所有数据行的笛卡尔积。如:
SELECT * FROM 表1 CROSS JOIN 表2
SELECT * FROM 表1,表2
其结果就是两个表所有数据的组合。如表1中有4个属性,表2中有3个属性,那么一共查出4*3=12个属性。在实际开发中少见。
2.内连接(分为隐式和显示)
使用比较运算符对两个表中的数据进行比较,列出与连接条件匹配的数据行,组成新记录。
SELECT 查询字段 FROM 表1 [INNER] JOIN 表2 ON 表1.关系字段=表2.关系字段
- 隐式内连接
看不到inner join的关键字。用where来代替。在交叉连接基础上的升级(多使用了where)
SELECT * FROM 表1.表2 WHERE 表1.关系字段 = 表2.关系字段
- 显示内连接
显示内连接:明显调用了inner join的关键字
SELECT * FROM 表1 INNER JOIN 表2 ON 表1.关系字段 =表2.关系字段
SELECT * FROM 表1 JOIN 表2 ON 表1.关系字段 =表2.关系字段
3.外连接(分为左连接和右连接)
- 左连接:返回包括左表中的所有记录和右表中符合连接条件的记录
SELECT * FROM 表1 LEFT OUTER JOIN 表2 ON 表1.关系字段 =表2.关系字段
- 右连接:返回包括右表中的所有记录和左表中符合连接条件的记录
SELECT * FROM 表1 RIGHT OUTER JOIN 表2 ON 表1.关系字段 =表2.关系字段
4.图例
- 内连接:红色部分
- 左外连接:灰色+红色部分
- 右外连接:红色+蓝色部分
由此引出HQL的多表查询
六、 HQL的多表查询
HQL会比原来SQL分类的基础上多出一些。其分类有以下几种:
1. 内连接
- 普通内连接
String hql="from Customer c inner join c.linkMen";
//此举类似于SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id =l.link_cust_id
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
此时查询出来的语句应该是客户人和联系人之间有联系的所有数据,客户但没有联系人或者有联系人但没有客户的数据,均不会被查询出来
- 迫切内连接
String hql="from Customer c inner join fetch c.linkMen";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
System.out.println(list);
其与普通连接效果类似。但是迫切内连接是讲我们的外键数据,封装到一个对象中进行输出。而普通外连接是将连接的两端对象分别返回,放到一个数组中
2. 外连接
- 左外连接
String hql="from Customer c left join c.linkMen";
//c表示别名
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
该查询语句表示,客户中有联系人的客户也通通打印出联系人。客户没有联系人,都打出联系人为null
- 右外连接
String hql="from Customer c right join c.linkMen";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
该语句表示我们打印出所有的联系人,并且有客户的联系人,打印出客户,但是没有客户的联系人,客户设置为null
- 左,右外迫切连接
同样的与内连接的效果类似,把查出来的语句自动封装成一个对象。并且 在语句后面加上fetch
即可
七、 Criteria的单表查询
Criteria也是HibernateAPI中的一个查询接口,它需要由session进行创建。
1. 基本检索
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
System.out.println(list);
2. 条件查询
使用add添加条件,其中括号里面的参数根据参数表选择。具体可以查询API或者函数
Criteria criteria =session.createCriteria(Customer.class);
//设置条件
//查询id为1的查询
criteria.add(Restrictions.idEq(2l));
criteria.add(Restrictions.like("cust_name", "%小%"));
List<Customer> list = criteria.list();
System.out.println(list);
}
3. 分页检索
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFirstResult(2);
criteria.setMaxResults(7);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
4. 排序检索
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(Order.desc("cust_id"));
criteria.addOrder(Order.asc("cust_id"));
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
5. 统计检索
Criteria criteria = session.createCriteria(Customer.class);
criteria.setProjection(Projections.rowCount());
Long count = (Long) criteria.uniqueResult();
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
6. 离线条件检索
传统Criteria对象,他的创建依赖于session。在做一些特别复杂的条件查询的时候,往往会在WEB层向Service层传输很多参数,Service再将数据数据出纳递给Dao层。最后再Dao层中拼接SQL完成查询。有了离线条件查询对象,可以直接再WEB层进行封装完毕,传递到Service层。再由Service层传递给Dao层完成查询
@Test
public void fun1() {
//WEB/Service层
//---------------------------------------------
DetachedCriteria dc =DetachedCriteria.forClass(Customer.class);
dc.add(Restrictions.idEq(4l));//拼接条件(全部与普通Criteria一致)
//------------------------------------------
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria criteria = dc.getExecutableCriteria(session);
List list = criteria.list();
System.out.println(list);
tx.commit();
session.close();
}
八、 查询优化——类级别加载策略
- 所谓的延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作
- 通常延迟加载分为两类,一类叫做
类级别延迟
,另一类叫做关联级别的延迟
- 类级别延迟指的是当查询某个对象的时候,是否采用延迟。其通常在或上配置lay属性
- 我们再上述内容中,我们调用session.get()方法时,同时可以使用session.load()方法。其中load()方法可以进行慢加载。
//快加载
@Test
public void fun1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 1l);
System.out.println(customer);
tx.commit();
session.close();
}
//慢加载
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = session.load(Customer.class, 1l);
System.out.println(customer);
tx.commit();
session.close();
}
同样,我们也可以对load进行修改。可以再orm元数据中去修改该配置
<class name="Customer" table="cst_customer" lazy="true">
<id name="cust_id" >
<generator class="native"></generator>
</id>
总结:
- get方法没有任何策略,调用即立即查询数据库加载数据
- load方法:应用类级别的加载策略。我们可以在类的orm元数据中设置lazy属性,即会返回代理对象,会在使用属性时,根据关联的session查询数据库,加载数据。
- 为了提交效率。建议使用延迟加载
- 使用懒加载时要确保调用属性加载数据时,session还是打开的,否则抛出异常
九、查询优化——关联级别的加载
关联级别的延迟加载指的是查询到某个对象以后,检索它的关联对象的时候是否采取延迟加载
通过客户查询到其他象关的联系人对象,在查询联系人的时候是否采用延迟加载称为关联级别的延迟。关联级别的延迟通常是<set>
和<many-to-one>
上来进行配置。
<set>
标签上的lazy通常有三个取值- true:默认值,采用延迟加载
- false:检索关联对象的时候,不采用延迟加载
- extra:及其懒惰的
<many-to-one>
标签上的lazy通常有三个取值- proxy:默认值。是否采用延迟取决于一的一方类上的lazy属性的值。
- false:检索关联对象的时候,不采用延迟加载
抓取策略是指查询到某个对象的时候,如何抓取其关联对象-。这个也可以通过配置完成。在关联对象的标签上设置fetch属性。关联上就分为是在和上,也都有不同的取值。
<set>
标签上的fetch通常有三个取值- select(默认值):单表查询加载
- join:多表查询加载集合。发送一条迫切左外连接查询
- subselect:发送一条子查询语句查询其关联对象
<many-to-one>
标签上的fetch有两个取值- select :默认值,发送一条普通的select语句查询关联对象
- join:发送一条迫切左外连接语句查询其关联对象
<!--
lazy属性:决定是否延迟加载
true(默认值):延迟加载
false:立即加载
extra:及其懒惰
fetch属性:决定加载抓取策略,使用什么类型SQL语句加载集合数据
select(默认值):单表查询加载
join:多表查询加载集合
subselect:shi用子查询加载集合
-->
<set name="linkMen" inverse="true" lazy="true" fetch="select">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
通常set集合属性设置lazy fetch属性。搭配配置。如:fetch属性设置为join。而lazy属性不论设置任何属性(true,false,extra)均失效。均为立即加载
<!--
fetch:决定加载的sql语句
select:使用单表查询
join:多表查询
lazy决定加载实际
false:立即加载
proxy:由customer的类级别加载策略决定
-->
<many-to-one name="customer" column="lkm_cust_id" class="Customer" fetch="select" lazy="proxy"></many-to-one>
结论:
-
为了提高效率,fetch的选择应该选择select,lazy的选择应该选择true
-
no-session问题解决:扩大session的作用范围。默认session默认是service层调用。我们可以扩展到扩大到三层架构。我们使用Filter,进行扩大session调用