一、Hibernate说明:
首先说一下Hibernate吧,Hibernate是Java持久层框架之一,是一个开放源码的ORM框架,它在ORM框架中级别最高,是完全面向对象的。
那么什么是ORM框架呢?O--Object(对象)、R--Relation(关系)、M--Mapping(映射),简单的说就是在实体类和数据库表建立对应关系,避免去和复杂的sql语句打交道,通过映射关系,可以直接操作对象型的数据来代替操作数据库中的关系型数据,再着重说一下,Hibernate是完全面向对象的,在操作数据库的层面上,我们只需要用对象来操作数据库即可,与数据库中的表、字段就完全脱离开来。
二、Hibernate对象的三种状态
进入正题,Hibernate的对象共有三种状态:瞬时态、持久态、游离态(托管态)。
瞬时态:新new出来的对象,就属于瞬时态,此时session和数据库中都不存在该对象。
持久态:对象做持久化操作的时候,比如调用session.save()方法时,对象由瞬时态变为持久态,此时session和数据库中都存在。
游离态:对象与session脱离关联,比如调用session.close()方法时,此时只有数据库中存在该对象。
上个图(来源于网络--侵删)
代码:
package com.sinosoft.objectTest;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sinosoft.model.User;
@RunWith(SpringJUnit4ClassRunner.class) //spring整合了junit测试
@ContextConfiguration("classpath:applicationContext.xml") //用于加载spring的配置文件,初始化容器
public class TransientTest {
@Autowired
//spring容器为我们提供了工厂对象,这里使用注解的方式去容器中拿到该对象
private SessionFactory factory;
@Test
public void testMethod01(){
//从session工厂中拿到session对象
Session session = factory.openSession();
//开启事物
Transaction tx = session.beginTransaction();
//新建一个对象,此时session还没有对user对象管理,所以处于瞬时态
User user = new User();
user.setUserName("Transient");
user.setUserPassword("123");
//执行完save方法之后,该对象的状态由瞬时态变为持久态
session.save(user);
//提交事务
tx.commit();
//关闭session
session.close();
}
}
提一句,关于Hibernate的sql打印,下面这段代码,在调用完save()方法之后,user对象就变为了持久态,并且被加入了一级缓存中,提交事物时,会发送一条sql语句:Hibernate: insert into user_test(user_name, user_password) values(?, ?)此时,如果我们更改了user对象的属性,它会拿当前的user对象去和一级缓存中的user对象进行比较,如果相同,则无需发送新的sql语句,如果不同,则会发送一条更新的sql语句。这里的测试用例会发送一条sql语句:Hibernate: update user_test setuser_name=?,user_password=? where user_id=?。这里只是做了一点简单的说明,关于具体sql打印的规则。略
@Test
public void testMethod02(){
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setUserName("Transient01");
user.setUserPassword("123");
//此时user对象由瞬时态变为持久态,同时对象被session管理,放入一级缓存
session.save(user);
//更改对象的属性??
user.setUserPassword("234");
tx.commit();
session.close();
}
三、HIbernate的一二级缓存
Hibernate的一级缓存也叫做session级别的缓存也叫做事物级别的缓存,它的生命周期和session的生命周期一样,当session关闭,一级缓存也就消失了。支持的方法,get()、load()。session缓存是一块内存空间,用来存放相互管理的java对象,Hibernate查询对象的时候,首先会使用对象的属性OID值在hibernate得一级缓存中进行查找,如果找到匹配OID值得对象,就直接将该对象从一级缓存中取出使用,不再查询数据库;如果没有找到相同的OID的值得对象,则会去数据库中查找相应数据。当从数据库查询数据的时候,该数据信息也会放置到一级缓存中。Hibernated的一级缓存的作用就是减少对数据库的访问次数。
Hibernate的二级缓存是SessionFactory级别的缓存,它的二级缓存策略的一般过程如下:
(1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
(2) 把获得的所有数据对象根据ID放入到第二级缓存中。
(3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
(4) 删除、更新、增加数据的时候,同时更新缓存。
什么样的数据适合存放到第二级缓存中? 很少被修改的数据、不是很重要的数据
四、HIbernate的主键策略
代理主键(没有任何业务意义的主键):
increment:先查最大值,加1,有线程安全问题,只是用测试
identity:数据库逐渐自增
sequence:使用数据库中的序列生成主键(oracle才有)
hilo:由Hibernate自己封装主键自增算法
native:自动三选一(increment,identity,hilo)
uuid:随机生成字符串作为uuid
自然主键:
assigned:id值需要手动指定,一个个去设置。
五、Hibernate的Get和load方法的区别
(1)get()方法直接返回实体类,如果查不到数据则返回null。load()会返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,如果没有数据不存在,就会抛出个org.hibernate.ObjectNotFoundException异常
(2)load先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这就是我们常说的 load在默认情况下支持延迟加载(lazy);get先到缓存(session缓存/二级缓存)中去查,如果没有就到DB中去查(即马上发出sql)。总之,如果你确定DB中有这个对象就用load(),不确定就用get()(这样效率高),具体看代码?:
@Test
public void testMethod03(){
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setUserName("Transient03");
user.setUserPassword("sdsdf");
//"7"这个userID对应的user对象还没有加入库中,缓存中也没有
//user = (User)session.load(User.class, 7);
user = (User)session.get(User.class, 7);
System.out.println(user);
tx.commit();
session.close();
}
问题:查询不到的时候会返回什么?
我们将新建一个user对象,该user对象即没有入库,也没有加入缓存,当我们使用load()方法加载这个还没有入库的对象时,它在缓存和数据库中都没有找到该对象,此时会抛出一个ObjectNotFoundException的异常。把load()方法注释掉以后,使用get方法加载并且打印对象,只是返回一个null.
异常信息:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.sinosoft.model.User#7]
at org.hibernate.boot.internal.StandardEntityNotFoundDelegate.handleEntityNotFound(StandardEntityNotFoundDelegate.java:28)
at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:242)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:159)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:266)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:68)
at com.sinosoft.model.User_$$_jvste44_0.setUserPassword(User_$$_jvste44_0.java)
at com.sinosoft.objectTest.TransientTest.testMethod03(TransientTest.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)