第三章:对像关系映射基础
本章内容:
1)pojo持久化类的属性及访问原理
2)Hibernate访问持久类属性的策略
3)在持久化类的访问中加入程序逻辑
4)代理主键与自然主键
5)Hibernate主键生成器
pojo持久化类的属性及访问原理
1)pojo类使用JavaBean的风格,为需要访问的属性提供getXxx()和setXxx()方法。这两个
方法也称为持久化类的访问。
2)Hibernate在进行数据持久化时也会调用pojo类对应的getXxx()和setXxx()方法
Hibernate会调用pojo类的getXxx()方法从pojo对像中获取数据,并把它保存到
数据库中去,会调用pojo类的setXxx()方法把从数据库读出的信息写入到pojo对像去
3)具体说当Hibernate的Session在执行save(),update(),delete()方法把数据写入数据库时
会调用pojo类的getXxx()方法;当Hibernate的Session在执行get(),load()或find()方法从
数据库中读取数据时,会调用pojo类的se.tXxx()方法
4)值得注意的是,Java应用程序不能访问持久化类的private的类型的getXxx()和setXxx()方法
而Hibernate没有这个限制,它能够访问各种级别的getXxx()和setXxx()方法。
Hibernate访问持久类属性的策略
1)在mapping文件(对像-关系映射文件)中,<propery>元素的access属性用于指定Hibernate
访问持久化类的属性方式。如果把这个属性设置为propery,这也是默认值表明Hibernate
通过相应的getXxx()与setXxx()方法来访问类的属性.
比如:<property name="cname" type="string" access="property">或
<property name="cname" type="string">这样Hibnate就会在访问数据库时自动去调用 getName()方法与setcname()方法。不管这个pojo类中是否有cname这个属性。
只要有get与set方法就行了也就是说可以像这样配置
Cust====pojo类============
public class Cust
{
private String cname;//这个代码可以省略
public void setCname(String cname)
{
...
}
public int getCname()
{
...
}
}
在持久化类的访问中加入程序逻辑
1)持久化类中的属性是与表中的字段保持一致。有时可能希望持久化类对表中的字段进一步进行分解
我们就可以把这种分解的程序逻辑写在持久化类中。
2)在Order表中有一个字段是订单日期。表现形式如:"2005年3月4日",现在希望对这个字段
进一步分解,分成年、月、日。步骤如下:
A:在Order类中加入年、月、日属性,只需要get不需要set。尽管这些属性在表中没有
public class Orders implements java.io.Serializable
{
private int y;
private int m;
private int d;
public void setDtm(String dtm)
{
this.y=Integer.parseInt(dtm.substring(0, 4));
this.m=Integer.parseInt(dtm.substring(5,dtm.indexOf('月')));
this.d=Integer.parseInt(dtm.substring(dtm.indexOf('月')+1,dtm.length()-1));
this.dtm = dtm;
}
B:在Orders.hbm.xml文件中,无需映射Orders类的y,m,d属性,只需要映射dtm属性,它
和Orders表的dtm字段对应
<property name="dtm" column="dtm"/>
C:尽管在Order表中没有定义d,t,m属性,由于Hibernate不会直接访问d,t,m属性而是调用
getDtm()和setDtm()方法
代理主键与自然主键
1)关系型数据库按主键区分不同的记录
2)每条记录的主键值是永远不会改变的,任何企图改变主键的操作都是极其错误的
3)在订单表中如果把订单Id做为主键也是可行的。比如:订单Id由最大的记录号加上当前的日期来构 成但是以后如果需求发生了变化,则就必须修改数据库从而增加了数据维护的难度。
4)更合理的方式是使用代理主键,即不具备业务含义的字段,该字段一般取名为"ID",代理主键
通常为整数类型,因为整数类型比字符类型要节省更多的数据库空间
5)生成代理主键的方式:
A:sql server数据库可以使用Identity标识例来进行
B:oracle数据库可以通过Sequence中获取自动增长的标识符.
1:create sequence My_sequence start with 1 increment by 1
2:创建一个Sequence标识列,以后可以通过curval与nextval属性获取值
3:curval:返回序列的当前值。
4:nextval:先增加序列的值,然后返回序列值
5:执行select My_sequence.nextval from dual 把序列加载到进程中
6:执行insert into 表名(My_sequence.nextval...)方式来从序列中获取值
Hibernate主键生成器
1)关系型数据库是按照代理主键来区分一个表中的多条记录
2)而Java应用程序是通过内存地址来区分同一个类的不同对像
Person p1=new Person();
Person p2=new Person();
Person p3=p1;
System.out.print(p1==p2);//相等
System.out.print(p1==p3);//不相等
3)Hibernate使用OID(object identity)对像标识符来统一两者之间的矛盾的.
4)在运行时Hibernate根据OID来维持Java对像和数据库表中记录的对应关系
5)为了保证OID的唯一性与不变性,通常不是由调用者为OID赋值,而是由Hibernate来给
OID赋值。因此可以把OID的setId()方法设置为private类型,可以禁止Java应用程序
随便的修改OID.而把getId()方法设置为public类型,这使得Java程序可以读取持久化
类的OID
private int id;
private void setId(int id)
{
this.id=id;
}
public int getId()
{
return id;
}
6)在mapping文件中<id>元素用来设置对象标识符,例如:
<id name="id" type="long" column="ID">
<generator class="assigned"/>
</id>其中的<generator>子元素用来设定OID的生成器
7)通常Hibernate的主键生成器可以取以下几种:
1)increment生成器。该生成器是Hibernate自己的生成器,不依赖于具体的数据库
任何数据库都可以使用
A:在Xxx.hbm.xml配置文件中设置主键生成器:
<id name="deptno" type="long">
<column name="DEPTNO" precision="2" scale="0" />
<generator class="increment"/>
</id>
B:执行save方法。会发生Hibernate会自动到初始阶段读取本表中最大主键值
select max(Id) from 表名
接下来向表中插入记录时,能在max(ID)的基础上递增,增量为1
C:下面考虑两上Hibernate应用进程访问同一个数据库的情景:
假定第一个进程中的Hibernate在初始化阶段读取表中的最大主键为6
接着第二个进程中的Hibernate在初始化阶段读取表中的最大主键为6
接下来两个进程中的Hibernate各自向表中插入主键值为7的记录会出现错误
D:结论increment不适合于多个进程同时访问数据库的情景
2)sequence生成器。该生成器是Hibernate调用Oralce数据库的sequence自动获取值之后 再进行填充的生成器.
A:在Xxx.hbm.xml配置文件中设置主键生成器:
<id name="deptno" type="long">
<column name="DEPTNO" precision="2" scale="0" />
<generator class="sequence">
<param name="sequence">chen_seq</param>
此处的name必须写成sequence形式
</generator>
</id>
B:在进行save方法时Hibernate先从底层数据库的chen_seq序列中获得一个唯一的
序列号,再把它做为主键值。
3)assigned主键由用户自己决定
评论