第三章:对像关系映射基础 本章内容: 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主键由用户自己决定

评论