1.Hibernate的一级缓存的生命周期就是session的生命周期。
2.一级缓存存放的数据都是私有数据,不共享的。(因为session是存放在threadloacl中,不同的线程是不能访问的,所以保证了数据的安全性)
3.session.save(),update(),load(),get()方法会将数据库的数据存放在session一级的缓存中。
4.session.get()和load()可以获取到session一级缓存中的数据。
5.session.evict方法可以将一个指定缓存对象,从session的一级缓存中去除。
6.session.clear可以把session的一级缓存中的所有数据清空
7.session.refresh(obj)可以将指定对象数据从数据库更新到session的一级缓存中。
8.session.flush
在session的缓存内部,会去检查所有的持久化对象
1、如果一个持久化对象没有ID值,则会发出insert语句
2、如果一个持久化对象有ID值,则会根据这个ID去快照中ID和此ID一样的数据进行对比,如果其他一样,则什么都不做,如果其他不一样,则发出update语句
3、 检查所有的持久化对象是否有关联对象
检查关联对象的级联操作(cascade)
检查关联对象的关系操作(inverse)
9.在进行大批量操作的时候:
(1)当数据到某一个值的时候需要手动flush,否则内存会溢出
(2)session.flush方法并不会clear掉session中一级缓存的缓存数据,所以,在大批量操作的时候,手动flush后必须要紧跟手动clear操作。
public class One2ManyTest {
private static SessionFactory sessionFactory;
static{
Configuration con = new Configuration().configure();
sessionFactory = con.buildSessionFactory();
}
/**
* 测试load方法将数据放入一级缓存
* 只发出一条sql,从而可以得出一个结论:
* 当get操作的时候,hibernate将查询出来的数据放到了sesseion的一级缓存中,因为当第二次执行同样的查询操作的时候,是不会进行sql操作了,但还是能拿到数据
* Hibernate: select student0_.sid as sid1_0_, student0_.sname as sname1_0_, student0_.description as descript3_1_0_, student0_.cid as cid1_0_ from Student student0_ where student0_.sid=?
*/
@Test
public void testLoad(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//由这句话发出sql语句
Student stu = (Student) session.load(Student.class, 1L);
System.out.println(stu.getSname());
stu = (Student) session.load(Student.class, 1L);
transaction.commit();
//不需要seesion.close();因为是从当前线程获取到的session,它会在事务提交的时候自动关闭session
}
/**
* 测试get方法将数据放入一级缓存
* 只发出一条sql,从而可以得出一个结论:
* 当load操作的时候,hibernate将查询出来的数据放到了sesseion的一级缓存中,因为当第二次执行同样的查询操作的时候,是不会进行sql操作了,但还是能拿到数据
* Hibernate: select student0_.sid as sid1_0_, student0_.sname as sname1_0_, student0_.description as descript3_1_0_, student0_.cid as cid1_0_ from Student student0_ where student0_.sid=?
*/
@Test
public void testGet(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//由这句话发出sql语句
Student stu = (Student) session.get(Student.class, 1L);
System.out.println(stu.getSname());
stu = (Student) session.get(Student.class, 1L);
transaction.commit();
//不需要seesion.close();因为是从当前线程获取到的session,它会在事务提交的时候自动关闭session
}
/**
* 测试save方法将数据放入一级缓存
* 先保存一条数据,然后将刚才保存的数据查询出来,如果发生了查询的sql,那就表示数据没进缓存,反之,则代表数据存入了缓存
*/
@Test
public void testSave(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student s = new Student();
s.setSname("save学生");
s.setDescription("save");
session.save(s);
/*
* 注意:
* 如果这里s = (Student) session.load(Student.class, 1L);
* 还是会发出sql语句进行查询操作
*/
s = (Student) session.load(Student.class, s.getSid());
System.out.println(s.getSname());
transaction.commit();
}
/**
* 测试update方法将数据放入一级缓存(用evict将对象从session的一级缓存中情况)
* 先查询条数据(由于查询的时候会将查询的结果放入到session的一级缓存中),手动将查询出来的对象从session的一级缓存中删除
* 将查询出来的对象进行update操作,再进行次update那个对象的查询操作,如果此时没有发出sql语句,则表示update操作将对象放入了session的一级缓存中
* Hibernate: select student0_.sid as sid1_0_, student0_.sname as sname1_0_, student0_.description as descript3_1_0_, student0_.cid as cid1_0_ from Student student0_ where student0_.sid=?
* Hibernate: update Student set sname=?, description=?, cid=? where sid=?
*/
@Test
public void testUpdate_evict(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//由于get方法会将查询出来的数据放在session的一级缓存里,所以我们要清除这个对象
Student s = (Student) session.get(Student.class, 1L);
s.setDescription("改变");
session.evict(s);//student对象从session缓存中清空了
session.update(s);//update操作将student对象放入了session的一级缓存中
s = (Student) session.get(Student.class, 1L);//这里就没有进行查询的sql操作
System.out.println(s.getDescription());
transaction.commit();//由于之前清空了一级缓存,session.update(s);操作的时候,发现缓存没有可以对比的对象,所以这里会进行update的sql操作
}
/**
* 测试update方法将数据放入一级缓存(用clear将session的一级缓存中所有缓存的对象清空)
* 先查询条数据(由于查询的时候会将查询的结果放入到session的一级缓存中),手动将查询出来的对象从session的一级缓存中删除
* 将查询出来的对象进行update操作,再进行次update那个对象的查询操作,如果此时没有发出sql语句,则表示update操作将对象放入了session的一级缓存中
*
* Hibernate: select student0_.sid as sid1_0_, student0_.sname as sname1_0_, student0_.description as descript3_1_0_, student0_.cid as cid1_0_ from Student student0_ where student0_.sid=?
* Hibernate: update Student set sname=?, description=?, cid=? where sid=?
*/
@Test
public void testUpdate_clear(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//由于get方法会将查询出来的数据放在session的一级缓存里,所以我们要清除这个对象
Student s = (Student) session.get(Student.class, 1L);
s.setDescription("改变");
session.clear();
session.update(s);//update操作将student对象放入了session的一级缓存中
s = (Student) session.get(Student.class, 1L);//这里就没有进行查询的sql操作
System.out.println(s.getDescription());
transaction.commit();//由于之前清空了一级缓存,session.update(s);操作的时候,发现缓存没有可以对比的对象,所以这里会进行update的sql操作
}
@Test
public void testEquality_OID_ERROR(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student s_get = (Student) session.get(Student.class, 1L);
s_get.setDescription("改变");
session.clear();
Student s_new = new Student();
s_new.setSid(1L);
s_new.setSname("new_stu");
s_new.setDescription("newStudent");
/*
* 注意:
* * 如果这里是save的话,由于主键生成策略是identity,所以hibernate会无视s_new.setSid(1L);而用指定的方式生成主键,所以不会在session中出现两个id相同的情况而出现以下异常
* * 如果这里是update的话,由于之前已经进行了session.get(Student.class, 1L);所以在session的一级缓存里是有这个对象的
* 此时将新new的一个对象的id设值为1L,并将其进行update操作,势必造成session的一级缓存中出现了两个ID相同的对象,hibernate是不允许这种情况出现的,所以会出以下异常
* org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.xxc.one2many_bothway.Student#1]
*
* 解决办法:
* *将之前load方法查询出来的student对象从session的一级缓存中清除 可以用session.evict(s);或session.clear();
*/
//在这句代码前,s_new是一个临时状态对象
session.update(s_new);
transaction.commit();//由于之前清空了一级缓存,session.update(s);操作的时候,发现缓存没有可以对比的对象,所以这里会进行update的sql操作
}
/**
* 将指定数据从数据库刷新到session的一级缓存中
*/
@Test
public void testRefresh(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student s_get = (Student) session.get(Student.class, 1L);
s_get.setDescription("哈哈");
session.refresh(s_get);
System.out.println(s_get.getDescription());
transaction.commit();
}
/**
* 将指定数据从一级缓存同步到数据路中
* 注意:
* session.flush()只是发出sql语句操作,但是并没有将缓存中的对象清空
*/
@Test
public void testFlush(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student s_get = (Student) session.get(Student.class, 1L);
s_get.setDescription("哈哈");
session.flush();//例如insert和update语句都是在这句代码执行的时候才发出的,而不是commit执行的时候发出
s_get = (Student) session.get(Student.class, 1L);//经过测试发现,flush并不回清空session中一级缓存的内容,因为这句代码没有发出sql语句
transaction.commit();
}
/**
* 大批量数据操作
*/
@Test
public void testBatch(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
/*这段话要是写在上边,实际保存的次数只有100000/100=1000次
* Student stu = new Student();
* stu.setSname("1");
* stu.setDescription("haha");
*/
for(int i=0;i<=100000;i++){
//如果保存的是同一个对象,对象的创建要写在下边,否则hibernate的session的一级缓存中已经有了该对象,
//所以不会每一次都执行保存操作,只有session.flush();session.clear();才会执行,所以以上这种写法只会保存1000次
Student stu = new Student();
stu.setSname("1");
stu.setDescription("haha");
session.save(stu);
//批量操作的时候,一定要这么来,到一定数量就刷新给数据库,否则内存要溢出
if(i%100==0){
session.flush();
session.clear();
}
}
//最后还要刷一次,否则可能因为总次数没能被100整除,剩下的数据没能被保存
session.flush();
session.clear();
transaction.commit();
}
/**
* 插入
*/
@Test
public void testSaveStudent_Cascade_SaveClass(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student stu = new Student();
stu.setSname("xxc1");
stu.setDescription("1");
Set<Student> stus = new HashSet<Student>();
stus.add(stu);
Class clzz = new Class();
clzz.setCname("浙江大学一院");
clzz.setDescription("1");
clzz.setStudents(stus);
//保存班级,级联保存学生
session.save(clzz);
transaction.commit();
session.close();
}
}