Hibernate学习笔记三
1.1.1 hibernate的关联关系映射:(多对多)
1.1.1.1 多对多的配置:
步骤一创建实体和映射:
student:
public class student {
private integer sid;
private string sname;
// 学生选择多门课程.
private set<course> courses = new hashset<course>();
...
}
course:
public class course {
private integer cid;
private string cname;
// 课程可以被多个学生选择:
private set<student> students = new hashset<student>();
...
}
student.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.demo3.student" table="student">
<id name="sid" column="sid">
<generator class="native"/>
</id>
<property name="sname" column="sname"/>
<!-- 配置多对多关联关系 -->
<set name="courses" table="stu_cour">
<key column="sno"/>
<many-to-many class="cn.itcast. demo3.course" column="cno"/>
</set>
</class>
</hibernate-mapping>
course.hbm.xml
<hibernate-mapping>
<class name="cn.itcast. demo3.course" table="course">
<id name="cid" column="cid">
<generator class="native"/>
</id>
<property name="cname" column="cname"/>
<!-- 配置多对多关联关系映射 -->
<set name="students" table="stu_cour">
<key column="cno"/>
<many-to-many class="cn.itcast. demo3.student" column="sno"/>
</set>
</class>
</hibernate-mapping>
2 抓取策略(优化)
2.1 检索方式
l 立即检索:立即查询,在执行查询语句时,立即查询所有的数据。
l 延迟检索:延迟查询,在执行查询语句之后,在需要时在查询。(懒加载)
2.2 检查策略
l 类级别检索:当前的类的属性获取是否需要延迟。
l 关联级别的检索:当前类 关联 另一个类是否需要延迟。
2.3 类级别检索
l get:立即检索。get方法一执行,立即查询所有字段的数据。
l load:延迟检索。默认情况,load方法执行后,如果只使用oid的值不进行查询,如果要使用其他属性值将查询 。 customer.hbm.xml <class lazy="true | false">
lazy 默认值true,表示延迟检索,如果设置false表示立即检索。
@test public void demo02() { //类级别 session session = factory.opensession(); session.begintransaction();
//1立即 // customer customer = (customer) session.get(customer.class, 1); //2延迟 customer customer = (customer) session.load(customer.class, 1);
//打印 system.out.println(customer.getcid()); system.out.println(customer.getcname());
session.gettransaction().commit(); session.close(); } |
2.4 关联级别检索
2.4.1 一对多或多对多
2.4.1.1 介绍
l 容器<set> 提供两个属性:fetch、lazy
fetch:确定使用sql格式
lazy:关联对象是否延迟。
l fetch:join、select、subselect
join:底层使用迫切左外连接
select:使用多个select语句(默认值)
subselect:使用子查询
l lazy:false、true、extra
false:立即
true:延迟(默认值)
extra:极其懒惰
2.4.1.2 fetch="join"
l fetch="join" ,lazy无效。底层使用迫切左外连接,使用一条select将所有内容全部查询。
@test public void demo03() { //关联级别:一对多, // * customer.hbm.xml <set fetch="join"> // *** select语句使用左外连接,一次性查询所有 session session = factory.opensession(); session.begintransaction();
//1 查询客户 customer customer = (customer) session.get(customer.class, 1); system.out.println(customer.getcname());
//2 查询客户订单数 set<order> orderset = customer.getorderset(); system.out.println(orderset.size());
//3 查询客户订单详情 for (order order : orderset) { system.out.println(order); }
session.gettransaction().commit(); session.close(); } |
2.4.1.3 fetch="select"
l 当前对象 和 关联对象 使用多条select语句查询。
l lazy="false" , 立即,先查询客户select,立即查询订单select
l lazy="true",延迟,先查询客户select,需要订单时,再查询订单select
l lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要订单数,使用聚合函数(不查询详情)
2.4.1.4 fetch="subselect"
l 将使用子查询。注意:必须使用query否则看不到效果。
l lazy= 同上
@test public void demo04() { //关联级别:一对多, // 演示3:* customer.hbm.xml <set fetch="subselect"> session session = factory.opensession(); session.begintransaction();
//1 查询客户 list<customer> allcustomer = session.createquery("from customer").list(); customer customer = allcustomer.get(0); system.out.println(customer.getcname());
//2 查询客户订单数 set<order> orderset = customer.getorderset(); system.out.println(orderset.size());
//3 查询客户订单详情 for (order order : orderset) { system.out.println(order); }
session.gettransaction().commit(); session.close(); } |
2.4.2 多对一
2.4.2.1 介绍
l <many-to-one fetch="" lazy=""> (<one-to-one>)
l fetch取值:join、select
join:底层使用迫切左外连接
select:多条select语句
l lazy取值:false、proxy、no-proxy
false:立即
proxy:采用关联对象 类级别检索的策略。
订单 关联 客户 (多对一)
订单 立即获得 客户,需要在客户customer.hbm.xml <class lazy="false">
订单 延迟获得 客户,需要在客户customer.hbm.xml <class lazy="true">
no-proxy 不研究
2.4.2.2 fetch="join"
l fecth="join" select语句使用左外连接,此时lazy无效。
@test public void demo05() { //关联级别:多对一, // 演示1:* order.hbm.xml <set fetch="join"> lazy无效 // * 注意:检查customer.hbm.xml 和 order.hbm.xml 没有额外的配置 session session = factory.opensession(); session.begintransaction();
//1 查询订单 order order = (order) session.get(order.class, 1); system.out.println(order.getprice());
//2 查询订单客户信息 customer customer = order.getcustomer(); system.out.println(customer.getcname());
session.gettransaction().commit(); session.close(); } |
2.4.2.3 fetch="select"
l 将采用多条select语句,lazy="proxy"是否延迟,取决关联对象 类级别检索策略。
l lazy="false"
l lazy="proxy"
2.5 批量查询
l 当客户 关联查询 订单,给每一个客户生产一个select语句查询订单。批量查询使用in语句减少查询订单语句个数。
默认:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)
l <set batch-size="5"> 5表示括号中?个数。
@test public void demo06() { //批量查询 session session = factory.opensession(); session.begintransaction();
//1 查询所有客户 list<customer> allcustomer = session.createquery("from customer").list();
//2遍历 for (customer customer : allcustomer) { system.out.println(customer.getcname()); system.out.println(customer.getorderset().size()); }
session.gettransaction().commit(); session.close(); } |
2.6 检索总结
检索策略 |
优点 |
缺点 |
优先考虑使用的场合 |
立即检索 |
对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象 |
(1)select语句多 (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。 |
(1)类级别 (2)应用程序需要立即访问的对象 (3)使用了二级缓存 |
延迟检索 |
由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。 |
应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。 |
(1)一对多或者多对多关联 (2)应用程序不需要立即访问或者根本不会访问的对象
|
表连接检索 |
(1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。 (2)使用了外连接,select语句少 |
(1)可能会加载应用程序不需要访问的对象,浪费内存。 (2)复杂的数据库表连接也会影响检索性能。 |
(1)多对一或一对一关联 (2)需要立即访问的对象 (3)数据库有良好的表连接性能。 |
customer get(int id)
return session.load(customer.class,id);
- layz=false
- 在service层获得在页面要上要用到的属性=> 在service层中确保数据已经
3 查询方式总结
1.通过oid检索(查询)
get()立即、如果没有数据返回null
load()延迟,如果没有数据抛异常。
2.导航对象图检索方式:关联查询
customer.getorderset()
user.getpost().getdepartment().getdepname();
3.原始sql语句
sqlquery sqlquery = session.createsqlquery("sql 语句") --->表,表字段(列)
sqlquery.list() 查询所有
sqlquery.uniqueresult() 查询一个
4.hql,hibernate query language hibernate 查询语言【1】
query query = session.createquery("hql语句") --> 对象,对象属性
5.qbc,query by criteria 纯面对对象查询语言【2】
4 hql【掌握】
4.1 介绍
4.2 查询所有客户
@test public void demo01(){ //1 查询所有 session session = factory.opensession(); session.begintransaction();
//1 使用简单类名 , 存在自动导包 // * customer.hbm.xml <hibernate-mapping auto-import="true"> // query query = session.createquery("from customer"); //2 使用全限定类名 query query = session.createquery("from com.itheima.a_init.customer");
list<customer> allcustomer = query.list(); for (customer customer : allcustomer) { system.out.println(customer); }
session.gettransaction().commit(); session.close(); } |
4.3 选择查询
@test public void demo02(){ //2 简单条件查询 session session = factory.opensession(); session.begintransaction();
//1 指定数据,cid oid名称 // query query = session.createquery("from customer where cid = 1"); //2 如果使用id,也可以(了解) // query query = session.createquery("from customer where id = 1"); //3 对象别名 ,格式: 类 [as] 别名 // query query = session.createquery("from customer as c where c.cid = 1"); //4 查询所有项,mysql--> select * from... query query = session.createquery("select c from customer as c where c.cid = 1");
customer customer = (customer) query.uniqueresult(); system.out.println(customer);
session.gettransaction().commit(); session.close(); } |
4.4 投影查询(部分)
@test public void demo04(){ //4 投影 session session = factory.opensession(); session.begintransaction();
//1 默认 //如果单列 ,select c.cname from,需要list<object> //如果多列,select c.cid,c.cname from ,需要list<object[]> ,list存放每行,object[]多列 // query query = session.createquery("select c.cid,c.cname from customer c"); //2 将查询部分数据,设置customer对象中 // * 格式:new customer(c.cid,c.cname) // * 注意:customer必须提供相应的构造方法。 // * 如果投影使用oid,结果脱管态对象。 query query = session.createquery("select new customer(c.cid,c.cname) from customer c");
list<customer> allcustomer = query.list(); for (customer customer : allcustomer) { system.out.println(customer.getcid() + " : " + customer.getorderset().size()); }
session.gettransaction().commit(); session.close(); } |
4.5 排序
@test public void demo03(){ //3排序 ,mysql--> select... order by 字段 [asc]|desc ,.... session session = factory.opensession(); session.begintransaction();
query query = session.createquery("from customer order by cid desc");
list<customer> allcustomer = query.list(); for (customer customer : allcustomer) { system.out.println(customer.getcid()); }
session.gettransaction().commit(); session.close(); } |
4.6 分页
@test public void demo05(){ //分页 session session = factory.opensession(); session.begintransaction();
query query = session.createquery("from customer"); // * 开始索引 , startindex 算法: startindex = (pagenum - 1) * pagesize; // *** pagenum 当前页(之前的 pagecode) query.setfirstresult(0); // * 每页显示个数 , pagesize query.setmaxresults(2);
list<customer> allcustomer = query.list(); for (customer customer : allcustomer) { system.out.println(customer.getcid()); }
session.gettransaction().commit(); session.close(); } |
4.7 绑定参数
@test public void demo06(){ /* 6 绑定参数 * 方式1:占位符,使用? 在hql语句替换具体参数 * 设置参数 query.setxxx(int , object) * 参数1:?位置,从0开始。 * 参数2:实际参数 * 例如:string --> query.setstring(int,string) * 方式2:别名 , 格式 “属性= :别名 ” * 设置参数 query.setxxx(string,object) * 参数1:别名 * 参数2:实际参数 * 例如:integer --> query.setinteger(string,integer) * 提供公共设置方法 * setparameter(int|string , object) */ session session = factory.opensession(); session.begintransaction();
integer cid = 1;
//方式1 // query query = session.createquery("from customer where cid = ?"); // query.setinteger(0, cid); //方式2 query query = session.createquery("from customer where cid = :xxx"); // query.setinteger("xxx", cid); query.setparameter("xxx", cid);
customer customer = (customer) query.uniqueresult(); system.out.println(customer);
session.gettransaction().commit(); session.close(); } |
4.8 聚合函数和分组
@test public void demo07(){ /* 7 聚合函数 */ session session = factory.opensession(); session.begintransaction();
//1 // query query = session.createquery("select count(*) from customer"); //2 别名 // query query = session.createquery("select count(c) from customer c"); //3 oid query query = session.createquery("select count(cid) from customer");
long numlong = (long) query.uniqueresult(); int num = numlong.intvalue();
system.out.println(num);
session.gettransaction().commit(); session.close(); } |
4.9 连接查询
1.交叉连接 ,等效 sql 笛卡尔积
2.隐式内连接,等效 sql 隐式内连接
3.内连接,等效sql内连接
4.迫切内连接,hibernate底层使用 内连接。
5.左外连接,等效sql左外连接
6.迫切左外连接,hibernate底层使用 左外连接
7.右外连接,等效sql右外连接
内连接和迫切内连接?
左外连接和迫切左外连接?
@test public void demo08(){ /* 8 链接查询 : 左外连接和迫切左外连接? * * 左外连接 , left outer join * 底层使用sql的左外连接,hibernate进行数据自动封装,将一条记录,封装给两个对象(customer,order) * 将两个对象添加到一个对象数组中object[customer,order] * * 迫切左外链接 left outer join fetch * 底层使用sql的左外连接,hibernate将一条记录封装给customer,讲order数据封装order,并将order关联到customer * customer.getorderset().add(order) * 默认查询的数据重复 */ session session = factory.opensession(); session.begintransaction();
//左外连接 // list list = session.createquery("from customer c left outer join c.orderset ").list(); //迫切左外链接 (默认数据重复) // list list = session.createquery("from customer c left outer join fetch c.orderset ").list(); //迫切左外链接 (去重复) list list = session.createquery("select distinct c from customer c left outer join fetch c.orderset ").list();
session.gettransaction().commit(); session.close(); } |
4.10 命名查询
l 思想:将hql从java源码中,提取到配置文件中。
l 分类:全局、布局
l 配置
全局:*.hbm.xml <class></class><query name="名称">hql语句
局部: <class name="" table=""><id><property> <query name="">hql</class>
l 获得
全局:
session.getnamedquery("queryname")
局部:
session.getnamedquery("classname.queryname") 需要使用类的全限定名称
@test public void demo09(){ /* 9 命名查询 */ session session = factory.opensession(); session.begintransaction();
//全局 //list list = session.getnamedquery("findall").list(); //局部 list list = session.getnamedquery("com.itheima.a_init.customer.findall").list();
system.out.println(list.size());
session.gettransaction().commit(); session.close(); } |
5 qbc【了解】
5.0.0.1 qbc查询:
qbc:query by criteria条件查询.面向对象的查询的方式.
5.0.0.2 qbc简单的查询:
// 简单查询:
list<customer> list = session.createcriteria(customer.class).list();
for (customer customer : list) {
system.out.println(customer);
}
5.0.0.3 qbc分页的查询:
criteria criteria = session.createcriteria(order.class);
criteria.setfirstresult(10);
criteria.setmaxresults(10);
list<order> list = criteria.list();
5.0.0.4 qbc排序查询:
criteria criteria = session.createcriteria(customer.class);
// criteria.addorder(org.hibernate.criterion.order.asc("age"));
criteria.addorder(org.hibernate.criterion.order.desc("age"));
list<customer> list = criteria.list();
5.0.0.5 qbc条件查询:
// 按名称查询:
/*criteria criteria = session.createcriteria(customer.class);
criteria.add(restrictions.eq("cname", "tom"));
list<customer> list = criteria.list();*/
// 模糊查询;
/*criteria criteria = session.createcriteria(customer.class);
criteria.add(restrictions.like("cname", "t%"));
list<customer> list = criteria.list();*/
// 条件并列查询
criteria criteria = session.createcriteria(customer.class);
criteria.add(restrictions.like("cname", "t%"));
criteria.add(restrictions.ge("age", 35));
list<customer> list = criteria.list();
5.0.0.6 离线查询(了解)
l detachedcriteria 离线查询对象,不需要使用session就可以拼凑查询条件。一般使用在web层或service层拼凑。将此对象传递给dao层,此时将与session进行绑定执行查询。
l 离线查询条件与qbc一样的。
@test public void demo10(){ /* 10 离线查询 */
//web & service detachedcriteria detachedcriteria = detachedcriteria.forclass(customer.class); detachedcriteria.add(restrictions.eq("cid", 1));
//---------------dao
session session = factory.opensession(); session.begintransaction();
// 离线criteria 与session绑定 criteria criteria = detachedcriteria.getexecutablecriteria(session); list<customer> allcustomer = criteria.list(); system.out.println(allcustomer.size());
session.gettransaction().commit(); session.close(); } |
6 常见配置
6.1 整合c3p0(连接池) (了解)
l 整合c3p0
步骤一:导入c3p0 jar包
步骤二:hibernate.cfg.xml 配置
hibernate.connection.provider_class org.hibernate.connection.c3p0connectionprovider
l c3p0具体配置参数
###########################
### c3p0 connection pool###
###########################
#hibernate.c3p0.max_size 2
#hibernate.c3p0.min_size 2
#hibernate.c3p0.timeout 5000
#hibernate.c3p0.max_statements 100
#hibernate.c3p0.idle_test_period 3000
#hibernate.c3p0.acquire_increment 2
#hibernate.c3p0.validate false
6.2 事务
6.2.1 回顾
l 事务:一组业务操作,要么全部成功,要么全部不成功。
l 特性:acid
原子性:整体
一致性:数据
隔离性:并发
持久性:结果
l 隔离问题:
脏读:一个事务读到另一个事务未提交的内容
不可重复读:一个事务读到另一个事务已提交的内容(insert)
虚读(幻读):一个事务读到另一个事务已提交的内容(update)
l 隔离级别--解决问题
read uncommittd,读未提交。存在3个问题。
read committed,读已提交。解决:脏读。存在2个问题。
repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
serializable,串行化。单事务。没有问题。
6.2.2 hibernate设置隔离级别
l 在hibernate.cfg.xml 配置
hibernate.connection.isolation 4
6.2.3 lost update 丢失更新
l 悲观锁:丢失更新肯定会发生。
采用数据库锁机制。
读锁:共享锁。
select .... from ... lock in share mode;
写锁:排他锁。(独占)
select ... from .... for update
l 乐观锁:丢失更新肯定不会发生
在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。
6.2.4 hibernate处理丢失更新
l 悲观锁:写锁
@test public void demo01(){ //1 查询所有 session session = factory.opensession(); session.begintransaction();
customer customer = (customer) session.get(customer.class, 1 ,lockmode.upgrade); system.out.println(customer);
session.gettransaction().commit(); session.close(); } |
l 乐观锁:
在po对象(javabean)提供字段,表示版本字段。一般integer
在*.hbm.xml 文件配置 <version name="...">
步骤一:
步骤二:
步骤三:测试
@test public void demo02(){ //1 查询所有 session session = factory.opensession(); session.begintransaction();
// order order = new order(); // order.setprice(998d); // session.save(order);
order order = (order) session.get(order.class, 32); order.setprice(889d);
session.gettransaction().commit(); session.close(); } |