Hibernate的检索方式
Hibernate的抓取策略
Hibernate的批量检索
事务的并发问题
Hibernate的二级缓存
Hibernate的查询缓存
1.1 Hibernate框架2回顾:
1.Hibernate一级缓存:
* 缓存:优化手段.
* Hibernate中两个基本缓存:
* 一级缓存:与session的生命周期一致.
* 二级缓存:与sessionFactory的生命周期一致.
* 证明一级缓存的存在:
* session的生命周期开始
* 查询了一下Book.//发送SQL去查询
* 查询了一个Book.//不发送SQL.
* session的生命周期结束
* 一级缓存的管理:
* clear();
* evict(Object obj);
* flush();
* refresh(Object obj);
* 一级缓存的刷出时机:
* ALWAYS :
* AUTO :默认值
* COMMIT :事务提交/session.flush()时候
* MANUAL :手动调用session.flush()刷出缓存
* 持久化对象的操作的常用方法:
2.Hibernate为了管理持久化对象:
* 将持久化对象分成三种状态:
* 瞬时态:
* 没有唯一标识OID,且没有与session关联.
* 持久态:
* 有唯一的标识OID,且与session关联.
* 脱管态:
* 有唯一的标识OID,没有与session关联.
* 瞬时态对象:
* Book book = new Book();
* 瞬时-->持久:
* save()/saveOrUpdate();
* 瞬时-->脱管:
* book.setBid(1);
* 持久态对象:
* get()/load()/find()/lock();
* 持久态-->瞬时:
* delete();
* 持久态-->脱管:
* close()/clear()/evict();
* 脱管态对象:
* Book book = new Book();
* book.setBid(1);
* 脱管-->瞬时:
* book.setBid(null);
* 脱管-->持久:
* update();/saveOrUpdate();
3.Hibernate的关联关系配置:
* 一对多:
* 多对多:
* 一对一:
1.2 Hibernate的检索方式:
1.2.1 Hibernate的检索方式:
检索方式:查询的方式:(五种)
导航对象图检索方式: 根据已经加载的对象导航到其他对象
* Customer customer =(Customer)session.get(Customer.class,1);
* customer.getOrders();// 获得到客户的订单
OID 检索方式: 按照对象的 OID 来检索对象
* get()/load();方法进行检索.
HQL 检索方式: 使用面向对象的HQL 查询语言
* Query query = session.createQuery(“HQL”);
QBC 检索方式: 使用QBC(Query By Criteria) API 来检索对象. 这种 API 封装了基于字符串形式的查询语句, 提供了更加面向对象的查询接口.
* Criteria criteria = session.createCriteria(Customer.class);
本地 SQL 检索方式: 使用本地数据库的SQL 查询语句
* SQLQuery query = session.createSQLQuery(“SQL”);
1.2.2 HQL:
HQL:Hibernate Query Language:
* 特点:
* 面向对象的查询:
* 支持方法链编程:
* 使用:
1.创建Query接口.
1.查询所有记录:
使用HQL查询所有客户信息:List<Customer> list = session. createQuery( "from Customer").list();
for (Customer customer : list) { System.out.println(customer); }
Query query=session.createQuery("from Customer c where c.name = 'tom1' ");
List list=query.list();
使用QBC的方式查询所有记录:
主要由Criteria、Criterion接口和Restrictions类组成,他支持在运行时动态生成查询语句。
List<Customer> list = session.createCriteria(Customer.class).list();
for (Customer customer : list) { System.out.println(customer); }
Criteria criteria=session.createCriteria(Customer.class);
Criterion cn1=Restrictions.eq("name", "tom1");
criteria.add(cn1);
list=criteria.list();
//方法链编程:
session.createCriteria(Customer.class).add(Restrictions.eq("name", "tom1")).list();
List<Object[]> list = session. createSQLQuery( "select * from customer").list();
for (Object[] objects : list) {System.out.println(Arrays.toString(objects)); }
使用SQL查询所有记录:封装到实体对象中List<Customer> list = session.createSQLQuery("select * from customer"). addEntity(Customer.class).list();
for (Customer customer : list) {System.out.println(customer);}
SQLQuery sqlquery = session.createSQLQuery("select {c.*} from CUSTOMERS c where"+
" c.name =:customerName");
// 动态绑定参数
sqlquery.setString("customerName", “tom");
“c”用来引用数据表的别名,例如以上代码中{c.*}表示使用c来作为
customers表别名。 把sql查询返回的关系数据映射为对象
sqlquery.addEntity("c", Customer.class);
// 执行sql select语句,返回查询结果。
List list = sqlquery.list();
通过HQL检索一个类的实例时,如果查询语句的其他地方需要引用它,应该为这个类指定一个别名
from Customer as c where c.name=:custname(as可省略)
2.查询使用别名:
使用别名
List<Customer> list =session.createQuery("from Customer c").list();//(as可省略)
System.out.println(list);
使用别名:带参数
List<Customer> list= session.createQuery("fromCustomer as c where c.cname = ?").setString(0, "小沈").list();
// 不支持 select * from Customer写法.可以写成 select 别名from Customer as 别名;
List<Customer> list =session.createQuery("select c from Customer c").list();
多态查询(是指查询出当前类及所有子类的实例)
//查询出所有的实体(当前类和所有子类的实例)
Query query = session.createQuery(“from Customer”);//映射文件中已经配置过包,所以前面包可以省略
query.list();
//检索出所有实现serializable接口的实例
Query query = session.createQuery(“ from java.io.Serializable”)
query.list();
//检索出所有的持久化对象
Query query = session.createQuery(“ from java.lang.Object”)
query.list();
3.排序:
hql 查询:
1,List<Customer>list = session.createQuery("from Customer c order by c.id desc").list();
for (Customer customer :list) {System.out.println(customer);}
2,Query query = session.createQuery("from Customer c order by c.id");
query.list();
QBC查询:
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(org.hibernate.criterion.Order.asc("id"));
criteria.list();
4.分页查询:
分页查询:
setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下, Query 从查询结果中的第一个对象开始检索
setMaxResult(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和 Criteria 接口检索出查询结果中所有的对象
//hql查询
1,Query query = session.createQuery("from Order");
query.setFirstResult(20);
query.setMaxResults(10);
List<Order> list =query.list();
for (Order order : list) {
System.out.println(order);//查出10个订单
}
2,Query query=session.createQuery("from Order o order by o.id asc");
query.setFirstResult(3);
query.setMaxResults(4);
query.list();
//使用QBC
Criteria criteria=session.createCriteria(Order.class);
criteria.addOrder(org.hibernate.criterion.Order.asc("id"));
criteria.setFirstResult(3);
criteria.setMaxResults(4);
criteria.list();
5.单个对象查询:
hql 查询:
1,Customer customer =(Customer) session.createQuery("from Customer where cname = ?")
.setString(0, "小明").uniqueResult(); //.list是多个,.uniqueResult拿一个
System.out.println(customer);
2, Query query = session.createQuery("from Customer c order by c.id");
query.setMaxResults(1);
query.uniqueResult();
qbc查询:
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(org.hibernate.criterion.Order.asc(“id"));
criteria.setMaxResults(1);
criteria.uniqueResult();
Customer customer = (Customer) session.createCriteria(Customer.class)
.add(Restrictions.eq("cname", "小明")).uniqueResult();
System.out.println(customer);
6.参数绑定:
// 1.使用?号方式绑定
Query query =session.createQuery("from Customer where cname = ?");
query.setString(0, "小沈");
List<Customer> list =query.list();
System.out.println(list);
Query query =session.createQuery("from Customer where cname = ? and cid =?");
query.setString(0, "小沈");
query.setInteger(1,3);
List<Customer> list =query.list();
System.out.println(list);
Query query = session.createQuery("from Customer c where c.name=? and c.age=?");
query.setString(0,"Tom");
query.setInteger(1, 21);
query.list();
// 2.使用名称的方式绑定
Query query =session.createQuery("from Customer where cname=:name and cid=:id");
query.setString("name","小沈");
query.setInteger("id",3);
List<Customer> list =query.list();
System.out.println(list);
hql 查询:
Query query = session.createQuery("from Customer c where " +" c.name=:custname and c.age=:custage");
//第一个参数代表名字,第二个参数代表值
query.setString("custname", "Tom");
query.setInteger("custage", 21);
List list = query.list();
// 3.绑定实体
List<Order> list = session.createQuery("from Order o where o.customer = ?").setEntity(0,customer).list();
for (Order order : list) {System.out.println(order); } (o.customer:order里面的一个属性,setEntity:设置参数(必须是customer对象))
hibernate检索方式
7.投影操作:
// 查询客户的名称:
List<Object> list =session.createQuery("select c.cname from Customerc").list();
System.out.println(list);
List<Object[]> list =session.createQuery("select c.cid,c.cname from Customerc").list();
for (Object[] objects : list) {System.out.println(Arrays.toString(objects)); }
List<Customer> list =session.createQuery("select new Customer(cname) from Customer").list();//提供一个无参构造,有参构造(name)
System.out.println(list);
Query query = session.createQuery("select " new Customer(c.id,c.name) " +" from Customer c ");
List<CustomerRow> list = query.list();
for (int i=0;i<list.size();i++) {
CustomerRow cr=list.get(i);
System.out.print(cr.getId() + ""+cr.getName());
System.out.println(" ");
}
也可以 new list 、new map
//list集合中存放的对象数组,数组中存放的查询的部分属性
Query query = session.createQuery("select c.name,c.city " + " from Customer c");
List list = query.list();
for(int i=0;i<list.size();i++){
Object[] pair = (Object[])list.get(i);
for(int k=0;k<pair.length;k++){
System.out.print(pair[k]+”“);
}
System.out.println();
}
// QBC方式
List list = session.createCriteria(Customer.class).setProjection(Projections.projectionList().add(Property.forName("name")).add(Property.forName("city"))).list();
8.模糊查询:
Query query =session.createQuery("from Customer where cname like ?");
query.setParameter(0,"小%");//以小开头
List<Customer> list =query.list();
System.out.println(list);
Criteria criteria =session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname","大%"));
List<Customer> list =criteria.list();
System.out.println(list);
SQL多表查询:
* 使用连接的形式:
* 交叉连接:(用得少)
* select * from A,B;
* 内连接:查询的是两个表的交集
* select * from A inner join B on A.字段 = B.字段;
* 隐式内连接:
* select * from A,B where A.字段 = B.字段;(条件,查的是两个表的交集)
* 外连接:
* 左外连接:
* select * from A left outer join B on A.字段 = B.字段;
* 右外连接:
* select * from A right outer join B on A.字段 = B.字段;
关联级别运行时的检索策略
1.若在HQL、QBC代码中没有显式指定检索策略,使用映射文件中的检索策略。但HQL总是忽略映射文件中设置的迫切左外(内)连接检索策略。也就是说,即使映射文件中设置了迫切左外(内)连接检索策略,如果HQL查询语句中没有显示指定这种策略,那么HQL仍然采用立即检索策略。
2.若代码中显示指定了检索策略,则覆盖映射文件中的检索策略
3.目前的hibernate版本只允许在一个查询语句中迫切左外连接检
索一个集合。
4.HQL支持各种各样的连接查询
HQL多表的查询:
* 连接的形式:
* 交叉连接:(用得少)
* 内连接:
HQL查询:默认检索的是成对的对象Customer和Order。返回是对象数组
// inner join 表示内连接检索策略
Query query = session.createQuery("from Customer c inner join c.orders o where c.name like 'T%'");
List list = query.list();
for(int i=0;i<list.size();i++){
Object [] params = (Object[])list.get(i);
Customer c = (Customer)params[0];
Order o = (Order)params[1];
System.out.println(c.getId()+” “+c.getName()+” “+c.getAge());
System.out.println(o.getId()+” “+o.getOrderNumber()+” “+o.getPrice());
}
* 隐式内连接:(了解)
HQL:
createQuery(“from Order o where o.customer.name like ‘T%’”);
等价于:
from Order o join o.customer c where c.name like ‘T%’
* 迫切内连接:
//Inner join fetch表示迫切内连接检索策略 ,也就是覆盖映射文件中指定的检索策略
Query query = session.createQuery("from Customer c inner join fetch c.orders o where c.name like 'T%'");
List list = query.list();
* 左外连接:
//left outer join 左外连接,使用左外连接查询时,将根据映射文件的配置来决定
// orders集合的检索策略 返回是对象数组
Query query = session. createQuery("from Customer c left outer join c.orders o where c.name like 'T%'");
List list = query.list();
for(int i=0;i<list.size();i++){
Object [] params = (Object[])list.get(i);
Customer c = (Customer)params[0];
Order o = (Order)params[1];
System.out.println(c.getId()+” “+c.getName()+” “+c.getAge());
System.out.println(o.getId()+” “+o.getOrderNumber()+” “+o.getPrice());
}
* 迫切左外连接:
//left outer join fetch 表示迫切左外连接检索策略
//Query方法返回的集合中存放Customer对象的引用,每个Customer对象的orders
//集合都被初始化
Query query=session.createQuery("from Customer c left outer join fetch c.orders o where c.name=?");
query.setString(0, "tom");
List<Customer> list = query.list();
for(int i=0;i<list.size();i++){
Customer c = list.get(i);
System.out.println("customer "+c.getId()+ " "+c.getName()+” “+c.getAge());
Set orderes = c.getOrderes();
Iterator<Order> it=orderes.iterator();
while(it.hasNext()){
Order order=(Order)it.next();
System.out.println("order "+order.getId()+" “
+order.getOrderNumber());
}
}
* 右外连接:
HQL: right outer join 右外连接 返回是对象数组
Query query = session.createQuery("from Customer c right outer join c.orders o where c.name like ‘t%'");
List list = query.list();
* HQL的内连接和迫切内连接区别:
* 内连接查询 :将数据封装一个List<Object[]>中.
* 迫切内连接 :将数据封装一个List<Customer>中.但是迫切内连接,得到会有重复记录 ,需要使用distinct排重.
// 内连接查询的是两个表的交集部分:
Query query = session.createQuery("from Customer c inner join c.orders");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
// 迫切内连接:使用一个关键字 fetch(HQL)
Query query = session.createQuery("select distinct c from Customer c inner join fetch c.orders");//生成的sql语句没有fetch
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}
1.2.3 QBC:
1.查询所有记录:
List<Customer> list =session.createCriteria(Customer.class).list();
for (Customer customer :list) { System.out.println(customer);}
2.排序:
List<Customer>list = session.createCriteria(Customer.class).addOrder(org.hibernate.criterion.Order.desc("id")).list();
for (Customer customer :list) {System.out.println(customer); }
3.分页:
Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<Order> list =criteria.list();
for (Order order : list) {System.out.println(order);}
4.获取单个对象:
Customer customer =(Customer) session.createCriteria(Customer.class).add(Restrictions.eq("cname","小明")).uniqueResult();
System.out.println(customer);
5.带参数的查询:
List<Customer> list =session.createCriteria(Customer.class).add(Restrictions.eq("cname","小明")).list();
System.out.println(list);
List<Customer> list =session.createCriteria(Customer.class) .add(Restrictions.eq("cname","小明")).add(Restrictions.eq("cid",2)).list();
System.out.println(list);
6.模糊查询:
1.2.4 SQL:
1.SQL语句查询所有记录:
List<Object[]> list = session.createSQLQuery("select *from customer").list();
for (Object[] objects :list) {
System.out.println(Arrays.toString(objects));
}
List<Customer> list = session.createSQLQuery("select *from customer").addEntity(Customer.class).list();
for (Customer customer :list) {
System.out.println(customer);
}
avg()
报表查询
使用聚集函数
Long count = (Long) session.createQuery("select count(*) from Order").uniqueResult();
System.out.println("count"+count);
***********************************************************
Query query = session.createQuery("select avg(c.age) from Customer c");
Float avg=(Float)query.uniqueResult();
System.out.println("avg"+avg);
**********************************************************
Query query = session.createQuery("select max(c.age),min(c.age) from Customer c");
Object[] maxmin=(Object[])query.uniqueResult();
System.out.println("max"+(Long)maxmin[0]);
System.out.println("min"+(Long)maxmin[1]);
***********************************************************
Query query = session.createQuery("select sum(c.age) from Customer c");
Long sum=(Long)query.uniqueResult();
System.out.println("sum"+sum);
报表查询 分组
List list=session.createQuery("selectc.name,count(c) from Customer c group by c.name").list();
System.out.println(list.size());
for(inti=0;i<objects.size();i++){
Object [] o = objects.get(i);
System.out.print(o[0]+” “+o[1]);
System.out.println();}
在映射文件中定义命名查询语句
<class>
.......
</class>
<query name="findCustomersByName">
<![CDATA[from Customer c where c.namelike ?]]>
</query>
------------------------------------------------------------------
query= session.getNamedQuery(“findCustomersByName”);
query.setString(0,”%T%”);
query.list();
注意:在Class类中定义的HQL语句会覆盖映射文件
离线条件查询 DetachedCriteria
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
criteria.add(Restrictions.eq("city","beijing"));
List<Customer>customers =criteria.getExecutableCriteria(session).list();
System.out.println(customers);
public void demo15(){
// web层的封装
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
criteria.add(Restrictions.eq("cname", "小明"));
criteria.add(Restrictions.eq("cid", 2));
// 传递到DAO层
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c1 = criteria.getExecutableCriteria(session);
List<Customer> list = c1.list();
System.out.println(list);
tx.commit();
session.close();
}
1.3 Hibernate的抓取策略(检索)
1.3.1 区分延迟和立即检索:
立即检索:
* 当执行某行代码的时候,马上发出SQL语句进行查询.
* get()
延迟检索:
* 当执行某行代码的时候,不会马上发出SQL语句进行查询.当真正使用这个对象的时候才会发送SQL语句.
* load();
区分立即检索和延迟检索
public void demo1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 立即检索
/*Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer);*/
// 延迟检索:
// 持久化类如果设置为final 延迟检索就失效了.(不能生成代理对象)
// 在Customer.hbm.xml中在<class>标签上配置lazy="false"不支持延迟检索,就会立即检索.
Customer customer = (Customer) session.load(Customer.class, 1);
// System.out.println(customer);
// 初始化代理对象
// System.out.println(customer.getCname());
Hibernate.initialize(customer);
tx.commit();
session.close();
//初始化代理对象(两种方式)
1,使用除了id以外的其他属性,自动初始化
2,Hibernate.initialize(customer);
public void loadCustomertrueProxyInit(){Session session=sessionFacoty.openSession();
Transaction tx=session.beginTransaction();
//此时查询到的c对象是一个代理对象
Customer c=(Customer)session.load(Customer.class, 1);
System.out.println(c.getClass()); //代理对象
//判断代理对象是否被初始化 对集合对象也适用
if(!Hibernate.isInitialized(c)){
System.out.println(c.getClass()); //代理对象
System.out.println("没有被初始化");
//方法一
c.getAge();//会查询select语句
//初始化代理对象的方法,hibernate执行select查询,方法二
Hibernate.initialize(c);}
tx.commit();
session.close();
}
类级别检索和关联级别检索:
* 类级别的检索:
* <class>标签上配置lazy
* 关联级别的检索:
* <set>/<many-to-one>上面的lazy.
* 查询某个对象的时候,是否需要查询关联对象?
* 查询关联对象的时候是否采用延迟检索? (通过配置)
类级别可选的检索策略包括立即检索和延迟检索, 默认为延迟检索
类级别的检索策略可以通过<class>元素的lazy属性进行设置
如果程序加载一个对象的目的是为了访问它的属性, 可以采取立即检索. 如果程序加载一个持久化对象的目的是仅仅为了获得它的引用, 可以采用延迟检索
无论<class>元素的lazy属性是true还是false,Session 的get()方法及Query的list()方法在类级别总是使用立即检索策略
若 <class>元素的 lazy 属性为 true 或取默认值,Session 的 load()方法不会执行查询数据表的SELECT语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:
由Hibernate在运行时采用javassist工具动态生成
Hibernate创建代理类实例时,仅初始化其 OID属性
在应用程序第一次访问代理类实例的非OID属性时,Hibernate 会初始化代理类实例
关联级别的检索策略
在映射文件中, 用 <set>元素来配置一对多关联及多对多关联关系.<set> 元素有lazy 和 fetch属性
lazy: 主要决定orders集合被初始化的时机. 即到底是在加载Customer对象时就被初始化, 还是在程序访问orders集合时被初始化
fetch: 取值为“select”或“subselect”时,决定初始化orders的查询语句的形式; 若取值为”join”,则决定orders集合被初始化的时机
若把fetch设置为“join”,lazy 属性将被忽略
从一的一方关联多的一方:
* <set>
* fetch:控制sql语句的类型
* join :发送迫切左外连接的SQL查询关联对象.fetch=”join”那么lazy被忽略了.
* select :默认值,发送多条SQL查询关联对象.
* subselect :发送子查询查询关联对象.(需要使用Query接口测试)
* lazy:控制关联对象的检索是否采用延迟.
* true :默认值, 查询关联对象的时候使用延迟检索
* false :查询关联对象的时候不使用延迟检索.
* extra :及其懒惰.
***** 如果fetch是join的情况,lazy属性将会忽略.
@Test
/*
* <set>没有配置fetch 和 lazy情况
*/
public void demo2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = (Customer) session.get(Customer.class, 1);// 发送查询客户的SQL.
System.out.println(customer.getOrders().size());// 又发送一条SQL 去查询客户的关联的订单
tx.commit();
session.close();
}
@Test
/*
* <set>配置fetch="join" lazy就会被忽略!!!
* * 发送迫切左外连接查询两个表.
*/
public void demo3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 直接发送一条迫切左外连接
/*
* select
customer0_.cid as cid0_1_,
customer0_.cname as cname0_1_,
orders1_.cno as cno0_3_,
orders1_.oid as oid3_,
orders1_.oid as oid1_0_,
orders1_.addr as addr1_0_,
orders1_.cno as cno1_0_
from
customer customer0_
left outer join
orders orders1_
on customer0_.cid=orders1_.cno
where
customer0_.cid=?
*/
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getOrders().size());
tx.commit();
session.close();
}
@Test
/*
* 在<set>集合上配置
* * fetch="select" lazy="true"
* * lazy:true-使用延迟检索
* * 发送多条SQL,查询关联对象
*/
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 发送一条只查询客户的SQL
Customer customer = (Customer) session.get(Customer.class, 1);
// 使用订单的时候又发送一条查询这个客户的订单的SQL
System.out.println(customer.getOrders().size());
tx.commit();
session.close();
}
@Test
/*
* 在<set>集合上配置
* * fetch="select" lazy="false"
* * lazy:false:关联对象的检索不使用延迟
*/
public void demo5(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 发送多条SQL,查询关联对象.
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getOrders().size());
tx.commit();
session.close();
}
@Test
/*
* 在<set>集合上配置
* * fetch="select" lazy="extra"
* * lazy:extra及其懒惰.要订单的数量
*/
public void demo6(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = (Customer) session.get(Customer.class, 1);
// select count(*) from orders where cno = ?;(只统计数量,其他不弄)
System.out.println(customer.getOrders().size());
// 发送查询订单的SQL
for (Order order : customer.getOrders()) {
System.out.println(order);
}
tx.commit();
session.close();
}
@Test
/*
* 在<set>集合上配置
* * fetch="subselect" lazy="true"
* * 使用subselect的时候 需要使用 query接口进行测试.
* * 查询一个客户 查询多个客户.
* 如果有多个客户:
* select * from orders where cno in (1,2,3);
* 如果只有一个客户:(subselect看出效果,跟select一样)
* select * from orders where cno = 1;
*/
public void demo7(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
List<Customer> list = session.createQuery("from Customer").list();
for (Customer customer : list) {
System.out.println(customer.getOrders().size());
}
tx.commit();
session.close();
}
延迟检索和增强延迟检索
在延迟检索(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() 方法会忽略映射文件中配置的迫切左外连接检索策略, 而依旧采用立即检索还是延迟加载策略由set集合的lazy属性决定
在多的一方关联一的一方:
<many-to-one> 元素也有一个 lazy 属性和 fetch 属性.
* <many-to-one>
* fetch:控制SQL语句发送格式
* join :发送一个迫切左外连接查询关联对象.fetch=”join”,lay属性会被忽略.
* select :发送多条SQL检索关联对象.
* lazy:关联对象检索的时候,是否采用延迟
* false :不延迟
* proxy :使用代理.检索订单额时候,是否马上检索客户由Customer对象的映射文件中<class>上lazy属性来决定.
* no-proxy :不使用代理
在测试select和proxy的时候,需要在Order.hbm.xml中设置
<class name=“cn.itcast.n_many2oneseach.Order”table=“orders”lazy=“false”>
和 <set>一样,<many-to-one> 元素也有一个lazy 属性和 fetch属性.
若fetch属性设为join,那么lazy属性被忽略
迫切左外连接检索策略的优点在于比立即检索策略使用的SELECT语句更少.
无代理延迟检索需要增强持久化类的字节码才能实现
Query 的list 方法会忽略映射文件配置的迫切左外连接检索策略, 而采用延迟检索或立即检索策略,根据customer类级别的lazy属性 lazy=true为延迟检索,laze=false为立即检索
如果在关联级别使用了延迟加载或立即加载检索策略,可以设定批量检索的大小, 以帮助提高延迟检索或立即检索的运行性能.
<set>元素有一个batch-size属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少SELECT语句的数目, 提高延迟检索或立即检索的运行性能. 默认值是1
注:query.list()属于hql检索,hql检索忽略关联级别的迫切左外连接检索,只与lazy属性有关.
没有在<many-to-one>标签上配置:
* 发送多条SQL进行查询.
// 只会发送一条查询订单SQL.
Order order = (Order) session.get(Order.class, 1);
// 使用订单的客户对象的时候,又发送一条SQL查询订单关联的客户
System.out.println(order.getCustomer().getCname());
在<many-to-one>标签上配置:
* fetch="join" lazy="被忽略"
* 发送迫切左外连接
// 发送一条迫切左外连接.查询关联对象.
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getCustomer().getCname());
在<many-to-one>标签上配置:
* fetch="select" lazy="false"
* 发送多条SQL
// 在这行发送多条SQL 查询关联对象.
Order order = (Order) session.get(Order.class, 1); //立即检索,不延时
System.out.println(order.getCustomer().getCname());
在<many-to-one>标签上配置:
* fetch="select" lazy="proxy"使用代理.检索订单额时候,是否马上检索客户由Customer对象的映射文件中<class>上lazy属性来决定.
* 发送多条SQL
// 在这行发送多条SQL 查询关联对象.
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getCustomer().getCname());
@Test
/*
* 批量抓取
* 在客户一端配置
* <set>集合上配置batch-size="2"
<set name="orders" cascade="save-update" batch-size="2">
public void demo12(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
List<Customer> list = session.createQuery("from Customer").list();//查询所有客户
for (Customer customer : list) {
for (Order order : customer.getOrders()) {
System.out.println(order.getAddr());
}
}
tx.commit();
session.close();
}
@Test
/*
* 批量抓取
* 通过订单批量抓取客户:
* 需要在客户一端<class>标签上配置batch-size
<class name="cn.itcast.vo.Customer" batch-size="2" table="customer" lazy="true">
public void demo13(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
List<Order> list = session.createQuery("from Order").list();
for (Order order : list) {
System.out.println(order.getCustomer().getCname());
}
tx.commit();
session.close();
}
1.4 Hibernate的事务处理:
事务:
* 事务就是逻辑上的一组操作,要么全都成功,要么全都失败!!!
事务特性:
* 原子性:事务一组操作不可分割.
* 一致性:事务的执行前后,数据完整性要保持一致.
* 隔离性:一个事务在执行的过程中不应该受到其他事务的干扰.
* 持久性:一旦事务结束,数据就永久保存数据库.
如果不考虑事务的隔离性,引发一些安全性问题:
* 5大类问题:3类读问题 2类写问题.
* 读问题:
* 脏读 :一个事务读到另一个事务未提交数据.
* 不可重复读 :一个事务读到另一个事务已经提交数据(update),导致查询结果不一致.
* 虚读 :一个事务读到另一个事务已经提交的数据(insert),导致查询结果不一致
* 避免三种读的问题:
* 设置事务的隔离级别:
* 未提交读:以上三种读问题 都有可能发生.
* 已提交读:避免脏读,但是不可重复读和虚读有可能发生.
* 重复读:避免脏读和不可重复读,但是虚读是有可能发生.
* 串行的:可以避免以上三种读问题.
ANSI 事务隔离级别
ANSI SQL 标准定义了隔离级别,但并不是SQL数据库独有.JTA也定义了同样的隔离级别.级别越高,成本越高
* 在Hibernate中设置事务的隔离级别:
设置隔离级别
每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通过数据库配置设置,也可在应用程序中设置.例如Hibernate:
hibernate.connection.isolation= 4
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
注意:*Hibernate不可能改变在受管环境下由应用服务器提供的数据库连接的隔离级别,只能通过改变应用服务器配置的方式来改变.
* 设置隔离级别是全局选项,会影响所有的连接和事务.有时需要为某个特定事务指定更多的限制.
* Hibernate依赖于乐观的并发控制,使用版本检查和悲观锁实现附加的锁支持(了解).
* 在核心配置文件中:
<property name="hibernate.connection.isolation">4</property>
* 写问题:丢失更新
* 解决;
* 悲观锁:(排它锁)(数据库的一种锁机制,A结束后,B才能工作)(数据库中for update:排它锁)
* 乐观锁;(加一字段,每次操作自动增加,A要修改,匹对一致,修改,不一致,不修改)乐观锁包括版本控制和时间戳
乐观锁
<version name="version"/>//乐观锁,version:属性private Integer version;
悲观锁(拍他锁)
Customer customer = (Customer) session.get(Customer.class, 3, LockMode.UPGRADE);//已过时
管理session
线程绑定的session:
在hibernate.cfg.xml文件中增加
<!-- 配置session的线程本地化threadLocal-->
<property name=“hibernate.current_session_context_class">thread</property>
* 不是调用sessionFactory.openSession().而是调用sessionFactory.getCurrentSession().获取session对象.从当前的线程提取session,
* 当前线程如果存在session对象,取出直接使用
* 当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定
* 底层就是ThreadLocal.
***** 当前线程中的session不需要进行关闭,线程结束后自动关闭!!!
@Test
/*
* 事务通常在service层开启.session在DAO层.
* * 事务开启由session开启.
*/
public void demo7(){
Session session1 = HibernateUtils.openSession();
Session session2 = HibernateUtils.openSession();
System.out.println(session1 == session2);//false
Session session3 = HibernateUtils.getCurrentSession();
Session session4 = HibernateUtils.getCurrentSession();
System.out.println(session3 == session4);//true
}
@Test
/*
* 当前线程中绑定的session的使用
*/
public void demo8(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCname("张三");
customer.setAge(28);
session.save(customer);
tx.commit();
// session.close();//不用关闭,会报异常
}