首页 > 代码库 > hibernate 详解
hibernate 详解
Hibernate
1Hibernate的配置文件
1.1cfg
该文件的后缀为*.cfg.xml
hibernate配置文件,该文件中主要存放了
数据的url地址
数据库用户信息
缓存
mapping文件的配置路径
<session-factory >
<property name="connection.url">jdbc:oracle:thin:@192.168.8.62:1521:orcl</property>
<property name="connection.username">wangyajie</property>
<property name="connection.password">wangyajie</property>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping resource="cn/jbit/hibernate/t4/User.hbm.xml" />
</session-factory>
更多配置在/hibernate-release-4.3.5.Final/project/etc/hibernate.properties.template
1.2mapping
该文件的后缀为*.hbm.xml
<hibernate-mapping>
<class name="cn.jbit.entity.User" table="users">
<id name="id"type="java.lang.Integer">
<column name="id" />
<generator class="sequence" >
<param name="sequence">SEQ_ID</param>
</generator>
</id>
<property name="name"type="java.lang.String">
<column name="name"length="50"not-null="true" />
</property>
<property name="password"type="java.lang.String">
<column name="password"length="50"not-null="true" />
</property>
<!--省略其他配置-->
</class>
</hibernate-mapping>
class元素:定义一个实体类的映射信息。
常用属性如下。
name表示对象实体类的全限定名。
table表示对应的数据库表名。
id元素:定义该属性到数据库表主键字段的映射。
常用属性如下。
name表示实体类属性的名字。
type表示实体类属性的类型。
column表示数据库表字段的名字,也可在子元素column中指定。
generator元素:id元素的子元素用于指定主键的生成策略。
常用属性及子元素如下。
class用来指定具体主键生成策略。
param元素用来传递参数。
常用主键的生成策略如下
increment:对类型为long、short或int的主键,以自动增长的方式生成主键的值。主键按数值顺序递增,增量为1。
identity:对如SQL Server、DB2、MySQL等支持标识列的数据库,可使用该主键生成策略生成自动增长主键,但要在数据库中将该主键设置为标识列。
sequence:对如Oracle、DB2等支持序列的数据库,可使用该主键生成策略生成自动增长主键,通过子元素param传入数据库中序列的名称。
native:由Hibernate根据底层数据库自行判断采用何种主键生成策略,意思是由使用的数据库生成主键的值。
assigned:主键由应用程序负责生成,无需Hibernate参与。
property元素:定义实体类中属性和数据库中表的字段的对应关系。
常用属性如下。
name表示实体类属性的名字。
type表示实体类属性的类型。
column表示数据库表字段的名字,也可在子元素column中指定。
column元素:用于指定其父元素代表的实体类属性所对应的数据库表中的字段。
常用属性如下。
name表示字段的名字。
length表示字段长度。
not-null设定是否可以为null,值为true表示不能为null。
2hibernate操作
2.1操作步骤
// 1.读取并解析配置文件
// Configurationcfg =new
//Configuration().configure(TestUser.class.getClassLoader().getResource("cn/jbit/hibernate/t4/hibernate.cfg.xml"));
Configuration cfg =new Configuration();
Properties p =new Properties();
p.load(TestUser.class.getClassLoader().getResourceAsStream("cn/jbit/hibernate/t4/hibernate.properties"));
cfg.addProperties(p);
Configuration cfg=new Configuration().configure();
// 2.读取解析映射信息,创建sessionFactory
cfg.addClass(User.class);
SessionFactory factory =cfg.buildSessionFactory();
// 3.打开session
Session session =factory.openSession();
// 4.开始一个事务
session.beginTransaction();
// 6.提交事务
session.getTransaction().commit();
// 7.关闭session
session.close();
factory.close();
2.2load&get
get | load |
采用立即加载方式 | 采用延迟加载; |
方法执行的时候,会立即向数据库发出查询语句, | load()方法返回的是一个代理(此代理中只有一个id属性),只有等真正使用该对象属性的时候,才会发出sql语句 |
如果搜索数据库中没有对应的记录,get()方法返回的是null | 而load()方法出现异常ObjectNotFoundException |
| 如果lazy为false那么也会直接查询数据库 |
|
|
2.3save&update&saveORUpdate&delete
session.save(user);
执行完save方法后返回主键
事务提交后会返回插入到数据库
session.update(user);
session.delete(user);
注意:不能更新主键
自动提交
connection.autocommit=true
//同步数据库
session.flush();
2.4对象的状态
瞬时状态(Transient)
刚用new语句创建,还没有被持久化,且不处于Session的缓存中
持久状态(Persistent)
已经被持久化,且加入到Session的缓存中
游离状态(Detached)
已经被持久化,但不再处于Session的缓存中
状态转换图
2.5session管理
ThreadLocal
private static final ThreadLocal s=new ThreadLocal();
//......
this.session=(Session)s.get();
if(session==null){
this.session=factory.openSession();
s.set(this.session);
}
Session session1 =null;
Session session2 =null;
@Test
public void sessionMagr()throws IOException
{
Configuration cfg =new Configuration();
Properties p =new Properties();
p.load(TestUser.class.getClassLoader().getResourceAsStream("cn/jbit/hibernate/t4/hibernate.properties"));
cfg.addProperties(p);
// 2.读创essionFactory
cfg.addClass(User.class);
SessionFactory factory =cfg.buildSessionFactory();
session1 =factory.getCurrentSession();
session2 =factory.getCurrentSession();
System.out.println(session1== session2);
// session.beginTransaction();
//
// User user = new User();
//
// user.setAge(123);
// user.setName("wangyajie");
// session.save(user);
// session.flush();
}
openSession()和geturrentSession( )
getCurrentSession( )创建的Session会绑定到当前线程,而openSession( )不会
getCurrentSession( )创建的Session会在事务回滚或事务提交后自动关闭,而openSession( )创建的必须手动关闭
session1 = factory.getCurrentSession();
session2 =factory.getCurrentSession();
System.out.println(session1 ==session2);
session1 =factory.openSession();
session2 =factory.openSession();
System.out.println(session1 ==session2);
注:如果使用了getCurrentSession需要配置hibernate.current_session_context_class=thread
3hibernate配置
3.1单向一对多
public class Student
{
public int id;
public Stringname;
public int age;
}
<hibernate-mapping>
<class name="cn.jbit.hibernate.t5.Student" table="student" lazy="false">
<id name="id" type="int">
<column name="id" />
<generator class="sequence">
<param name="sequence">userid</param>
</generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="age" column="age" type="int"></property>
</class>
</hibernate-mapping>
public class Grade
{
private int id;
private Stringname;
private Set<Student> students=new HashSet<Student>();
}
<hibernate-mapping>
<class name="cn.jbit.hibernate.t5.Grade" table="grade">
<id name="id" type="int">
<generator class="sequence">
<param name="sequence">userid</param>
</generator>
</id>
<property name="name" type="string"></property>
<set name="students" inverse="true">
<key column="gradeid"/>
<one-to-many class="cn.jbit.hibernate.t5.Student" />
</set>
</class>
</hibernate-mapping>
保存的时候
session.beginTransaction();
Grade grade =new Grade();
Student student =new Student();
student.setAge(12);
student.setName("name");
grade.getStudents().add(student);
grade.setName("s1");
session.save(grade);
session.save(student);
session.getTransaction().commit();
inverse="false" 翻转
Hibernate: select userid.nextval from dual
Hibernate: select userid.nextval from dual
Hibernate: insert into grade (name, id) values(?, ?)
Hibernate: insert into student (name, age, id)values (?, ?, ?)
Hibernate: update student set gradeid=? whereid=?
Hibernate: update student set gradeid=? whereid=?
inverse="true"
Hibernate: select userid.nextval from dual
Hibernate: select userid.nextval from dual
Hibernate: insert into grade (name, id) values(?, ?)
Hibernate: insert into student (name, age, id)values (?, ?, ?)
lazy="true"
List<Grade> grades=session.createQuery("from Grade").list();
Hibernate: select grade0_.id asid1_0_, grade0_.name as name2_0_ from grade grade0_
List<Grade> grades =session.createQuery("fromGrade").list();
for (Grade grade : grades)
{
grade.getStudents().size();
}
Hibernate: select grade0_.id as id1_0_,grade0_.name as name2_0_ from grade grade0_
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
lazy="false"
List<Grade> grades =session.createQuery("from Grade").list();
ibernate: select grade0_.id as id1_0_,grade0_.name as name2_0_ from grade grade0_
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
Hibernate: select students0_.gradeid asgradeid4_0_0_, students0_.id as id1_1_0_, students0_.id as id1_1_1_,students0_.name as name2_1_1_, students0_.age as age3_1_1_ from studentstudents0_ where students0_.gradeid=?
fetch="join" 不支持hql(session.createQuery("fromGrade where id= 119").list();)
select grade0_.id as id1_0_0_,
grade0_.name as name2_0_0_,
students1_.gradeidas gradeid4_0_1_,
students1_.id as id1_1_1_,
students1_.id as id1_1_2_,
students1_.name as name2_1_2_,
students1_.age as age3_1_2_
from gradegrade0_
left outer join student students1_
on grade0_.id= students1_.gradeid
where grade0_.id= 119
fetch="select" n+1
select grade0_.idas id1_0_0_, grade0_.nameas name2_0_0_
from gradegrade0_
where grade0_.id= ? select students0_.gradeidas gradeid4_0_0_,
students0_.id as id1_1_0_,
students0_.id as id1_1_1_,
students0_.name as name2_1_1_,
students0_.age as age3_1_1_
from student students0_
where students0_.gradeid= ?
cascade="save-update"
session.beginTransaction();
Grade grade =new Grade();
Student student =new Student();
student.setAge(12);
student.setName("name");
grade.getStudents().add(student);
student =new Student();
student.setAge(1231);
student.setName("qwrqwer");
grade.getStudents().add(student);
grade.setName("s1");
session.save(grade);
// session.save(student);
session.getTransaction().commit();
Hibernate: select userid.nextval from dual
Hibernate: select userid.nextval from dual
Hibernate: select userid.nextval from dual
Hibernate: insert into grade (name, id) values (?,?)
Hibernate: insert intostudent(name,age, id) values (?, ?, ?)
Hibernate: insert intostudent(name,age, id) values (?, ?, ?)
cascade="none"
session.beginTransaction();
Gradegrade =new Grade();
Studentstudent =new Student();
student.setAge(12);
student.setName("name");
grade.getStudents().add(student);
student=new Student();
student.setAge(1231);
student.setName("qwrqwer");
grade.getStudents().add(student);
grade.setName("s1");
session.save(grade);
// session.save(student);
session.getTransaction().commit();
Hibernate: select userid.nextval from dual
Hibernate: insert into grade (name, id) values(?, ?)
3.2单向多对一
public class Student
{
public int id;
public Stringname;
public int age;
private Grade grade;
}
<many-to-one name="grade" class="cn.jbit.hibernate.t5.Grade" column="gradeid" ></many-to-one>
3.3双向一对多
3.4一对一
public int id;
public Stringname;
public int age;
private Gradegrade;
private Cardcard;
private int id;
private Stringname;
private Studentstudent;
<one-to-one name="student" class="cn.jbit.hibernate.t5.Student" lazy="proxy" fetch="select" ></one-to-one>
<!-- <many-to-one name="student"class="cn.jbit.hibernate.t5.Student" unique="true"column="id" update="false" insert="false"></many-to-one>
-->
<one-to-one name="card" class="cn.jbit.hibernate.t5.Card" fetch="select" ></one-to-one>
3.5多对多
<set name="teachers" table="teacher_student" >
<key column="sid"></key>
<many-to-many class="cn.jbit.hibernate.t5manytomany.Teacher" column="tid"></many-to-many>
</set>
<set name="students" table="teacher_student">
<key column="tid"></key>
<many-to-many class="cn.jbit.hibernate.t5manytomany.Student"
column="sid"></many-to-many>
</set>
private int id;
private Stringname;
private Set<Student> students =new HashSet<Student>();
private int id;
private Stringname;
private Set<Teacher> teachers=new HashSet<Teacher>();
3.6lazy详解
lazy,延迟加载
Lazy的有效期:只有在session打开的时候才有效;session关闭后lazy就没效了。
lazy策略可以用在:
* <class>标签上:可以取值true/false
* <property>标签上,可以取值true/false,这个特性需要类增强
* <set>/<list>等集合上,可以取值为true/false/extra
* <one-to-one>/<many-to-one>等标签上,可以取值false/proxy/no-proxy
6.1 get和load的区别:
* get不支持延迟加载,而load支持。
*当查询特定的数据库中不存在的数据时,get会返回null,而load则抛出异常。
6.2类(Class)的延迟加载:
*设置<class>标签中的lazy="true",或是保持默认(即不配置lazy属性)
*如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;这样有可能减少系统的开销。
* //不会发出查询sql
System.out.println("group id=" + group.getId());
这里有一个问题,为什么加载主键的时候不需要发出sql语句。
6.3集合(collection)的延迟加载:可以取值true,false,extra
*保持集合上的lazy的默认值,此时的效果和lazy="extra"是基本一样的。
*设置集合上的lazy=extra,此时的效果和lazy属性的默认值是基本一样的。但是推荐使用这个属性值,因为在统计时这种情况显得比较智能。当然延迟是有效果的。
*设置集合上的lazy=false
true:默认取值,它的意思是只有在调用这个集合获取里面的元素对象时,才发出查询语句,加载其集合元素的数据
false:取消懒加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联集合的数据
extra:一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate
并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据
6.4 Hibernate单端关联懒加载策略:即在<one-to-one>/<many-to-one>标签上可以配置
懒加载策略。可以取值为:false/proxy/no-proxy
false:取消懒加载策略,即在加载对象的同时,发出查询语句,加载其关联对象
proxy:这是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句查询其对象数据,其关联对象是代理类
no-proxy:这种懒加载特性需要对类进行增强,使用no-proxy,其关联对象不是代理类
注意:在class标签上配置的lazy属性不会影响到关联对象!!!
4缓存
缓存介于应用程序和永久性数据存储源之间
使用缓存,可以降低应用程序直接读写永久性数据存储源的频率,提高运行性能
Hibernate中提供了两个级别的缓存
第一级别的缓存是 Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate管理的,一般情况下无需进行干预
第二级别的缓存是 SessionFactory级别的缓存,它是属于进程范围的缓存
SessionFactory的缓存可以分为两类:
内置缓存:Hibernate自带的,不可卸载.通常在 Hibernate的初始化阶段, Hibernate会把映射元数据和预定义的 SQL语句放到 SessionFactory的缓存中,映射元数据是映射文件中数据的复制,而预定义 SQL语句时 Hibernate根据映射元数据推到出来的.该内置缓存是只读的.
外置缓存(二级缓存): 一个可配置的缓存插件.在默认情况下,SessionFactory不会启用这个缓存插件.外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘
4.1一级缓存
Session缓存
session.beginTransaction();
Studentstudent =new Student();
student.setName("wangyajie");
session.save(student);
//清空所有缓存
//会向数据库提交数据
// session.flush();
//不会向数据库提交数据
// session.clear();
//不会向数据库提交数据,清空单个缓存
session.evict(student);
session.getTransaction().commit();
4.2二级缓存
二级缓存如何工作的
Hibernate的二级缓存同一级缓存一样,也是针对对象ID来进行缓存。所以说,二级缓存的作用范围是针对根据ID获得对象的查询。
在执行各种条件查询时,如果所获得的结果集为实体对象的集合,那么就会把所有的数据对象根据ID放入到二级缓存中。
当Hibernate根据ID访问数据对象的时候,首先会从Session一级缓存中查找,如果查不到并且配置了二级缓存,那么会从二级缓存中查找,如果还查不到,就会查询数据库,把结果按照ID放入到缓存中。
删除、更新、增加数据的时候,同时更新缓存。
Hibernate的二级缓存的实现原理与一级缓存是一样的,也是通过以ID为key的Map来实现对对象的缓存。
二级缓存是缓存实体对象的,由于Hibernate的二级缓存是作用在SessionFactory范围内的,因而它比一级缓存的范围更广,可以被所有的Session对象所共享。
在通常情况下会将具有以下特征的数据放入到二级缓存中:
很少被修改的数据。
不是很重要的数据,允许出现偶尔并发的数据。
不会被并发访问的数据。
常量数据。
不会被第三方修改的数据
而对于具有以下特征的数据则不适合放在二级缓存中:
经常被修改的数据。
财务数据,绝对不允许出现并发。
与其他应用共享的数据。
在这里特别要注意的是对放入缓存中的数据不能有第三方的应用对数据进行更改(其中也包括在自己程序中使用其他方式进行数据的修改,例如,JDBC),因为那样Hibernate将不会知道数据已经被修改,也就无法保证缓存中的数据与数据库中数据的一致性。
常见的缓存组件
在默认情况下,Hibernate会使用EHCache作为二级缓存组件
缓存插件 | 缓存实现类 | 是否支持 查询缓存 | 类型 |
EHCache | org.hibernate.cache.EhCacheProvider | 是 | 进程范围的缓存; 内存或硬盘 |
OSCache | org.hibernate.cache.OSCacheProvider | 是 | 进程范围的缓存; 内存或硬盘 |
SwarmCache | org.hibernate.cache.SwarmCacheProvider | 否 | 集群范围的缓存 |
JBossCache | org.hibernate.cache.TreeCacheProvider | 是 | 集群范围的缓存 |
如何在程序里使用二级缓存:
首先在hibernate.cfg.xml开启二级缓存
......
<!--开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!--启动"查询缓存"如果想缓存使用findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集,必须配置此项-->
<property name="hibernate.cache.use_query_cache">true</property>
<!--设置二级缓存插件EHCache的Provider类-->
<!-- <propertyname="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property> -->
<!--二级缓存区域名的前缀 -->
<!--<propertyname="hibernate.cache.region_prefix">test</property>-->
<!--高速缓存提供程序 -->
<property name="hibernate.cache.region.factory_class">
net.sf.ehcache.hibernate.EhCacheRegionFactory
</property>
<!-- Hibernate4以后都封装到org.hibernate.cache.ehcache.EhCacheRegionFactory-->
<!--指定缓存配置文件位置 -->
<!-- <propertyname="hibernate.cache.provider_configuration_file_resource_path">
ehcache.xml
</property> -->
<!--强制Hibernate以更人性化的格式将数据存入二级缓存 -->
<property name="hibernate.cache.use_structured_entries">true</property>
<!--Hibernate将收集有助于性能调节的统计数据 -->
<property name="hibernate.generate_statistics">true</property>
......
然后是ehcache配置(ehcache.xml)
cache参数详解:
● name:指定区域名
● maxElementsInMemory :缓存在内存中的最大数目
● maxElementsOnDisk:缓存在磁盘上的最大数目
● eternal :设置是否永远不过期
● overflowToDisk :硬盘溢出数目
● timeToIdleSeconds :对象处于空闲状态的最多秒数后销毁
● timeToLiveSeconds :对象处于缓存状态的最多秒数后销毁
● memoryStoreEvictionPolicy:缓存算法,有LRU(默认)、LFU、LFU
关于缓存算法,常见有三种:
● LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象
● LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象
● LFU:(First In First Out)把最早进入二级缓存的对象替换掉
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--如果缓存中的对象存储超过指定的缓存数量的对象存储的磁盘地址 -->
<diskStore path="D:/ehcache" />
<!--默认cache:如果没有对应的特定区域的缓存,就使用默认缓存 -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="false" />
<!--指定区域cache:通过name指定,name对应到Hibernate中的区域名即可 -->
<cache name="cn.javass.h3test.model.UserModel" eternal="false"
maxElementsInMemory="100" timeToIdleSeconds="1200"
timeToLiveSeconds="1200" overflowToDisk="false">
</cache>
</ehcache>
在每个实体的hbm文件中配置cache元素,usage可以是read-only或者是read-write等4种。
<?xml version="1.0" encoding=‘UTF-8‘?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class>
<!--设置该持久化类的二级缓存并发访问策略 read-only read-writenonstrict-read-write transactional-->
<class name="cn.java.test.model.User" table="TBL_USER">
<cache usage="read-write"/>
......
</class>
</hibernate-mapping>
Query或Criteria接口查询时设置其setCacheable(true):默认的如果不在程序中显示的执行查询缓存声明操作,Hibernate是不会对查询的list进行缓存的。
Sessions1=HibernateSessionFactory.getCurrentSession();
s1.beginTransaction();
System.out.println("第一次查询User");
Queryq = s1.createQuery("fromUser");
q.setCacheable(true);
q.list();
System.out.println("放进二级缓存");
s1.getTransaction().commit();
Sessions2=HibernateSessionFactory.getCurrentSession();
s2.beginTransaction();
System.out.println("第二次查询User,将不会发出sql");
Queryq =s2.createQuery("fromUser");
q.setCacheable(true);
q.list();
s2.getTransaction().commit();
//如果配置文件打开了generate_statistics性能调解,可以得到二级缓存命中次数等数据
Statistics s=HibernateSessionFactoryUtil.getSessionFactory().getStatistics();
System.out.println(s);
System.out.println("put:"+s.getSecondLevelCachePutCount());
System.out.println("hit:"+s.getSecondLevelCacheHitCount());
System.out.println("miss:"+s.getSecondLevelCacheMissCount());
二级缓存的管理:
// evict(Class arg0,Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源.
sessionFactory.evict(Customer.class,new Integer(1));
// evict(Class arg0)将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源
sessionFactory.evict(Customer.class);
// evictCollection(String arg0)将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源.
sessionFactory.evictCollection("Customer.orders");
设置一级缓存和二级缓存的交互权限
//仅向二级缓存读数据,而不向二级缓存写数据,这里load的数据就不会放入二级缓存,下次再查还是会去数据库拿
session.setCacheMode(CacheMode.GET);
//只向二级缓存写数据,而不从二级缓存读数据
//session.setCacheMode(CacheMode.PUT);
//不与二级缓存交互
//session.setCacheMode(CacheMode.IGNORE);
//可以与二级缓存交互
//session.setCacheMode(CacheMode.NORMAL);
Studentstudent = (Student)session.load(Student.class,1);
设置二级缓存策略
● READ_ONLY:实体只读缓存
只读缓存不允许更新,将报错Can‘t write to a readonly object。
允许新增,(从2.0以后新增直接添加到二级缓存)
//确保数据库中有标识符为1的FarmModel
FarmModelfarm = (FarmModel) session.get(FarmModel.class,1);
//如果修改将报错,只读缓存不允许修改
//farm.setName("aaa");
● NONSTRICT_READ_WRITE:实体非严格读/写缓存
允许更新,更新后缓存失效,需再查询一次。
允许新增,新增记录自动加到二级缓存中。
整个过程不加锁。
● READ_WRITE:实体读/写缓存
允许更新,更新后自动同步到缓存。
允许新增,新增记录后自动同步到缓存。
保证read committed隔离级别及可重复读隔离级别(通过时间戳实现)
整个过程加锁,如果当前事务的时间戳早于二级缓存中的条目的时间戳,说明该条目已经被别的
事务修改了,此时重新查询一次数据库,否则才使用缓存数据,因此保证可重复读隔离级别。
读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁
● TRANSACTIONAL:实体事务缓存
缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境
● Collection集合缓存<hibernate-mapping>
<class name="cn.java.test.model.UserModel" table="TBL_USER">
<cacheusage="read-write" />
<setname="farms" cascade="all" inverse="true" lazy="false">
<cacheusage="read-write"/>
<keycolumn="fk_user_id"/>
<one-to-manyclass="cn.java.test.model.FarmModel"/>
</set>
</class>
</hibernate-mapping>
4.2.1类级别缓存区
散装数据的存储:每次使用二级缓存,将获得一个新的对象:
<class name="cn.jbit.hibernate.firstcache.Student" table="student">
<cache usage="read-write" />
<id name="id" type="int">
<column name="id" />
<generator class="sequence">
<param name="sequence">userid</param>
</generator>
</id>
<property name="name" column="name" type="string"></property>
</class>
Sessionsession =factory.openSession();
session.get(Student.class, 120);
System.out.println(session.hashCode());
session.close();
session =factory.openSession();
session.get(Student.class, 120);
System.out.println(session.hashCode());
两次的hashcode是不一样的
Hibernate: select student0_.id as id1_0_0_,student0_.name as name2_0_0_ from student student0_ where student0_.id=?
303575666
1501149104
Query接口可以将数据放置到类级别的二级缓存中,但是不能使用query接口的list方法从缓存中获取数据,能存不能取:
Session session =factory.openSession();
Stringhql ="from Student";
session.createQuery(hql).list();
session.close();
session=factory.openSession();
session.get(Student.class, 120);
session.close();
session=factory.openSession();
hql="fromStudent";
session.createQuery(hql).list();
session.close();
Hibernate: select student0_.id as id1_0_,student0_.name as name2_0_ from student student0_
Hibernate: select student0_.id as id1_0_,student0_.name as name2_0_ from student student0_
Query接口的iterate可以使用缓存
Session session =factory.openSession();
String hql ="from Student";
Iterator<Student>iterator =session.createQuery(hql).iterate();
while (iterator.hasNext())
{
iterator.next();
}
session.close();
session =factory.openSession();
session.get(Student.class, 120);
session.close();
session =factory.openSession();
hql ="from Student";
System.out.println("----------------------------");
iterator =session.createQuery(hql).iterate();
while (iterator.hasNext())
{
iterator.next();
}
session.close();
4.2.2集合级别的缓存区
集合级别缓存存放的是保存在类级别缓冲区中的数据对象的OID:
Session session =factory.openSession();
Grade grade = (Grade)session.get(Grade.class, 191);
grade.getStudents().size();
session.close();
session =factory.openSession();
grade = (Grade)session.get(Grade.class, 191);
grade.getStudents().size();
session.close():
select grade0_.id as id1_0_0_, grade0_.name asname2_0_0_ from Grade grade0_ where grade0_.id=?
Hibernate: select students0_.gid as gid3_0_0_,students0_.id as id1_1_0_, students0_.id as id1_1_1_, students0_.name asname2_1_1_ from student students0_ where students0_.gid=?
;
1.年纪学生集合都缓存
2.年纪学生缓存
3.年纪学生都不缓存
4.2.3更新时间戳
Session session =factory.openSession();
session.beginTransaction();
Studentstudent =(Student) session.get(Student.class, 192);
System.out.println(session.hashCode());
// Stringhql = "update Student set name=‘wangyajie‘where id=192";
// session.createQuery(hql).executeUpdate();
student.setName("jiaywamg");//不能使用set
session.save(student);
session.getTransaction().commit();
session.close();
session =factory.openSession();
session.get(Student.class, 192);
System.out.println(session.hashCode());
4.2.4查询缓存(三级缓存)
之前类级别缓存、集合级别缓存,都是将数据存入类级别缓冲区,通过id查询对象。key是对象的id,value是缓存对象;这里的查询缓存key是HQL语句或SQL语句,value是查询结果数据。查询缓存依赖二级缓存。
Session session =factory.openSession();
String hql ="select name from Student where id=192";
session.createQuery(hql).setCacheable(true).list().size();
session.close();
session =factory.openSession();
hql ="select name from Student where id=192";
session.createQuery(hql).setCacheable(true).list().size();
session.close();
4.2.5缓存到硬盘
<ehcache>
<diskStore path="D://123" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
5注解
注解 | 含义和作用 |
@Entity | 将一个类声明为一个实体bean(即一个持久化POJO类) |
@Id | 声明了该实体bean的标识属性(相当于数据表的主键) |
@GeneratedValue | 定义标识符的生成策略 |
@Table | 为实体bean映射指定表(table)、目录(catalog)和schema的名字。默认值:实体bean的类名,不带包名 |
@UniqueConstraint | 定义表的唯一约束 |
@Lob | 表示属性将被持久化为Blob或者Clob类型 |
@Column | 将属性映射到列 |
@Transient | 将忽略这些字段和属性,不用持久化到数据库 |
@NamedQuery | 配置命名查询 |
@OneToOne | 建立实体bean之间的一对一关联 |
@OneToMany | 建立实体bean之间的一对多关联 |
@ManyToOne | 建立实体bean之间的多对一关联 |
@ManyToMany | 建立实体bean之间的多对多关联 |