Support Wikipedia Follow My Heart: Object Relational Mapping 学习笔记(1)

2012年8月2日星期四

Object Relational Mapping 学习笔记(1)

好久没写博客了,今天就写一写吧。这两天在研究jpa 2.0规范,做一下笔记,一方面加深印象,一方面也方便自己以后复习。

在公司做project的时候,看到有人是这样用jpa的,唉,完全没有体现jpa的价值所在啊。想来jpa技术虽然不难,但是也还是有一点门槛的,所以可能很多同学都会图方便(不用专门学习,不用手工写mapping),而采取这种方案。

jpa2

 

这个方案简单来说,就是通过额外编码(MyDTO, MyDTOFactory),事先将java 对象之间的关系解构,生成仅仅包含java 基本类的Entity对象。持久化时存储到数据库中的,也就仅仅是这些个简单的Entity对象而已。这种方式由于Entity中仅仅包含基本类,所以持久化时不需要添加特殊的jpa标签进行定制,非常容易上手,所以在公司内部的project中很有“人气”。 但是这种方案一方面会造成代码冗余,另外,基本没有体现出JPA技术的真正用处。

使用JPA是为了方便,为了不用手工将java object解构,为了使得代码更简明直观易于维护!因此,我们的目标是这样的:

jpa

 

Object千变万化,所以要实现上面的目标,不手工定制持久化方案是不行的。但是持久化的手工配置并不是太可怕的东西,一会儿就能学懂。

首先明确一点,Object中的基本类型数据的持久化是很简单的,不需要特别的标签(除非要设置fetch type)。 Object中的复杂类型数据(其他类,基本数据的集合,其他类的集合)等等(不妨称其为“子对象”),需要根据其“特点”和“与父对象的关系”,进行针对性的配置。

1, “特点”的分类

子对象的“特点”,这里指的是它的独立性。可以分为两类,即:

  1. 子对象自身包含区别于其他同类子对象的主键。因此在数据库中,它可以单独存储与其他数据表中,通过主键的链接,实现从父对象到子对象,或者子对象到父对象的查找操作。下文称这类子对象为“独立子对象”。E.g., Employee对象中有一个Department子对象,指向所述的部门对象。每个部门都有自己的编号。 demo
  2. 子对象自身没有主键,仅仅是父对象一部分状态数据的集合体,如果不嫌麻烦,拆开来一条一条写出来也一样的效果。在数据库中的体现就是子对象不是单独的数据表,而是“嵌入(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的持久化问题 --- 这个以后再写。。。。

1 条评论:

  1. Thanks for your blogging!
    I heard that using relation annotation (like @ManyToOne) is too buggy and heavy to use. How do you think? Is it possible to rely on?

    回复删除