Java实战之02Hibernate-02映射、一级缓存、实体对象状态
五、映射基础
1、实体类采用javabean的编写规范
JavaBean编写规范:
a、类一般是public的
b、有默认的构造方法
c、字段都是私有的
d、提供公有的getter和setter方法
e、一般都实现java.io.Serializable接口
注意:hibernate采用的暴力反射
2、基本类型OR包装类型
int ---> Integer
.......
3、访问实体类数据的策略(了解)
通过上面我们可知,hibernate是采用的暴力反射,所以我们既可以通过属性页可以用过字段访问。但是一般都使用属性(即get和set方法)
直接执行会报错,我们需要通过在配置文件中配置,来告知hibernate采用访问属性的方式进行取值和赋值。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 创建映射文件: 3 1、在实体类所在的包下,创建一个以实体类名称.hbm.xml的文件。 4 2、导入dtd约束 5 在hibernate的核心jar包中 hibernate-mapping-3.0.dtd 6 明确: 7 映射中指定类中的属性都是针对的set方法后面的部分。 8 --> 9 <!DOCTYPE hibernate-mapping PUBLIC 10 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 11 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 12 <hibernate-mapping package="cn.itcast.domain"> 13 <!-- 类和表的对应关系 --> 14 <class name="Student" table="student"> 15 <!-- 类中的字段和表中的列的对应关系 --> 16 <!-- 主键的映射 --> 17 <id name="id" column="id"> 18 <!-- 主键的生成方式 19 native: 20 它指的是使用本地数据自己的自动增长能力 21 (如果数据库之中字段自增长,那就是用字段自增长。 22 如果数据库支持的是序列自增长,那就是用序列自增长。) 23 --> 24 <generator class="native"></generator> 25 </id> 26 <!-- 其他字段映射 27 指定访问的方式是通过字段访问:access="field" 28 --> 29 <property name="name" column="name"></property> 30 <property name="gender" column="gender"></property> 31 <property name="birthday" column="birthday"></property> 32 </class> 33 </hibernate-mapping>
4、派生属性
概念:
实体类中的某些属性在数据库表中没有对应的字段,该属性的值是由数据库表中其他字段计算出来的。这样的属性叫做派生属性。
模拟测试数据:
创建一个订单表:
1 <hibernate-mapping package="cn.itcast.domain"> 2 <class name="Customer" table="customer"> 3 <id name="id" column="id"> 4 <generator class="native"></generator> 5 </id> 6 <property name="name" column="name"></property> 7 <!-- 映射totalPrice,它是一个派生属性。不是表中本身的字段信息 8 formula:用于映射派生属性 9 必须以括号开头 10 括号内中 需要使用别名的地方,要自己指定 11 --> 12 <property name="totalPrice" formula="(select sum(o.price) from orders o where o.customer_id = id)"></property> 13 </class> 14 </hibernate-mapping>
5、控制insert和update
映射文件:xml
5.1、property元素(了解)
insert:默认值是true,如果改为false。表示该属性永远不会被插入。(动态生成的SQL语句)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 创建映射文件: 3 1、在实体类所在的包下,创建一个以实体类名称.hbm.xml的文件。 4 2、导入dtd约束 5 在hibernate的核心jar包中 hibernate-mapping-3.0.dtd 6 明确: 7 映射中指定类中的属性都是针对的set方法后面的部分。 8 --> 9 <!DOCTYPE hibernate-mapping PUBLIC 10 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 11 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 12 <hibernate-mapping package="cn.itcast.domain"> 13 <!-- 类和表的对应关系 14 属性: 15 dynamic-insert:动态生成SQL语句,生成的规则是,永远不涉及为null的字段。 16 取值true/false。默认值false。当改为true时,按照上述规则生成。 17 dynamic-update:动态生成SQL语句,生成规则是,永远不涉及不变的字段。(修改前和修改后数据一样的,不会被涉及) 18 取值true/false。默认值false。当改为true时,按照上述规则生成。 19 --> 20 <class name="Student" table="student" dynamic-insert="true" dynamic-update="true"> 21 <!-- 类中的字段和表中的列的对应关系 --> 22 <!-- 主键的映射 --> 23 <id name="id" column="id"> 24 <!-- 主键的生成方式 25 native: 26 它指的是使用本地数据自己的自动增长能力 27 (如果数据库之中字段自增长,那就是用字段自增长。 28 如果数据库支持的是序列自增长,那就是用序列自增长。) 29 increment: 30 有hibernate自己维护主键的自增长。需要先查询一次最大的id。 31 identity: 32 使用的是本地数据库的字段自增长能力 33 sequence: 34 使用的是本地数据库的序列自增长能力 35 assigned: 36 使用手动指定的方式。(主键有一定的生成规则时,使用此值) 37 hilo: 38 高地位算法 39 --> 40 <generator class="identity"></generator> 41 </id> 42 <!-- 其他字段映射 43 指定访问的方式是通过字段访问:access="field" 44 insert属性:用于动态生成SQL语句,该属性取值是true和false。默认值是true。 45 true:会插入该字段数据。 46 false:永远不会插入该字段数据。 47 update属性:用于动态生成SQL语句,该属性取值true和false。默认值是true。 48 true:会更新字段信息。 49 false:永远不会更新该字段信息 50 --> 51 <property name="name" column="name" ></property> 52 <property name="gender" column="gender"></property> 53 <property name="birthday" column="birthday"></property> 54 </class> 55 </hibernate-mapping>
5.2、class元素
mutable:默认值是true,如果为false,表示当前类所有属性都不会被修改。
dynamic-insert:默认是false,如果为true。会动态生成insert语句,只涉及不为null的字段。
dynamic-update:默认值是false。如果为true,会动态生成update语句,只更新数据有变化了的字段。
如果类的属性很多,建立动态生成insert或update。(动态生成SQL消耗的资源可以忽略不计)。
6、避免与数据库关键字冲突的问题
例如:
我们在创建订单表时,有可能会使用Order来作为表名,但是由于order是关键字,可以在order上加入反引号。
六、映射对象标识符
1、概述
总结:应该让Hibernate来管理OID。
2、Hibernate主键生成策略
2.1、如何配置
1 <id name="id" column="id"> 2 <generator class="native"></generator> 3 </id>
2.2、常用生成策略
a、increment
OID必须是long,int,short类型的。
弊端:
适用于单应用访问同一个数据库的场合,在集群环境下不推荐使用。
b、identity
数据库系统必须支持自动增长字段类型
OID必须是long、int、short类型
注意:increment和identity的区别,一个是由Hibernate来负责维护,一个是由数据库来负责维护。
c、hilo:hibernate利用高地位算法生成OID
不依赖底层数据库,适用于所有的数据库。
OID必须是long、int、short类型
该算法生成的标识符只能在一个数据库中保证唯一
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 创建映射文件: 3 1、在实体类所在的包下,创建一个以实体类名称.hbm.xml的文件。 4 2、导入dtd约束 5 在hibernate的核心jar包中 hibernate-mapping-3.0.dtd 6 明确: 7 映射中指定类中的属性都是针对的set方法后面的部分。 8 --> 9 <!DOCTYPE hibernate-mapping PUBLIC 10 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 11 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 12 <hibernate-mapping package="cn.itcast.domain"> 13 <!-- 类和表的对应关系 14 属性: 15 dynamic-insert:动态生成SQL语句,生成的规则是,永远不涉及为null的字段。 16 取值true/false。默认值false。当改为true时,按照上述规则生成。 17 dynamic-update:动态生成SQL语句,生成规则是,永远不涉及不变的字段。(修改前和修改后数据一样的,不会被涉及) 18 取值true/false。默认值false。当改为true时,按照上述规则生成。 19 --> 20 <class name="Student" table="student" dynamic-insert="true" dynamic-update="true"> 21 <!-- 类中的字段和表中的列的对应关系 --> 22 <!-- 主键的映射 --> 23 <id name="id" column="id"> 24 <!-- 主键的生成方式 25 native: 26 它指的是使用本地数据自己的自动增长能力 27 (如果数据库之中字段自增长,那就是用字段自增长。 28 如果数据库支持的是序列自增长,那就是用序列自增长。) 29 increment: 30 有hibernate自己维护主键的自增长。需要先查询一次最大的id。 31 identity: 32 使用的是本地数据库的字段自增长能力 33 sequence: 34 使用的是本地数据库的序列自增长能力 35 assigned: 36 使用手动指定的方式。(主键有一定的生成规则时,使用此值) 37 hilo: 38 高地位算法 39 --> 40 <generator class="identity"></generator> 41 </id> 42 <!-- 其他字段映射 43 指定访问的方式是通过字段访问:access="field" 44 insert属性:用于动态生成SQL语句,该属性取值是true和false。默认值是true。 45 true:会插入该字段数据。 46 false:永远不会插入该字段数据。 47 update属性:用于动态生成SQL语句,该属性取值true和false。默认值是true。 48 true:会更新字段信息。 49 false:永远不会更新该字段信息 50 --> 51 <property name="name" column="name" ></property> 52 <property name="gender" column="gender"></property> 53 <property name="birthday" column="birthday"></property> 54 </class> 55 </hibernate-mapping>
算法:
高值*(max_lo+1)+不大于max_lo的值
高值从0开始
0*(5+1)+0=0 忽略
0*(5+1)+1=1
0*(5+1)+2=2
0*(5+1)+3=3
0*(5+1)+4=4
0*(5+1)+5=5 下一个高值为1
1*(5+1)+0=6
1*(5+1)+1=7
1*(5+1)+2=8
1*(5+1)+3=9
1*(5+1)+4=10
1*(5+1)+5=11 下一个高值为2
d、native
依据底层数据库对自动生成标识符的支持能力来选择使用identity、sequence来生成标识符
适合跨数据库平台开发
OID必须是long、int、short类型
七、Session对象的一级缓存
1、Session的一级缓存
1 //证明一级缓存的存在 2 @Test 3 public void test1(){ 4 Session s = HibernateUtil.getSession(); 5 Transaction tx = s.beginTransaction(); 6 Student s1 = s.get(Student.class, 2);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 7 System.out.println(s1); 8 Student s2 = s.get(Student.class, 2);//不会去查询,因为一级缓存之中有数据。 9 System.out.println(s2); 10 tx.commit(); 11 s.close(); 12 }
2、为什么使用缓存
a、减少访问数据库的频率。应用从缓存中读取数据的速度比从数据库获取数据要快,提高了数据的访问性能
b、当缓存中的对象之间存在循环关联关系时,Session保证不会出现访问对象的关联死循环,以及由死循环造成的JVM堆栈溢出问题
c、保证数据库中的相关记录与缓存中的对象保持同步
注意:当与数据库中的数据一致时,不会进行更新。
3、如何做到数据发生变化时进行同步
3.1、快照(SnapShot)机制
1 //修改数据 2 //快照机制 3 @Test 4 public void test2(){ 5 Session s = HibernateUtil.getSession(); 6 Transaction tx = s.beginTransaction(); 7 Student s1 = s.get(Student.class, 2);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 8 System.out.println(s1);//gender:male 9 s1.setGender("male");//改gender,其实改的是内存对象 10 System.out.println(s1);//gender:female 11 tx.commit(); 12 s.close();//一级缓存在此行执行之后就消失了。 13 14 //在此种再接着写代码 15 System.out.println(s1);//gender:female 16 }
4、控制Session的一级缓存
目的:学习Session中与一级缓存有关的方法。
4.1、Session.clear();:清空缓存。
1 /* 2 * clear方法 3 * 作用:清空一级缓存 4 * 5 * 和close有区别: 6 * close执行之后,一级缓存也没有了。 7 * clear执行完成之后,session是还可以用的。 8 */ 9 @Test 10 public void test1(){ 11 Session s = HibernateUtil.getSession(); 12 Transaction tx = s.beginTransaction(); 13 Student s1 = s.get(Student.class, 2);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 14 System.out.println(s1); 15 16 s.clear(); 17 18 Student s2 = s.get(Student.class, 2); 19 System.out.println(s2); 20 tx.commit(); 21 s.close(); 22 }
4.2、Session.evict(Object entity);:清除一级缓存中指定的实体对象。
1 /* 2 * evict方法 3 * 作用清除的是指定实体的缓存 4 */ 5 @Test 6 public void test2(){ 7 Session s = HibernateUtil.getSession(); 8 Transaction tx = s.beginTransaction(); 9 Student s2 = s.get(Student.class, 2);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 10 Student s3 = s.get(Student.class, 3);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 11 12 s.evict(s2); 13 14 Student s4 = s.get(Student.class, 2);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 15 Student s5 = s.get(Student.class, 3);//不会去查询,因为一级缓存之中有数据 16 tx.commit(); 17 s.close(); 18 }
4.3、Session.refresh(Obect entity);:重新刷新缓存(用数据库中的数据同步缓存中指定的实体)
1 /* 2 * refresh方法 3 * 作用:那数据库的数据,来同步一级缓存。 4 */ 5 @Test 6 public void test3(){ 7 Session s = HibernateUtil.getSession(); 8 Transaction tx = s.beginTransaction(); 9 Student s1 = s.get(Student.class, 6);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 10 System.out.println(s1);//gender :male 11 s1.setGender("female");//修改gender:female 12 System.out.println(s1); 13 s.refresh(s1); 14 System.out.println(s1); 15 tx.commit(); 16 s.close(); 17 }
4.4、Session.flush();:清理缓存(同步缓存中数据到数据库中),但是不会清空。
a、清理时机:
1、Transaction.commit()时会先清理缓存后提交事务。安排在此处是为了减少数据访问的频率和缩短当前事务对数据库中资源的锁定时间。
2、当应用中执行一些查询操作(Query)时,如果缓存中的对象发生了变化,会先进行清理。从而保证查询出的数据是正确的
3、显式调用Session的flush()方法
1 /* 2 * flush方法 3 * 作用:手动刷新缓存。把数据从session中刷新出去。 4 */ 5 @Test 6 public void test5(){ 7 Session s = HibernateUtil.getSession(); 8 Transaction tx = s.beginTransaction(); 9 Student s1 = s.get(Student.class, 6);//会去数据库查询,同时会把查询出来的数据存入一级缓存之中。 10 System.out.println(s1);//gender :male 11 s1.setGender("male");//修改gender:female 12 System.out.println(s1); 13 14 s.flush();//当有此行存在时,此时就会看到update语句 15 16 tx.commit();//默认此行刷出缓存中的数据。去拿缓存修改数据库的数据 17 s.close(); 18 }
b、设置清理时机
八、实体对象的状态
1、各种状态图
2、状态说明:(判断对象处于什么状态不以数据库中有无记录作为依据,因为事务的问题,事务是否已经提交)
2.1、临时状态(transient):
用new语句创建,还没有被持久化,并且不在Session的缓存中。
标识:OID为null,没有和Session建立关系。
2.2、持久化状态(persistent):
已经计划被持久化,并且加入到Session的缓存中。(为什么说计划:因为事务问题,是否已经提交事务)
标识:OID不为null,建立了和Session的关系。
2.3、删除状态(removed):
不在Session的缓存中,且Session已经计划将其从数据库中删除。
标识:OID不为null,计划要从Session中删除的。
2.4、游离状态(detached脱管):
已经被持久化,不在Session的缓存中
标识:OID不为null,没有和Session建立关系。