- 浏览: 233410 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
shuhucy:
必须赞啊,源码理解的很深,解决一个困扰两天的问题
Spring AOP源码分析(八)SpringAOP要注意的地方 -
sealinesu:
精彩
Spring事务源码分析(一)Spring事务入门 -
whlt20090509:
"WEB-INF/view目录下有一个简单的hell ...
SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门 -
hai0378:
兄台 算我一个,最近在研究dubbo motan 及 zk ...
ZooKeeper源码研究寻求小伙伴 -
zpkbtzmbw:
看懂了,原理
SpringMVC源码总结(五)Tomcat的URIEncoding、useBodyEncodingForURI和CharacterEncodingFilter
上一篇文章介绍了基本知识后,本篇该介绍下现实中的一对多的关联关系。如Customer和Order,一个Customer可以拥有多个Order,每个Order只属于一个Customer。这样就存在几种表示形式,可以分为单向关联和双向关联。
形式1:Order拥有一个Customer引用,这种属于单向关联
形式2:Customer拥有Order的集合,这种也属于单向关联
形式3:Order拥有一个Customer引用,同时Customer拥有Order集合,这种属于双向关联
先来说说形式1:Order拥有一个Customer引用
Customer还是上一篇文章的形式:
customer表的信息是:
它的映射文件为Customer.hbm.xml:
然后是Order类:
order表的信息如下:
对应的映射文件为Order.hbm.xml:
然后我们看下这种单向关联的形式的增添和查询。增添如下:
这个过程先保存了Customer对象,然后保存了Order对象,此时一切正常,如下sql语句:
然而当我们不保存Customer对象时,即不执行上述的session.save(customer),则会报如下错误:
说Order对象引用了未保存的Customer对象,如果希望customer_id可以为空,单独保存Order,则需要在Order.hbm.xml中指明customer_id字段是可以为空的,即not-null="false"(默认就是false,所以不用再设置)。同时,不能执行setCustomer(customer);这样的语句,要保留Customer为空,一旦不为空,并且属于没有保存的对象,则就会报上述错误。
如果希望,在保存Order的时候,同时级联的保存Customer对象,则需要在Order.hbm.xml中指明customer_id字段的cascade="save-update",此时就会保存或者更新Order的同时级联的保存
Customer对象,如下sql:
对于查询Order,如下:
查询sql如下:
会先去查询Order信息,然后当你使用到customer时,才会进一步去查询Customer信息,这就是延迟加载,此时的customer对象仅仅是一个代理对象。如果你想查询Order信息时同时把Customer信息查询出来,就需要在Order.hbm.xml的customer_id字段中指定lazy="false",默认是"proxy"。如下效果:
先把所有信息都查出来,然后供使用。目前lazy的取值有三个,false、proxy、no-proxy。false代表:查询Order信息时,立马把Customer的信息查出来,此时order.getCustomer()就是一个Customer对象。proxy代表:查询Order信息时,并不会去查询Customer信息,只有当你用到order的Customer时才会去查询,此时order.getCustomer()返回的是一个代理对象。no-proxy:目前的效果和proxy一样,这一块我还需要继续研究。
然后来说说形式2:Customer拥有Order的集合
其中Order类为:
Order对应的映射文件为:
Customer对应的类为:
Customer对应的映射文件为:
形式2的增添为:
会产生如下sql语句:
当保存完Customer对象后,由于cascade="save-update"设置,会级联的保存Customer所包含的Order集合,但是此时的Order没有保存customer_id属性,在事务提交前,才会去检查和更新Order的customer_id属性,这个关系是有Customer维护的,当Customer的映射文件中<set>标签的inverse="true"时,即Customer放弃维护这个关系时,此时就只有三个insert语句了:
此时的数据就会不完整了,所以inverse必须为true,由Customer去维护关系,此时又会造成多出两个update语句,无法消除,所以一般不采用形式2,关联关系尽量由many的一方来维护。
对于形式2的查询,可以从Customer去查到它所包含的Order,但是从Order就查不到它所在的Customer,只能手写sql去查出customer_id的值,然后查出对应的Customer,所以更不会采用形式2了。
说完了形式2,来说说形式3:Order拥有一个Customer引用,同时Customer拥有Order集合
若仍想使用上述的数据库的表结构,即order表中含有一个customer_id信息。则映射文件的配置要做相应的修改。
对于Customer类:
对于Customer的映射文件:
比之前多加了一个list,这里有一个无法理解的地方。对于List,是虽说是有序的,但是有时候我并不看重这个顺序,而它这里强制性加上了一个<list-index>标签或者<index>标签,不然就报错,这个标签的作用就是对于Customer的List<Order>集合从base开始编号,然后把这个编号强制到数据库order表中的column="list_index"指定的字段上,也就是说,数据库的Order表必须多余的添加一个list_index字段用来存储编号。我无法忍受这样的强制性,各种各样的需求在很多情况下都存在的,不要以为你认为有几种情况就只有几种情况。为了不需要这样的字段,我们只能使用Set,这就必须要求Customer中不是List<Order>而是Set<Order>。所以这一块,我无法理解,还希望知道的网友帮我解答这个疑问。
对于Order没有变化和上面的一样。
测试添加:
会看到如下的sql语句:
为什么会有两个update语句呢?
还是因为那个list-index标签要存储编号到order的list_index字段上。
这里我就更该List为Set,同时还要重写Order类的equals和hashcode方法,要满足orderNumber和customer_id都一致时视为重复,为什么这样做可以看我的之前的一篇专门讲述这个事情的文章(http://lgbolgger.iteye.com/blog/2115446)。
更该为set后又产生了一个新的问题,上述sql语句中insert into hibernate.order (orderNumber, customer_id) values (?, ?)其中customer_id是有值的,但是更该为Set后就变成null,导致后来又补上两个update语句把customer_id值补上去了。
如下sql:
越来越感觉挺简单的东西让hibernate搞的逻辑更加复杂了。这时候就需要这样做了,建立两者的双重关联,并且在配置文件中让one方放弃维护两者关系,在set标签中使用inverse="true"。即在Customer.hbm.xml中如下更该:
在代码中需要建立双重关系,如下:
此时,一切就正常了,只有三个insert语句,如下:
对于形式3的查询,从Customer查Order:
此时也可以通过设定Customer.hbm.xml中set标签的lazy="false",来提前查询Order,默认是用到Order时才会去查询。
从Order到Customer的查询也是类似的。
若想转载请注明出处: http://lgbolgger.iteye.com/blog/2124860
作者:iteye的乒乓狂魔
形式1:Order拥有一个Customer引用,这种属于单向关联
形式2:Customer拥有Order的集合,这种也属于单向关联
形式3:Order拥有一个Customer引用,同时Customer拥有Order集合,这种属于双向关联
先来说说形式1:Order拥有一个Customer引用
Customer还是上一篇文章的形式:
public class Customer { private Long id; private String name; private String email; private Timestamp registeredTime; //省略get、set方法 }
customer表的信息是:
CREATE TABLE `customer` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) DEFAULT '', `email` varchar(45) DEFAULT '', `registeredTime` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
它的映射文件为Customer.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class name="com.ligang.domain.Customer" table="customer"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="name" column="name" type="string"/> <property name="email" column="email" type="string"/> <property name="registeredTime" column="registeredTime" type="timestamp"/> </class> </hibernate-mapping>
然后是Order类:
public class Order { private Long id; private String orderNumber; private Customer customer; //省略get、set方法 }
order表的信息如下:
CREATE TABLE `order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `orderNumber` varchar(45) DEFAULT '', `customer_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
对应的映射文件为Order.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class name="com.ligang.domain.Order" table="order"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="orderNumber" column="orderNumber" type="string"/> <many-to-one name="customer" column="customer_id" class="com.ligang.domain.Customer"/> </class> </hibernate-mapping>
然后我们看下这种单向关联的形式的增添和查询。增添如下:
@Test public void testAddOrder(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Customer customer=new Customer(); customer.setName("校长"); customer.setEmail("sdsd@qq.com"); customer.setRegisteredTime(getCurrentTime()); session.save(customer); System.out.println(customer.getId()+":"+customer.getName()); Order order1=new Order(); order1.setCustomer(customer); order1.setOrderNumber("第一个订单"); Order order2=new Order(); order2.setCustomer(customer); order2.setOrderNumber("第二个订单"); session.save(order1); session.save(order2); tx.commit(); session.close(); }
这个过程先保存了Customer对象,然后保存了Order对象,此时一切正常,如下sql语句:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) 33:校长 Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?)
然而当我们不保存Customer对象时,即不执行上述的session.save(customer),则会报如下错误:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.ligang.domain.Customer
说Order对象引用了未保存的Customer对象,如果希望customer_id可以为空,单独保存Order,则需要在Order.hbm.xml中指明customer_id字段是可以为空的,即not-null="false"(默认就是false,所以不用再设置)。同时,不能执行setCustomer(customer);这样的语句,要保留Customer为空,一旦不为空,并且属于没有保存的对象,则就会报上述错误。
如果希望,在保存Order的时候,同时级联的保存Customer对象,则需要在Order.hbm.xml中指明customer_id字段的cascade="save-update",此时就会保存或者更新Order的同时级联的保存
Customer对象,如下sql:
null:校长 Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?)
对于查询Order,如下:
@Test public void testGetOrder(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Order order=(Order)session.get(Order.class,9L); System.out.println(order.getOrderNumber()); System.out.println(order.getCustomer().getName()); tx.commit(); session.close(); }
查询sql如下:
Hibernate: select order0_.id as id1_1_0_, order0_.orderNumber as orderNum2_1_0_, order0_.customer_id as customer3_1_0_ from hibernate.order order0_ where order0_.id=? 第二个订单 Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=? 校长
会先去查询Order信息,然后当你使用到customer时,才会进一步去查询Customer信息,这就是延迟加载,此时的customer对象仅仅是一个代理对象。如果你想查询Order信息时同时把Customer信息查询出来,就需要在Order.hbm.xml的customer_id字段中指定lazy="false",默认是"proxy"。如下效果:
Hibernate: select order0_.id as id1_1_0_, order0_.orderNumber as orderNum2_1_0_, order0_.customer_id as customer3_1_0_ from hibernate.order order0_ where order0_.id=? Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=? 第二个订单 校长
先把所有信息都查出来,然后供使用。目前lazy的取值有三个,false、proxy、no-proxy。false代表:查询Order信息时,立马把Customer的信息查出来,此时order.getCustomer()就是一个Customer对象。proxy代表:查询Order信息时,并不会去查询Customer信息,只有当你用到order的Customer时才会去查询,此时order.getCustomer()返回的是一个代理对象。no-proxy:目前的效果和proxy一样,这一块我还需要继续研究。
然后来说说形式2:Customer拥有Order的集合
其中Order类为:
public class Order { private Long id; private String orderNumber; //略get、set方法 }
Order对应的映射文件为:
<hibernate-mapping> <class name="com.ligang.domain.Order" table="order"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="orderNumber" column="orderNumber" type="string"/> </class> </hibernate-mapping>
Customer对应的类为:
public class Customer { private Long id; private String name; private String email; private Timestamp registeredTime; private Set<Order> orders; //略get、set方法 }
Customer对应的映射文件为:
<hibernate-mapping> <class name="com.ligang.domain.Customer" table="customer"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="name" column="name" type="string"/> <property name="email" column="email" type="string"/> <property name="registeredTime" column="registeredTime" type="timestamp"/> <set name="orders" cascade="save-update"> <key column="customer_id"/> <one-to-many class="com.ligang.domain.Order"/> </set> </class> </hibernate-mapping>
形式2的增添为:
@Test public void testAddCustomerInverse(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Customer customer=new Customer(); customer.setName("校长"); customer.setEmail("sdsd@qq.com"); customer.setRegisteredTime(getCurrentTime()); Set<Order> orders=new HashSet<Order>(); Order order1=new Order(); order1.setOrderNumber("第一个订单"); Order order2=new Order(); order2.setOrderNumber("第二个订单"); orders.add(order1); orders.add(order2); customer.setOrders(orders); session.save(customer); tx.commit(); session.close(); }
会产生如下sql语句:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber) values (?) Hibernate: insert into hibernate.order (orderNumber) values (?) Hibernate: update hibernate.order set customer_id=? where id=? Hibernate: update hibernate.order set customer_id=? where id=?
当保存完Customer对象后,由于cascade="save-update"设置,会级联的保存Customer所包含的Order集合,但是此时的Order没有保存customer_id属性,在事务提交前,才会去检查和更新Order的customer_id属性,这个关系是有Customer维护的,当Customer的映射文件中<set>标签的inverse="true"时,即Customer放弃维护这个关系时,此时就只有三个insert语句了:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber) values (?) Hibernate: insert into hibernate.order (orderNumber) values (?)
此时的数据就会不完整了,所以inverse必须为true,由Customer去维护关系,此时又会造成多出两个update语句,无法消除,所以一般不采用形式2,关联关系尽量由many的一方来维护。
对于形式2的查询,可以从Customer去查到它所包含的Order,但是从Order就查不到它所在的Customer,只能手写sql去查出customer_id的值,然后查出对应的Customer,所以更不会采用形式2了。
说完了形式2,来说说形式3:Order拥有一个Customer引用,同时Customer拥有Order集合
若仍想使用上述的数据库的表结构,即order表中含有一个customer_id信息。则映射文件的配置要做相应的修改。
对于Customer类:
public class Customer { private Long id; private String name; private String email; private Timestamp registeredTime; private List<Order> orders; //略get、set方法 }
对于Customer的映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class name="com.ligang.domain.Customer" table="customer"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="name" column="name" type="string"/> <property name="email" column="email" type="string"/> <property name="registeredTime" column="registeredTime" type="timestamp"/> <list name="orders" cascade="save-update"> <key column="customer_id"/> <list-index base="0" column="list_index"/> <one-to-many class="com.ligang.domain.Order"/> </list> </class> </hibernate-mapping>
比之前多加了一个list,这里有一个无法理解的地方。对于List,是虽说是有序的,但是有时候我并不看重这个顺序,而它这里强制性加上了一个<list-index>标签或者<index>标签,不然就报错,这个标签的作用就是对于Customer的List<Order>集合从base开始编号,然后把这个编号强制到数据库order表中的column="list_index"指定的字段上,也就是说,数据库的Order表必须多余的添加一个list_index字段用来存储编号。我无法忍受这样的强制性,各种各样的需求在很多情况下都存在的,不要以为你认为有几种情况就只有几种情况。为了不需要这样的字段,我们只能使用Set,这就必须要求Customer中不是List<Order>而是Set<Order>。所以这一块,我无法理解,还希望知道的网友帮我解答这个疑问。
对于Order没有变化和上面的一样。
测试添加:
@Test public void testTwoRelation(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Customer customer=new Customer(); customer.setName("校长"); customer.setEmail("sdsd@qq.com"); customer.setRegisteredTime(getCurrentTime()); List<Order> orders=new ArrayList<Order>(); Order order1=new Order(); order1.setOrderNumber("第一个订单"); Order order2=new Order(); order2.setOrderNumber("第二个订单"); orders.add(order1); orders.add(order2); customer.setOrders(orders); session.save(customer); tx.commit(); session.close(); }
会看到如下的sql语句:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: update hibernate.order set customer_id=?, list_index=? where id=? Hibernate: update hibernate.order set customer_id=?, list_index=? where id=?
为什么会有两个update语句呢?
还是因为那个list-index标签要存储编号到order的list_index字段上。
这里我就更该List为Set,同时还要重写Order类的equals和hashcode方法,要满足orderNumber和customer_id都一致时视为重复,为什么这样做可以看我的之前的一篇专门讲述这个事情的文章(http://lgbolgger.iteye.com/blog/2115446)。
更该为set后又产生了一个新的问题,上述sql语句中insert into hibernate.order (orderNumber, customer_id) values (?, ?)其中customer_id是有值的,但是更该为Set后就变成null,导致后来又补上两个update语句把customer_id值补上去了。
如下sql:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: update hibernate.order set customer_id=? where id=? Hibernate: update hibernate.order set customer_id=? where id=?
越来越感觉挺简单的东西让hibernate搞的逻辑更加复杂了。这时候就需要这样做了,建立两者的双重关联,并且在配置文件中让one方放弃维护两者关系,在set标签中使用inverse="true"。即在Customer.hbm.xml中如下更该:
<hibernate-mapping> <class name="com.ligang.domain.Customer" table="customer"> <id name="id" column="id" type="long"> <generator class="identity"/> </id> <property name="name" column="name" type="string"/> <property name="email" column="email" type="string"/> <property name="registeredTime" column="registeredTime" type="timestamp"/> <set name="orders" cascade="save-update" inverse="true"> <key column="customer_id"/> <one-to-many class="com.ligang.domain.Order"/> </set> </class> </hibernate-mapping>
在代码中需要建立双重关系,如下:
@Test public void testTwoRelation(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Customer customer=new Customer(); customer.setName("校长"); customer.setEmail("sdsd@qq.com"); customer.setRegisteredTime(getCurrentTime()); Set<Order> orders=new HashSet<Order>(); Order order1=new Order(); order1.setCustomer(customer); order1.setOrderNumber("第一个订单"); Order order2=new Order(); order2.setCustomer(customer); order2.setOrderNumber("第二个订单"); orders.add(order1); orders.add(order2); customer.setOrders(orders); session.save(customer); tx.commit(); session.close(); }
此时,一切就正常了,只有三个insert语句,如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?) Hibernate: insert into hibernate.order (orderNumber, customer_id) values (?, ?)
对于形式3的查询,从Customer查Order:
@Test public void testGetTwoRelation(){ Session session=hibernateDao.getSession(); Transaction tx=session.beginTransaction(); Customer customer=(Customer)session.get(Customer.class,48L); System.out.println(customer.getName()); for(Order order:customer.getOrders()){ System.out.println(order.getOrderNumber()); } tx.commit(); session.close(); }
此时也可以通过设定Customer.hbm.xml中set标签的lazy="false",来提前查询Order,默认是用到Order时才会去查询。
从Order到Customer的查询也是类似的。
若想转载请注明出处: http://lgbolgger.iteye.com/blog/2124860
作者:iteye的乒乓狂魔
发表评论
-
hibernate系列(四)一对一关联关系
2014-10-09 07:32 989以Person类和IDCard类为例,这里仅仅说一种一对一关联 ... -
hibernate系列(五)Session接口方法
2014-10-24 07:11 2055Session接口方法主要有save、persist、load ... -
hibernate系列(三)多对多的关联关系
2014-10-08 06:35 1038以Teacher和Student为例,他们之间是多对多的关系。 ... -
hibernate系列(一)hibernate入门
2014-10-06 10:31 1534最近开始学习hibernate,然后就想把这个学习历程总结下来 ...
相关推荐
Hibernate映射一对多关联关系
这里包含了hibernate多对一单向关联关系实现源码,希望对你有用。
hibernate外键实现一对一双向关联关系源码
Hibernate 一对多外键单向关联 Hibernate 一对多连接表单向关联 Hibernate 多对一外键单向关联 Hibernate 多对一连接表单向关联 Hibernate 多对多单向关联 Hibernate 一对一外键双向关联 Hibernate 一对一主键双向...
NULL 博文链接:https://dreamzhong.iteye.com/blog/1200915
hibernate双向一对多关联映射(注解版)
Hibernate一对一,一对多,多对多实例
Hibernate一对多双向自身关联demo代码
Hibernate双向一对一关联映射(注解版)
Hibernate 系列教程 单向一对多
包含《多对多双向关联映射》《多对一单向关联映射》《多对一双向关联映射》《一对多单向关联映射》等文档,并有图解及例子,非常适合新手学习,尤其是刚刚接触hibernate,对映射关系不清楚的。。。。
│ ├─ 一对多外键单向关联 │ ├─ 一对多连接表单向关联 │ ├─ 多对一外键单向关联 │ ├─ 多对一连接表单向关联 │ └─ 多对多单向关联 └─双向关联 ├─ 一对一外键双向关联 ├─ 一对一主键双向关联 ├─...
有关hibernate中保存多对一关联关系的进一步分析,很详细的介绍了多对一关系
NULL 博文链接:https://dreamzhong.iteye.com/blog/1200387
是用hibernate实现一对多的关联对初学javaee有一定的帮助希望大家努力学习呀
Hibernate关联关系(一对多)代码的具体实现,包括保存班级;保存学生;保存班级的时候同时保存学生;已经存在一个班级,新建一个学生,并且建立该学生和该班级之间的关系等一对多关系的情况。不足之处还望谅解
hibernate双向一对多关联映射(XML)
hibernate关联关系多对一实例
demo代码; 博客地址:http://blog.csdn.net/smilefyx/article/details/48951933
hibernate关联映射详解SSH 多对多,一对多关系对象映射