培训第2个月的第1天----Hibernate初识(3)

           今天可以说,很开心,因为今天觉得弄懂了一些东西,嘻嘻嘻嘻嘻。

           现在来说一下今天的内容: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所对应的对象,那么在查询时就没有查到。

猜你喜欢

转载自blog.csdn.net/qq_41160264/article/details/81748207