第三章:域模型和元数据--POJO的getter和setter方法
(2012-04-19 20:59:56)
标签:
hibernateit |
分类: Hibernate和数据库 |
POJO的getter和setter方法
本文源于Hibernate in action
大关总结
1. 属性访问的类型和特点(4.4.1)
持久化引擎可以直接或者通过访问器(即get和set方法)访问属性。Hibernate中,使用XML配置形式,通过default-access设置默认的访问形式,可以有四种选项,default-access=”field|property|noop|custom.Class”。而使用Annotations形式的配置方式,默认的访问形式由@Id标记放置的位置决定,当@Id标记在属性上声明,而不是在getter方法上,则所有的其它属性默认采用属性访问形式(即不通过getter和setter方法,直接访问属性)。这种方式是JTA默认的。
当然,Hibernate提供了一种更灵活的方式,使用@org.hibernate.annotations.AccessType可以更改这种默认配置,策略如下:
(1)如果AccessType在类(实体)级别设置,类中的所有属性都使用这个默认访问方式,除非某个属性在其属性或者getter方法上重写了访问方式,重写访问方式将覆盖默认方式。但是类级别的设置将会重写@Id方式的默认访问方式。
(2)如果实体默认设置或者显示设置了属性访问,在属性上设置AccessType(“property”),更改访问方式为getter/setter。
(3)如果实体默认设置为property访问,而在getter方法上标记了AccessType(“field”),则getter方法在运行过程中将转换成field访问的形式。
(4)@Embedded将会继承拥有者类的默认访问方式。
(5)@MappedSuperclass与映射的实体有关。
2. 属性访问方法(getter和setter)(3.2.3)
你可以选择使用直接访问属性或者使用setter和getter方法的形式修改类属性。对于类的设计并不会受到Hibernate的具体访问形式的干扰。可以选择将类中部分属性的setter和getter方法设置为私有(例如,Id的setter方法一般为私有的,因为Id属性是通过系统自动生成而不是人为设定,还有其它系统设定属性可以设置为私有访问方法甚至将相应的getter和setter方法完全省略)。但是有些时候,getter和setter方法可能具有某些特殊意义。
2.1 在访问方法中添加用户逻辑
有时用户期望在访问方法中添加一些逻辑,如下例所示,希望在数据库中将用户名字分成firstName和lastName分别存储,而在用户获取的时候,name是一个属性,如下所示:
import org.hibernate.annotations.AccessType;
@Entity
@Table(name="USERS")
public class User {
}
测试代码如下:
输出信息是:
Hibernate:
Hibernate:
Stiffer Ruffer
数据库中的表内容是:
mysql> select * from users;
+---------+-----------------+----------------+
| USER_ID | USER_FIRST_NAME | USER_LAST_NAME |
+---------+-----------------+----------------+
|
+---------+-----------------+----------------+
1 row in set (0.02 sec)
在这个例子中,@Id标记在属性上,因此,Hibernate默认使用field形式直接访问属性(而不是getter和setter)。firstName和lastName被设置成私有的,并且没有提供getter和setter方法,但是Hibernate能够直接通过访问firstName和lastName属性,读取其值存储于数据库中,或者从数据库中读取值直接赋值给这两个属性。使用setUserName属性为firstName和lastName添加了用户自定义的逻辑。
在某些时候,可以在setter方法中添加数据验证逻辑,当setter的参数没有通过验证时,需要抛出异常,系统事务管理接收到异常会将事务自动回滚,并提示用户,用户提供的数据无法通过验证,产生异常。
2.2 多对一关系处理
现在考虑多对一关系,在多对一关系中,如果期望对象能够双向导航,需要创建对应的两个属性,我们以Student和Teacher模型为例,现在假设每个学生只能由一个老师管理,而每个老师可以管理多个学生,因此,这即形成了一个多对一的关系。此时,Student类中需要存放一个Teacher属性,而Teacher类中需要设置一个Student集合。解决这种双向导航的类问题远比想象中的复杂,而很多程序开发者并没有发现其内在的复杂性。下面以Teacher和Student建立关联关系为例,代码如下:
从实际逻辑上讲,在对Student设置了teacher属性时,teacher应当知道有一个新同学应当加入它的students集合中,因此第四句话似乎显得多余。从数据库角度看,关联关系只不过是一个外键的建立,就是STUDENT表中存放一个TEACHER_ID的外键(外键要放到多的一方),数据库中的关系永远是单向的,而对于多对一关系的双向导航类来说,需要将数据库中的一个单向关系映射成一个双向关系,此时,按照传统的getter和setter方法将无法满足需求。因此使用下面的代码和策略:
@Entity
@Table(name="STUDENT")
public class Student {
}
}
@Entity
@Table(name="TEACHER")
public class Teacher {
}
在为Student添加Teacher时,需要将student和其原有teacher间的关系撤销,之后将当前teacher更新新值,并将自己添加到新teacher的student集合中。另一方面,Teacher需要提供添加单个学生的方法addStudent和移除student的方法removeStudent,需要注意的是,Teacher类没有提供setStudents方法,因为,我们不期望提供对students集合的直接更改,students集合的更改应当是通过Student类的addTeacher方法间接完成的。(需要注意的是addStudent方法和removeStudent方法,只是为Student类提供操作students集合的方法,对于外部逻辑应该不能直接使用这些方法,我们找不到一个更好的可见性关键字形容它,因此使用了public(感觉C++中的friend关键字,似乎满足需求,但java没有))。在Teacher类中,对获取students集合的方法进行了限制,可以使用Collections.unmodifiableCollection(c)或者Collection.unmodifiableSet(c)两个方法来限制返回的集合不会被外部修改。因此,如果想修改Student和Teacher间的关联关系,只能通过Student的setTeacher方法,但是,如果想访问他们之间的关系,则可以通过使用Student中的getTeacher方法或者使用Teacher中的getStudents方法,因此,针对修改是单向的,而针对访问是双向的。现在当需要建立Student和Teacher间的关系时,只需要下面的语句。
双向导航关系的代码被称为脚架式代码(scaffolding code)。在数据库角度来看,因为外键是建立在Student一侧,因此,只有Student的Teacher属性被设置了,当前的student才具有一个外键,如果仅Teacher的Student集合中包含了student对象,而student对象并没有设置Teacher属性(即设置是单向的),在数据库中该student条目将不会产生对应与teacher的外键。如下代码所示:
此时数据库中不会建立student和teacher的关系。在数据库中t的外键是NULL。
多对多关系与上述处理方式类似,这里不再累述。