好久没写博客了,今天就写一写吧。这两天在研究jpa 2.0规范,做一下笔记,一方面加深印象,一方面也方便自己以后复习。
在公司做project的时候,看到有人是这样用jpa的,唉,完全没有体现jpa的价值所在啊。想来jpa技术虽然不难,但是也还是有一点门槛的,所以可能很多同学都会图方便(不用专门学习,不用手工写mapping),而采取这种方案。
这个方案简单来说,就是通过额外编码(MyDTO, MyDTOFactory),事先将java 对象之间的关系解构,生成仅仅包含java 基本类的Entity对象。持久化时存储到数据库中的,也就仅仅是这些个简单的Entity对象而已。这种方式由于Entity中仅仅包含基本类,所以持久化时不需要添加特殊的jpa标签进行定制,非常容易上手,所以在公司内部的project中很有“人气”。 但是这种方案一方面会造成代码冗余,另外,基本没有体现出JPA技术的真正用处。
使用JPA是为了方便,为了不用手工将java object解构,为了使得代码更简明直观易于维护!因此,我们的目标是这样的:
Object千变万化,所以要实现上面的目标,不手工定制持久化方案是不行的。但是持久化的手工配置并不是太可怕的东西,一会儿就能学懂。
首先明确一点,Object中的基本类型数据的持久化是很简单的,不需要特别的标签(除非要设置fetch type)。 Object中的复杂类型数据(其他类,基本数据的集合,其他类的集合)等等(不妨称其为“子对象”),需要根据其“特点”和“与父对象的关系”,进行针对性的配置。
1, “特点”的分类
子对象的“特点”,这里指的是它的独立性。可以分为两类,即:
- 子对象自身包含区别于其他同类子对象的主键。因此在数据库中,它可以单独存储与其他数据表中,通过主键的链接,实现从父对象到子对象,或者子对象到父对象的查找操作。下文称这类子对象为“独立子对象”。E.g., Employee对象中有一个Department子对象,指向所述的部门对象。每个部门都有自己的编号。
- 子对象自身没有主键,仅仅是父对象一部分状态数据的集合体,如果不嫌麻烦,拆开来一条一条写出来也一样的效果。在数据库中的体现就是子对象不是单独的数据表,而是“嵌入(Embedded)”到父对象的表中。下文称其为“嵌入子对象”。比如Employee对象中,需要记录本人的家庭住址,而住址包括邮编,街道,市区等等信息。这些信息统合为Address子对象,并没有独立的标识,而是嵌入到某一个具体的Employee对象中。
本文主要介绍独立子对象的持久化配置方法。
2, “关系”的分类
对于独立自对象,其与父对象的关系,无外乎以下四类,即:
No. | 对应父对象数量 | 对应子对象数量 |
1 | Many | One |
2 | One | One |
3 | One | Many |
4 | Many | Many |
3,“Many-to-One”关系如何持久化
这可能是最常见的一组关系,比如刚才举的Employee(父对象)和Department(子对象)的关系。该关系的持久化设置只需要针对父对象的java class就行,子对象的class不用碰。当然了,子对象持久化需要的基本配置(比如@Entity, @Id 等标签)还是需要的。
最简单的例子:
Employee Class
这个例子的实质,是在Employee对应的数据表中,添加一列,用于存储对应的Department的Key。 列名是默认方法生成的(如果Department表中的主键列名是ID的话,那么上例对应的列名就是“Department_ID”)。 如果您想自定义列名(比如改为“CustomeName”),或者您必须自定义列名(比如DB Schema已经有了,不能改),那么请参考下面的例子。
稍微复杂一点的例子:
Employee Class
上面的例子体现的关系,都是从Employee到Department的单向ManyToOne关系。如果在Department中加入了List<Employee> employees 这样的属性,就使得Department对象具有了从Department到Employee的OneToMany关系(这时候Deparment变成父对象,Employee就变成子对象了)。在这种情况下,如果仅仅配置了Employee对象的ManyToOne关系是不够的,还必须如后文所述,对Department的OneToMany关系也进行特别配置。
4,“One-to-One”关系如何持久化
比如一位员工需要有一个独立的衣橱。每一个独立的衣橱都对应一位员工。所以员工和衣橱的关系就是OneToOne关系。
OneToOne关系的配置和ManyToOne关系的配置很类似,只要修改标签的名字就可以了,比如下面这个简单的例子。
Employee Class
Wardrobe Class
如果您想修改JPA自动生成的外键列列名,那么更上文所述方法一样,在Employee类的myWardrobe属性前,加入@JoinColumn标签进行配置。
如果这时候Wardrobe类中也加入了Employee类的引用,那么就使得Wardrobe类也具有了从Wardrobe到Employee类的OneToOne关系。所以持久化时也需要特别配置。 Employee对象的配置方法不变,Wardrobe对象如下所述进行配置。
Wardrobe Class
这里的配置,就是加入了“mappedBy”属性,告诉JPA,这个OneToOne关系,和Employee类里为“myWardrobe” 属性配置的OneToOne关系是一对的,修改的时候一定要连动更新。
5,“One-to-Many”关系如何持久化
这是另一种非常常用的关系。其内容与ManyToOne关系正好相反。对于ManyToOne关系,JPA会自动生成外键匹配列。然而,对于OneToMany关系来说,情况就复杂了。简单来说,在一个关系数据表中,仅仅一行数据,是很难在某一列中存储任意多个值的(除非把事先通过编码把这些值合成一个(比如序列化),以后使用时也额外编码来实现子对象的索引,但是这就不是JPA的方式了。)。所以,JPA的做法,是要求用户将OneToMany关系,转化为与其对应的ManyToOne关系,强制在子对象中加入指向父对象的引用。
复杂么?其实不复杂,请看下面的例子。
JPA无法完成持久化的初始代码:
Failed Example of One-To-Many
JPA可以接受的等价方式:
OK Example of One-To-Many
如果您想修改JPA自动生成的外键列列名,那么更上文所述方法一样,在Employee类的myDept属性前加入@JoinColumn标签进行配置。另外,
OK Example of One-To-Many
6,“Many-to-Many”关系如何持久化
乍一看这个关系可能不好理解,其实现实生活中还是挺常见的,比如Employee和Project的关系。一个Project有很多成员,一位员工可能同时属于多个项目组。这个关系的配置给OneToMany关系很像,比如要父子对象都拥有指向对方的引用。换句话说,他们之间的关系是平等的,都是ManyToMany,也就是说,我们认为哪一个关系是父对象都没关系。
这个关系的配置方法是,任选一个对象作为子对象;对于子对象必须声明“mappedBy”属性,其他一样。
Failed Example of Many-To-Many
OK Example of Many-To-Many 1
OK Example of Many-To-Many 2
ManyToMany关系在JPA中处理时,其实是在后台建立了一个JoinTable,用来存储父子对象主键的组合关系。如果您需要自定义这个JoinTable的表名或着列名,那么可以在ManyToMany关系的父对象(那个都OK,选一个)定义。
Custome Join Table
7,其他本文中没有讲到的东西
JPA中最简单的基本数据类型的持久化 ---- 这个大家随便google一下就明白了。
嵌入式对象的持久化问题 ---- 这个下一篇学习笔记里面写。
高级mapping,比如List和Map的持久化问题 --- 这个以后再写。。。。