数据关联是ORM中很重要的一个特征。数据关联又分为一对一、一对多、多对多三种,其中每种又可分为单向关联和双向关联。由于对于一对多关联的理解不是很透彻,所以决定自己动手写一个程序,以加深理解。
由于双向一对多关联就是一方单向一对多和另一方单向多对一关联的组合,所以弄懂双向一对多关联后,单向的就更不在话下。经常用到双向一对多关联的地方就是主从表(也叫主表和明细表),例如网上购物时的预定单,就可分成预订单主表和预订单明细表。就来写一段保存预订单的程序。
第一步:建立实体模型
需要定义两个实体类,Order类对应于主表,Orderdetail类对应为明细表。主表中包含了发出订单的用户(user)和日期(date),明细表包含了预定的书(book)及其数目(num)。正常情况下,属性user和属性book的类型应该分别为实体类User和Book的,这里为了方便,就设为String了。
第二步:在数据库中建表
我用的是Mysql。建立两张表,主表order和明细表orderdetail。
CREATE
TABLE
`
order
` ( `id`
int
(
10
) unsigned
NOT
NULL
auto_increment, `
user
`
varchar
(
45
)
NOT
NULL
, `date`
datetime
NOT
NULL
,
PRIMARY
KEY
(`id`) )
CREATE
TABLE
`orderdetail` ( `id`
int
(
10
) unsigned
NOT
NULL
auto_increment, `book`
varchar
(
45
)
NOT
NULL
, `num`
int
(
10
) unsigned
NOT
NULL
, `orderid`
int
(
10
) unsigned
NOT
NULL
default
'
0
'
,
PRIMARY
KEY
(`id`),
KEY
`FK_orderdetail_1` (`orderid`),
CONSTRAINT
`FK_orderdetail_1`
FOREIGN
KEY
(`orderid`)
REFERENCES
`
order
` (`id`) )
明细表orderdetail中包含一个orderid字段,此字段与主表order的id字段相关联,即“唯一外键关联”。
第三步:建立hbm.xml映射文件和实体类
利用MyEclipse直接从数据库生成hbm.xml文件和实体类Order、Orderdetail。
<
hibernate-mapping
>
<
class
name
="entity.Order"
table
="order"
schema
="test"
>
<
id
name
="id"
type
="integer"
>
<
column
name
="id"
/>
<
generator
class
="native"
/>
</
id
>
<
property
name
="user"
type
="java.lang.String"
>
<
column
name
="user"
length
="45"
not-null
="true"
/>
</
property
>
<
property
name
="date"
type
="java.util.Date"
>
<
column
name
="date"
length
="19"
not-null
="true"
/>
</
property
>
<
set
name
="orderdetails"
inverse
="true"
cascade
="all"
>
<
key
>
<
column
name
="orderid"
not-null
="true"
/>
</
key
>
<
one-to-many
class
="entity.Orderdetail"
/>
</
set
>
</
class
>
</
hibernate-mapping
>
<
hibernate-mapping
>
<
class
name
="entity.Orderdetail"
table
="orderdetail"
schema
="test"
>
<
id
name
="id"
type
="integer"
>
<
column
name
="id"
/>
<
generator
class
="native"
/>
</
id
>
<
many-to-one
name
="order"
class
="entity.Order"
>
<
column
name
="orderid"
not-null
="true"
/>
</
many-to-one
>
<
property
name
="book"
type
="java.lang.String"
>
<
column
name
="book"
length
="45"
not-null
="true"
/>
</
property
>
<
property
name
="num"
type
="java.lang.Integer"
>
<
column
name
="num"
not-null
="true"
/>
</
property
>
</
class
>
</
hibernate-mapping
>
修改主键的生成方式为native。
注意:1.在Order.hbm.xml中set节点的属性inverse设为true。inverse=“false”的一方为主动方,由主动方负责维护关联关系。在one-to-many关系中,把many的一方设为主动方,即inverse=false,有助于提高性能。
2.属性inverse和cascade的区别。inverse指的是关联关系的控制方向,而cascade是指对象的级联关系。如这里cascade设为all,表示如果发生对order对象的操作,则需要对order所关联的对象也进行同样的操作。如对order进行save,则hibernate也会自动对order关联的ordertail进行save。
第四步:编写一个测试程序,进行保存预定单的测试。
public
class
test
extends
TestCase
...
{ public void testAddOrder() ... { Order order = new Order(); order.setDate( new Date()); order.setUser( " rose " ); Orderdetail od = new Orderdetail(); od.setBook( " 尘缘 " ); od.setNum( 1 ); od.setOrder(order); Orderdetail od2 = new Orderdetail(); od2.setBook( " 亵渎 " ); od2.setNum( 2 ); od2.setOrder(order); order.getOrderdetails().add(od); order.getOrderdetails().add(od2); OrderDao odao = new OrderDao(); odao.saveOrder(order); } }
其中调用的了OrderDao的saveOrder()方法,由于Dao比较简单,这里就不贴出来了。
注意:
1.odao.saveOrder(order);即为通过主动方进行级联更新。save了order的同时也save了orderdetail。
2.由于Order.hbm.xml中把inverse设为true,即指定了由Orderdetail来维护关联关系。那么Orderdetail的属性orderid也就是由Orderdetail自己去维护了,所以必须 有:od.setOrder(order)和od2.setOrder(order)。否则,测试将通不过。
最后,进行测试,为小绿条
PS:在测试过程中遇到一个很郁闷的问题。由于以前在网上看过文章,说利用Eclipse工具自动生成hbm.xml文件时,应该把class节点的属性schema=“数据库的名字”删掉,否则会出现错误。于是我在hbm.xml文件生成后,迫不及待地把schema删掉。结果一直出错:ERROR [main] (JDBCExceptionReporter.java:72) - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order (user, date) values ('rose', '2007-07-08 00:40:21')' at line 1
把schema属性补上后,才测试通过。