本文描述类图和ORM映射的关系。在例子部分,小鸡射手采用EJB 3.0 Annotation方式为主,数据库是HSQLDB。由于例子比较多,全文分三部分。
1 类图与ORM的关系
面向对象的分析类包括边界类、控制类和实体类,需要持久化的是实体类。
UML类图中,类和类的关系包括:
- 关联(Associate)
- 泛化(Generalize)
- 聚集(Aggregate)
- 复合(Compose)
- 多元关联(Association)
ORM映射方式如下:
- 复合关系:采用值类型(Value Type)映射,包括单个值类型和值类型集合
- 泛化关系:采用继承映射,包括:
一个继承结构使用一个表(Table per class hierarchy)
各子类使用单独的表(Table per subclass)
支持多态的各具体实体类使用单独的表(Table per concrete class with implicit polymorphism)
采用Union的各具体实体类使用单独的表(Table per concrete class with unions)
- 其他关系采用关联映射,包括:
单向ManyToOne,这是最基本、最重要的关联;
单向OneToMany,这是涉及到集合的最主要关联;
双向ManyToOne;
单向OneToOne;
双向OneToOne;
单向ManyToMany;
双向ManyToMany,一般地最好实现为两个OneToMany关系;
三元关联(Association)采用中间表和Map实现。
2 值类型映射
ORM映射不是简单地将一个类映射成一个数据库表。对象模型一般是细颗粒度,例如User类和Address类是复合关系,Address类可以映射为值对象。相对于实体,值对象不需要主键(Identity),和复合关系一样,它的生命期和所属实体一致。在ORM映射中,
应该尽量使用值对象。
下面的例子描述User类和Address类的映射。简单起见实体中只包括字段定义,如果需要增加字段属性如NOT NULL,可以增加@Column(nullable = false)等,例子中均省略了。
@Embeddable

public
class
Address1
...
{
private String street;
}

@Entity

public
class
User1
...
{
@Id private int id;
private String name;
@Embedded Address1 homeAddress;
}












由于Address类是值类型,数据库表只有一个:
create
table
"USER1"(
"ID"
INTEGER
not
null
,
"NAME"
VARCHAR
(
255
),
"STREET"
VARCHAR
(
255
),
constraint
"SYS_PK_1637"
primary
key
("ID")
);

create
unique
index
"SYS_PK_1637"
on
"USER1"("ID");







如果User类中有多个Address对象,例如homeAddress和companyAddress,则需要字段更名:
@Embeddable

public
class
Address2
...
{
private String street;
}

@Entity

public
class
User2
...
{
@Id private int id;
private String name;
@Embedded Address2 homeAddress;

@Embedded @AttributeOverrides(...{
@AttributeOverride(name="street",column=@Column(name="COMPANY_STREET"))})
Address2 companyAddress;
}















数据库表如下:
create
table
"USER2"(
"ID"
INTEGER
not
null
,
"NAME"
VARCHAR
(
255
),
"STREET"
VARCHAR
(
255
),
"COMPANY_STREET"
VARCHAR
(
255
),
constraint
"SYS_PK_1638"
primary
key
("ID")
);

create
unique
index
"SYS_PK_1638"
on
"USER2"("ID");








如果希望Address类既是值类型,数据又放在单独的表中,可以采用@SecondaryTable实现:
@Embeddable

public
class
Address3
...
{
private String street;
}

@Entity

@SecondaryTable(name
=
"
USER3_ADDRESS3
"
,
pkJoinColumns
=
...
{@PrimaryKeyJoinColumn(name="id")}
)

public
class
User3
...
{
@Id private int id;
private String name;
@Embedded

@AttributeOverrides(...{
@AttributeOverride(name = "street",
column = @Column(name="STREET", table = "USER3_ADDRESS3"))})
private Address3 homeAddress;
}

















数据库表如下:


















JPA规范目前不支持值类型的集合。如果采用Hibernate,则可以使用Hibernate特有的Annotation实现值类型集合。值类型可以是简单类型(如String),也可以是Value Type(如Address);Hibernate支持的集合类型包括:
Set:集合中不能包括重复的元素;
Bag:集合中允许重复元素,但不是排序的;
List:集合元素记录了位置信息;
Map:集合元素是<key,value>,并且不是排序的;
SortedSet:排序的Set集合;
SortedMap:排序的Map元素;
OrderedSet:数据库排序的Set集合;
OrderedMap:数据库排序的Map集合;
OrderedBag:数据库排序的Bag集合。
由于这不是JPA支持的特性,仅举两个例子说明。第一个例子是User类有一个String值类型的Set:
@Entity

public
class
User4
...
{
@Id private int id;
private String name;
@org.hibernate.annotations.CollectionOfElements
@JoinTable(name = "User4_Street4", joinColumns = @JoinColumn(name = "id"))
@Column(name = "street", nullable = false)
private Set<String> streets = new HashSet<String>();
}










数据库表如下:


















第二个例子是User类有一个Address值类型的Set:























数据库表如下:















