Session接口是Hibernate向程序提供操纵数据库的最主要接口,它提供了基本的保存、更新、删除和查询方法。它有一个缓存,保存了持久化对象,当清理缓存时,按照这些持久化对象同步更新数据库
JAVA对象在JVM中的生命周期
就用new语句创建一个对象是,JAVA虚拟机会为这个对象分配内存空间,只要这个对象不被任何引用变量引用,它就结束生命周期.如:
//创建一个Customer对象和两个Order对象,定义三个引用变量c、o1、o2
Customer c=new Customer(“Tom”,new HashSet());
Order o1=new Order(“Tom_ 001”,null);
Order o2=new Order(“Tom_002”,null)
//建立关联关系
o1.setCustomer(c);
c.getOrders().add(o1);
//c、o1、o2置null,不再引用对象,
o1=null;//第一个Order不被o1引用,但它被Customer引用
o2=null;//这个对象(Order o2=new Order(“Tom_002”,null))结束生命
c=null;//因为关联第一个Order到现在才结束生命
理解Session的缓存
缓存是一个内存空间,这个空间放一些JAVA集合,这些集合就是Session的缓存,如:
private final Map entitiesByKey;
entitiesByKey.put(key,object);//向Session的缓存加入一个持久化对象
entitiesByKey.romove(key);//从Session缓存删除一个持久化对象
对象被加入缓存中,即使不被程序的变量引用,只要缓存不被清空,或被清空,但程序中有变量引用该对象,对象仍然存在,在以后的检索中,是先从缓存检索,没有才去数据库中检索.如:
Customer c1=new Customer(“Tom”,new HashSet());
session.save(c1);//持久化对象,加入到缓存
Long id=c1.getId();
c1=null;//不引用对象了,但对象仍存在缓存
Customer c2=(Customer)session.load(Customer.class,id);//从缓存中读取对象
tx.commit();
session.close();//关闭,清空缓存
System.out.println(c2.getName());//清空缓存,但对象还被引用,仍然在生命周期内
c2=null;//对象生命结束
Session缓存的作用:
1. 可以从缓存中读取数据,减少访问数据库的频率,提高性能
2. 保证缓存中对象和数据库的相关记录一致.当缓存中对象数据改变了,Session能够把几条相关的SQL语句合并为一条执行,减少访问数据库的次数
3. 当缓存中持久化对象之间存在循环关联关系时,Session保证不出现访问对象图的死循环
Session在清理缓存时,才执行SQL语句
Session会在下面的时间点清理缓存
1. 调用net.sf.hibernate.Transaction.commit()方法,先清理缓存,然后提交事务
2. 调用Session的find()或者iterate()时,如果持久化对象属性有变化就清理缓存
3. 显式调用Session的flush()
flush()和commit()方法的区别:flush()不会提交事务,commit()先调用flush() 方法,后提交事务,使更新被永久保存下来
Session的setFlushMode()方法用来设定清理缓存的时间点,方法如:
session.setFlushMode(FlushMode.COMMIT);
有三种清理模式:
清理缓存模式 | Session的查询方法 | Session的commit() | Session的flush() |
FlushMode.AUTO | 清理 | 清理 | 清理 |
*.COMMIT | 不清理 | 清理 | 清理 |
*.NEVER | 不清理 | 不清理 | 清理 |
其中*.AUTO为默认值.如果能肯定清理和不清理查询的结果一样,用*.COMMIT来提高性能.
什么时候用flush()方法?
1. 插入、删除、更新某个持久化对象时会引发数据库中的触发器,使用flush()方法让它马上执行语句,触发触发器
2. JDBC驱动程序不健壮,导致Hibernate自动清理无法正常工作
在Hibernate应用中JAVA对象的状态
对于需要被持久化的对象,在它生命周期中,有三个状态
1. 临时状态:刚刚用new语句创建,还没有被持久化,这不在Session的缓存中
2. 持久化状态:已经被持久化,在Sesson缓存中
3. 游离状态:已经被持久化,但不在Session缓存中
对象状态转换图:
临时对象的特征
1.不在Session的缓存中,不与任何Session实例关联
持久化对象的特征
1. 在Session的缓存中,总被一个Session实例关联,Session被清理时,会根据持久化对象属性的变化来同步更新数据库,当一个持久化对象关联一个临时对象,在允许级联保存的情况下,Session在清理缓存时把这个临时对象也转变为持久化对象.一个Session实例对应一个缓存,在同一个缓存中数据库表的记录只对应(只有)一个持久化对象.同一个持久化对象可以被多个Session实例关联(共用),如:
Session session1=sessionFactory.openSession();
Session session2=sessionFactory.openSession();
Transaction tx1=session1.beginTransaction();
Transaction tx1=session1.beginTransaction();
Customer c=(Customer)session1.load(Customer.class,new Long(1));
session2.update(c);//Customer对象被加入到session2缓存,
c.setName(“Jack”);
tx1.commit();//执行update语句
tx2.commit();//执行同样的update语句,因为共用一个持久化对象
session1.close();//应避免一个对象同时被多个Session实例关联
session2.close();
游离对象的特征
不在位于缓存中,不被Sesion关联
Session的保存、更新、删除和查询方法
Session的save()方法
Customer customer =new Customer();
customer.setName(“Tom”);
Session session=sessionFactory.openSession();
Transaction tx=session.beginTransaction();
session.save(customer);//对象加入缓存中,变成持久化对象, 然后根据生成器分配OID
//如果要程序为对象指定OID,调用如:save(customer,new Long(9))重载方法
customer.setName(“Jack”); //修改属性
tx.commit();//清理缓存才执行一条insert语句和一条update语句
session.close();
Session的update()方法 .使用方法如:
Customer customer =new Customer();
customer.setName(“Tom”);
Session session1=sessionFactory.openSession();
Transaction tx1=session.beginTransaction();
session.save(customer);//对象加入缓存中,变成持久化对象, 然后根据生成器分配OID
tx.commit();//清理缓存才执行一条insert语句
session.close();//customer对象变为游离状态
Session session2=sessionFactory.openSession();
Transaction tx2=session.beginTransaction();
customer.setName(“Jack”); //修改属性
session2.update(customer);//游离状态变为持久化状态
customer.setName(“Linda”); //又修改属性
tx2.commit();//清理缓存,只执行一条update语句(多条update变成一条来执行)
session2.close();
注意:
(1)如果希望Session在Customer对象属性有所改变,才执行update,可以把映射文件<class>元素的select-befor-update设为true,让在执行update前先执行select,查看属性是否有变化,有变化才执行update
(2)update()关联一个游离对象时,如果缓存存在相同OID的同类的持久化对象,会抛出异常;或者数据库不存在相应的记录,也会抛出异常
Session的saveOrUpdate()方法
saveOrUpdate()方法包含save()和update()功能,如果传入的参数是临时对象(没有保存过)就调用save()方法;如果传入的参数是游离对象,就调用update()方法(因为已经保存过了);如果是持久化对象,就直接返回(清理缓存时会同步更新数据库).如果满足以下情况之一,Hibernate把它作为临时对象
1. JAVA对象的OID为null
2. 映射文件中<id>元素的unsaved-value(未保存过的值)属性值和OID一致
3. JAVA对象具有version属性并且值为null
4. 在映射文件中为version属性设置了unsaved-value属性,并且version属性值和unsaved-value属性值一致
如果JAVA对象的id属性为java.lang.Long类型,默认为null,故new出来的对象一定是临时对象;如果为long类型,默认为0,这就要显式设置<id>元素的unsaved-value属性,它默认为null;
<id name=”id” column=”ID” unsaved-value=”0”>
<generator class=”increment”/>
</id>
Session的load()和get()方法
load()和get()方法都能根据给定的OID从数据库中加载一个持久化对象,它们的区别在于:当数据库不存在与OID对应的记录时,load()方法抛出异常,而get()方法返回null
Session的merge()方法
merge()方法能够把一个游离的对象的属性复制到一个持久化对象中。当Session用update()方法关联一个游离对象时,如果Session缓存中已经存在一个同类型并且OID相同的持久化对象,那么update()方法会抛出NonUniqueException
//创建UserInfo实例
UserInfo userInfo = new UserInfo(); //使之成为游离状态
userInfo.setId(11112);
userInfo.setName("RW3");
userInfo.setSex("M");
//创建UserInfo实例
UserInfo userInfo2 = new UserInfo(); //使之成为游离状态
userInfo2.setId(11112);
userInfo2.setName("RW4");
userInfo2.setSex("F");
//启动Session
Session session = HibernateSessionFactory.currentSession();
//启动事务
Transaction tx = session.beginTransaction();
//调用merge方法,此时UserInfo实体状态并没有被持久化
session.merge(userInfo);
//调用merge方法,此时UserInfo实体状态并没有被持久化
//但是数据库中的记录被更新了
①session.merge(userInfo2);
//merge方法与update方法的差别在于针对同样的操作update方法会报错
//原因在于update方法使得实体状态成为了持久化状态,而Session中不允许两个持久化实体有同样的持久化标识
②//session.update(userInfo);
//session.update(userInfo2);
//以下两句不会发送SQL,因为userInfo2不是持久化状态的实体
③userInfo2.setName("RW5");
userInfo2.setSex("M");
//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
Session的delete()方法
delete()方法用于从数据库中删除与JAVA对象对应的记录.如果传入的参数是持久化对象,在清理缓存时会执行delete语句,当调用close()方法时,再从缓存中删除对象;如果传入的是游离对象,它先变成持久化对象,再操作.如果是希望删除多个对象,可以使用另一种重载形式的delete()方法,如:
session.delete(“form Customer as c where c.id>8”);
级联操纵对象
实际应用,对象与对象之间相互关联.因此缓存中存放的是一幅相互关联的对象图.例如以下代码看似
只加载了一个Category(种类)对象:
Category appleCategory=(Category)session.load(Category.class,new Long(2));
实际上,Session加载了所有和appleCategrory直接关联或间接关联的Category对象,见图:
这样就可以通过getParentCategory()方法导航到父类的Categroy对象,通过getChildCategories().iterator().next()导航到子类别的Category对象(也可以说是把因主外键关联与形成的图的对象都加载,它的一条记录相当于用多表连接查询结果一样).那么如何操纵与当前对象关联的其他对象?
在映射文件中,在<set>、<many-to-one>和<one-to-one>等元素中设置cascade(层叠)属性。
cascade描述如下:
cascade属性值 | 描述 |
none(决不) | 在保存、更新或删除当前对象时,忽略其他关联对象。这是默认值 |
save-update | 使用save(),update()或saveOrUpdate()方法时,级联保存或更新对象 |
delete | 使用delete()方法时,级联删除所有的关联对象 |
all | 使用save(),update(),saveOrUpdate(),evict(),lock()或delete()时,级联... |
delete-orphan | 删除所有和当前对象解除关联的对象 |
all-delete-orphan | 包含all和delete-orphan的行为. |
例子:
<set name=”childCategories” cascade=”save-update” inverse=”true”>
<key column=”CATEGORY_ID”/>
<one-to-many class=”mypack.Category”/>
</set>
保存或更新当前Category对象时,会调用getChildCategories()方法,导航到所有子类别Category对象,然后级联保存或更新
1 级联保存临时对象
Category foodCategory=new Category(“food”,null,new HashSet());
Category fruitCategory=new Category(“fruit”,null,new HashSet());
Category appleCategory=new Category(“apple”,null,new HashSet());
//建立关联
foodCategory.addchildCategory(fruitCategory);
fruitCategory.addchildCategory(appleCategory);
saveOrUpdate(foodCategory);//级联保存三个临时对象,
2 更新持久化对象
tx=session.beginTransaction();
Category fruitCategory=findCategoryByName(session,”fruit”);//得到持久化对象
Category orangeCategory=new Category(“orange”,null,new HashSet());
fruitCategory.addchildCategory(orangeCategory);//关联临时对象
tx.commit();//更新持久化对象,持久化orangeCategory对象,不是级联
3 持久化临时对象
Category foodCatygory=findCategoryByName(“food”);//不同Session缓存,foodCategory对//于这个Session缓存是一个游离对象
Category vegetableCategory=new Category(“vegetable”,null,new HashSet());//临时对象
foodCategory.addChildCategory(vegetableCategory);//关联对象
saveOrUpdate(vegetableCategory);//持久化vegetableCategory,会级联更新foodCategor、fruitCategory对象(遍历关联图中所有对象,并更新它们).如果有1000个Category对象,Session就会多执行1000条update语句(这些update语句执不执行数据库的数据不变,因为它们没有数据变化),解方法是把<many-to-one>元素的cascade属性设为”none”,就只更新子类,不更新父类和与父类关联的其它对象
4 更新游离对象
//得到游离对象,因为不同Session缓存不同,不在我这个缓存而又不用new就是游离对象
Category vegetableCategory=findCategoryByName(“vegetable”);
vegetableCategory.setName(“green vegetable”);
Category tomatoCategory=new Category(“tomato”,null,new HashSet());
vegetableCategory.addChildCategory(tomatoCategory);//关联对象
saveOrUpdate(vegetableCategory);//因为<set>和<many-to-one>元素的cascade属性都为”save-update’会级联更新vegetableCategory的子类和父类,使执行多余的update语句,解决方法是把<many-to-one>的cascade 属性设为”none”只更新自已的子类
遍历对象图
以下章的最终图为:
是一个递归算法,用来遍历以上的图略