今天可以说,很开心,因为今天觉得弄懂了一些东西,嘻嘻嘻嘻嘻。
现在来说一下今天的内容:1,hibernate的3种状态。2,flush()方法 和 clear()
方法的区别。3,load()方法和get()方法的区别。
一.hibernate中的3种状态
hibernate中的对象有3种状态,持久化分别是瞬时对象,对象和离线对象。那么怎么
区分对象的状态是这3种的一种呢?
1.对于刚创建的一个对象,如果session缓存中和数据库中都不存在该对象,那么该
对象就是瞬时对象(Transient)。
2.瞬时对象调用save方法,或者离线对象调用update方法可以使该对象变成持久化
对象,如果对象是持久化对象时, 那么对该对象的任何修改,都会在提交事务时才会与之
进行比较,如果不同,则发送一条update语句,否则就不会发送语句。如果调用load(),
get()方法,那么查出来的数据就已经是持久化对象。
3.离线对象就是,数据库存在该对象,但是该对象又没有被session所托管。
这里我就以代码的形式来展示三种状态的区别,如果能看出每个例子中要输出的sql
语句数量,那么就说明差不多了解这3种状态了。其中我想要说的话和规律也都在代码中注
释了,所以在代码外我就不多说,请看代码:
package com.java.Text;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.java.Bean.User;
import com.java.Utils.HibernateSessionFactory_i;
//Hibernate的三种状态
public class Text {
public static void main(String[] args) {
Text text=new Text();
// text.text_Persistent1();
// text.text_Persistent2();
// text.text_Persistent3();
// text.text_Persistent4();
// text.text_Persistent5();
// text.text_Detached1();
// text.text_Detached2();
text.text_Detached3();
}
public void text_Persistent1() {
//创建与数据库的关联对象session
Session session=HibernateSessionFactory_i.getSession();
//开启事务
Transaction transaction=session.beginTransaction();
//创建一个瞬时对象(Transient)
User user=new User();
user.setUserId("21");
user.setUserName("qiao");
user.setUserPassword("ampere");
/*到目前为止,user仍然是一个瞬时对象,因为数据库中没有这个20的主键,并且user
*还没有交给session托管,session缓存中还没有user这个对象
* */
session.save(user);
/*当session调用save方法时,那么user就交给session托管,
*并且session缓存中有user对象,当提交时,user就被持久化到数据库中,
*如果主键已经存在了,则会爆异常
*Caused by: java.sql.BatchUpdateException: Duplicate entry '21' for key 'PRIMARY'
* */
session.getTransaction().commit();
/*输出的sql文为一条:Hibernate: insert into t_user
* (userpassword, username, usersex, userrole, usercollege,
* usermajor, userclass, dormitoryId, userid) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
* */
}
public void text_Persistent2() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=new User();
user.setUserId("22");
user.setUserName("qiao");
user.setUserPassword("ampere");
session.save(user);
/*到目前为止,user是一个Persistent(持久化对象)
*因为它执行了save方法,在session内存中有了user
*对象
* */
user.setUserName("aiya");
/*此时重新设置了userName,当提交的时候,由于user被session代理,user
*对象存在于session缓存中,所以就会直接执行更新语句
* */
transaction.commit();
/*事务到底是怎么提交的呢?
*先看事务到底是rollback还是commit,如果是rollback,那么就不执行当前transaction
*中的代码,如果提交就执行transaction中的代码。
* */
/*输出的sql文为两条:Hibernate: insert into t_user (userpassword, username, usersex,
* userrole, usercollege, usermajor, userclass, dormitoryId, userid) values (?,
* ?, ?, ?, ?, ?, ?, ?, ?)
* Hibernate: update t_user set userpassword=?, username=?, usersex=?,
* userrole=?, usercollege=?, usermajor=?, userclass=?, dormitoryId=?
* where userid=?
* */
}
public void text_Persistent3() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=new User();
user.setUserId("23");
user.setUserName("qiaoya");
user.setUserPassword("ampere");
session.save(user);
/*如果提交事务,那么到现在user就从瞬时状态变为持久化对象
* */
user.setUserName("111");
session.save(user);
/*如果一个对象以及是持久化状态了,那么此时对该对象进行各种修改,或者调用多次update、save方法时,
*hibernate都不会发送sql语句,只有当事物提交的时候,此时hibernate才会拿当前这个对象与之前
*保存在session中的持久化对象进行比较,如果不相同就发送一条update的sql语句,否则就不会发送update语句
* */
user.setUserName("123");
session.update(user);
session.getTransaction().commit();
/*输出的sql文为两条:
* Hibernate: insert into t_user (userpassword, username, usersex, userrole, usercollege,
* usermajor, userclass, dormitoryId, userid) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update t_user set userpassword=?, username=?, usersex=?, userrole=?, usercollege=?,
usermajor=?, userclass=?, dormitoryId=? where userid=?
* */
}
public void text_Persistent4() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=(User)session.load(User.class,"23");
/*当session调用load、get方法时,此时如果数据库中有该对象,则该对象也变成了一个持久化对象,被session所托管.
* */
user.setUserPassword("222");
transaction.commit();
/*当设置的密码属性值和session缓存中的一样是,就不会向数据库发送sql语句,如果不一致,则会发出update语句,
*所以这个方法会发送两条sql语句,一个是select,一个是update。
* */
}
public void text_Persistent5() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=(User)session.load(User.class,"23");
user.setUserPassword("333");
session.clear();
transaction.commit();
/*在数据库进行提交的时候,如果对象已经被session代理,那么在提交时,就会拿这个代理对象和(数据库中的对比),如果不同
*则发送update语句,而这个在提交的时候,发现缓存中已经没有对象了,那么也就没法和数据库中的进行对比,也就不能发送update
*sql语句。所以这个方法只有一个select语句。
* */
}
public void text_Detached1() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=new User();
user.setUserId("23");
session.update(user);
/*代码到这里,update方法会将一个离线对象变为持久化对象,这里不用save()方法是因为sava方法是保存的方法,会出现主键重复异常。
* */
user.setUserName("wahahah");
session.update(user);
transaction.commit();
/*这里要注意的是,将离线状态通过update变为持久化状态时,session缓存中只是保存了对象的主键,没有对象其他的属性值。
*在提交更新时,不仅会更改要修改的值,还会将没修改的值置为空。
*并且我们可以修改属性值,但是不能修改主键ID值,如果修改,会发生异常。
* */
}
public void text_Detached2() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=new User();
user.setUserId("22");
//session.update(user); 将持久转换为瞬时状态
session.delete(user);
user.setUserName("111");
transaction.commit();
/*delete()方法将user从离线对象变为瞬时对象,user不再有session来管理,session内存中也没有user对象
*所以之后在对user进行修改,也不会发送sql语句。
* */
}
public void text_Detached3() {
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction=session.beginTransaction();
User user=(User)session.load(User.class, "21");
User user2=new User();
user2.setUserId("21");
user2.setUserName("hahaha");
/*此时u2将会变成持久化状态,在session的缓存中就存在了两份同样的对象,在session中不能存在两份拷贝,否则会抛出异常
* */
session.merge(user2); //如果有相同的对象,则合并
transaction.commit();
}
}
。。。。。。。。
二.session的flush()和clear()方法
首先session是有一级缓存的,目的是为了减少查询数据库的时间,提高效率,一
级缓存的生命周期和session是一样的,session.flush()和session.clear()就针对session的
一级缓存的处理。
1.session.flush()的作用就是将session的缓存中的数据与数据库同步。
2.session.clear()的作用就是清除session中的缓存数据(不管缓存与数据
库的同步)。
package com.java.Text;
import org.hibernate.Session;
import com.java.Bean.User;
import com.java.Utils.HibernateSessionFactory_i;
public class Text2 {
public static void main(String[] args) {
Text2 text=new Text2();
text.text_fulsh();
}
public void text_fulsh() {
Session session=HibernateSessionFactory_i.getSession();
session.beginTransaction();
User user=new User();
user.setUserId("20");
session.update(user);
user.setUserName("11111");
session.flush();
session.clear();
//将缓存中的数据同步到数据库
User user2=(User)session.get(User.class,"20");
System.out.println(user2.getUserName()); //看看有没有同步到数据库
/*这个读取了数据库中没有提交的数据,是脏读
* */
user.setUserName("qiaoyaya");
session.getTransaction().rollback();
// System.out.println(user.getUserName());只是回滚的数据库中的,并没有回滚缓存中的数据
}
}
这个代码演示了flush()方法和clear()方法的作用。其中当执行session.flu
sh()方法时,将缓存中的user对象保存到了数据库中,但这时还没有提交事务。接下来
用clear()方法清除缓存,user对象在缓存中就不存在了,这时用get()方法查询数
据库发现,竟然读到了刚刚 flush()进数据库的数据 “111111”,但是数据明明回滚了,
为什么还会读到呢?这是因为读到了脏数据,也就是也就是读到了没有提交的数据。
进行事务回滚后,我们发现,我们打印的user.getUserName确实“11111”,这就说明,
回滚的是数据库中的数据,而缓存中的数据并没有回滚。
三.load()方法和get()方法的区别
简单来说:hibernate对于load方法认为该数据在数据库中一定存在,可以放
心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,
hibernate一定要获取到真实的数据,否则返回null。
1.load()方法:使用load()方法时会先查一下session缓存,看看该id对应的
对象是否存在,不存在则创建代理。创建的代理对只拥有 主键ID ,并没有其他属性值,所
以创建代理是非常快的。当用到主键ID对象的其他属性时,就在去数据库中查找对象的属
性是什么,如果没有,那么就会相应的抛出异常。
load()方法先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代
理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这
就是我们常说的 load在默认情况下支持延迟加载(lazy)。
2.get()方法:get在查询的时候首先到一级缓存(session级缓存)中查找,没
有的话进入二级缓存(介于内存与硬盘之间) 如果二级缓存还没有则直接进入数据库中查
询!查询到并将查询的结果放入二级缓存!查询不到不会报错。get()方法直接返回实体类,
如果查不到数据则返回null。
下面贴一下测试代码:
package com.java.Text;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.java.Bean.User;
import com.java.Utils.HibernateSessionFactory_i;
public class Text3 {
public static void main(String[] args) {
Text3 text=new Text3();
text.text3();
}
public void text3(){
Session session=HibernateSessionFactory_i.getSession();
Transaction transaction = session.beginTransaction();
User user=(User)session.get("com.java.Bean.User","20");
User user2=(User)session.load(User.class,"21");
session.close(); //会抛出异常,could not initialize proxy - the owning Session was closed
System.out.println(user);
System.out.println(user2);//当用到这个代理对象的属性时,才会去数据库中查
}
}
在来叙述一下:
load()的加载方式:当使用load方法来得到一个对象时,此时hibernate会
使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个
对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代
理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这
个时候才会发出sql语句,从数据库中去查询我们的对象。当我们访问user.getUerId时,
并不会生成sql语句查询数据库,但是在控制台会打印出userId,这是因为这个代理
对象里面有userId。但是一旦访问user.getUserName时,就会查询数据库,按照主键将
这个 user对象查询出来,即使只是想要输出System.out.println(user.getUserName); 查询
出来的也是一个完整的user对象,只不过在控制台输出的是 user.getUserName。
使用load()方法时,可能会抛出来个异常,一个是上面所说的could not initialize
proxy - theowning Session was closed;的异常,这是因为session是查询数据库的“接口”,
sessoin关闭之后,自然通过id就差不了对象了。另一中是ObjectNotFoundException这个
异常了,因为当数据库中没有id所对应的对象,那么在查询时就没有查到。