理解hibernat的flush(转)
*SessionFlush:
*session flush方法主要做了两件事:
清理临时记录
执行sql
*session在什么情况下执行flush
默认在事务提交时(commit)
显示的调用flush
在执行查询前,如:iterate
hibernate按照save(insert),update、delete顺序提交相关操作
!!只要事务没有commit,就随时可以回滚 !!
所有状态都适用:
flush:清的是临时记录!!!!!
elementData临时数据,entryArray缓存数据:只有在save后才有数据!!与三个状态有关
调用commit的时候默认会先执行flush()
uuid:commit时的flush清理临时记录
save后:Hiernate就分配一个32的字符串,数据库中没有记录,elementData临时记录:有数据,entryArray缓存中:有记录,existsInDatabase:flase,纳入了session管理.
【如果此时执行:session.evict(user);//逐出缓存,清的是缓存中的记录,但临时集合没有清掉. 然后再执行commit,flush时发现临时集合有数据,就会发insert,然后找缓存记录,把existsInDatabase状态变为true,因为缓存清掉了(existsInDatabase也没了),所以有问题,抛异常】【上述问题的解决:save()完后执行flush(发sql,清临时集合,找缓存,改变esistsInDatabase状态),然后执行逐出,清理缓存,再执行commit,找临时集合有没数据,有的话发sql,已经flush了,所以没数据,不发sql】
flush后:清理临时集合,发sql,existsInDatabasse:true,临时集合没有数据了,但是由于隔离级别,MySQL数据库是查不到的。缓存中有记录。
commit后:数据不可回滚了,commit默认会先执行flush();所以不用显示调用
native:save清临时记录
save后:执行insert语句,清理临时记录。返回由数据库生成的id,纳入session管理因为缓存的map中有记录了,其中:id是由数据库返回的。临时数据中没记录(永远也看不到:save前没有save后也没有,只有save过程中才有,一旦发了sql临时记录中就没有记录了)
【此时执行session.evict(user),没问题,与uuid的区别!!!】
flush后:临时记录在save的时候已经清了
commit后:数据不可回滚了
不管什么策略:执行commit()默认会先执行flush(),commit()后数据无法回滚!!
assigned:flush情况和uuid相同,因为id new对象的时候给出了,不用数据库里取.
Uuid是Hibernate自动生成了,也不用数据库里取
public void testSave3(){
Session session = null;
Transaction tx = null;
try{
session = HibernateUtils.getSession();
tx = session.beginTransaction();
//Transient 瞬时状态
User3 user = new User3();
user.setId("0001");
user.setName("张三");
session.save(user);
user.setName("王五");
session.update(user);//此行可以不写,因为此时为持久状态,Hibernate会自动同步
User3 user3 = new User3();
user3.setId("002");
user3.setName("李四");
session.save(user3);
System.out.print("hell");
tx.commit();//此时发sql,hibernate按照save(insert),update、delete顺序提交相关操作,而上面的顺序是:保存、更新、保存。发的顺序发insert,insert,update。//如果在user.setName("王五");前先执行session.flush();则先发两条sql,然后最后commit的时候再发一条insert。所有,发sql的顺序按我们的意愿执行。
}catch(Exception e){
e.printStackTrace();
tx.rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
数据库隔离级别:解决并发性问题
隔离级别 是否存在脏读 是否存在不可重复读 是否存在幻读
Read Unommited(未提交读) Y Y Y
Read Commited(提交读,Oracle默认的) N Y 与悲观锁有关 Y
Repeatable Read(可重复读,MySql默认) N N Y
Serialiazble(序列化读) N N N
隔离级别低到高:MySQL隔离级别高于Oracle
序列化读:并发性最差,一个人独占!
脏读:没有提交数据就读到
不可重复读:第一次读出来为张三,再刷新一次为李四,悲观锁锁住就变成可重复读了
存在幻读:影响的是插入操作。A读出5条记录,刷新变成十条记录,新增了5条。
序列化读:锁住表,别人不可能添加数据。
查看mysql的隔离级别: select @@tx_isolation; 默认级别可重复读,必须提交了才可以看得到
改隔离级别为未提交读:set transaction isolation level read uncommitted
package com.guojie.hibernate;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.guojie.hibernate.HibernateUtils;
import com.guojie.hibernate.User1;
import com.guojie.hibernate.User2;
import com.guojie.hibernate.User3;
import junit.framework.TestCase;
public class SessionFlushTest extends TestCase {
/**
* 测试uuid主键生成策略
*/
public void testSave1() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("李四");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//调用flush,hibernate会清理缓存,执行sql
//如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
//并且session中existsInDatebase状态为true
session.flush();
//提交事务
//默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
//commit后数据是无法回滚的
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试native主键生成策略
*/
public void testSave2() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User2 user = new User2();
user.setName("张三1");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
//纳入了session的管理,修改了session中existsInDatebase状态为true
//如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
session.save(user);
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试uuid主键生成策略
*/
public void testSave3() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("王五");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后
//需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries
//中逐出了,所以找不到相关数据,无法更新,抛出异常
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试uuid主键生成策略
*/
public void testSave4() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("王五");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//flush后hibernate会清理临时集合,会将user对象保存到数据库中,将session中的insertions中的user对象
//清除,并且设置session中existsInDatebase的状态为true
session.flush();
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理临时集合时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试native主键生成策略
*/
public void testSave5() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User2 user = new User2();
user.setName("张三11");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
//纳入了session的管理,修改了session中existsInDatebase状态为true
//如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
session.save(user);
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试assigned主键生成策略
*
*/
public void testSave6() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User3 user = new User3();
user.setId("001");
user.setName("张三");
session.save(user);
user.setName("王五");
session.update(user);
User3 user3 = new User3();
user3.setId("002");
user3.setName("李四");
session.save(user3);
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?
//hibernate按照save(insert),update、delete顺序提交相关操作
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 测试assigned主键生成策略
*
*/
public void testSave7() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User3 user = new User3();
user.setId("003");
user.setName("张三");
session.save(user);
user.setName("王五");
session.update(user);
session.flush();
User3 user3 = new User3();
user3.setId("004");
user3.setName("李四");
session.save(user3);
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//因为我们在session.udpate(user)后执行了flush,所以在清理缓存时执行flush前的sql不会生成
//sql会按照我们的意愿执行
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}