- 浏览: 300212 次
- 性别:
- 来自: 成都
文章分类
- 全部博客 (187)
- JAVA (66)
- JS (2)
- AJAX (9)
- Servlet (5)
- eclipse (4)
- html (2)
- PL/SQL (9)
- SOAP (1)
- other (14)
- JavaScript (8)
- Struts2 (6)
- Spring (10)
- Hibernate (5)
- JSP (1)
- Linux (3)
- WebService (2)
- 数据结构 (1)
- DB (5)
- English (1)
- maven (4)
- Code standard (2)
- SQL (1)
- 软件架构 (1)
- Tomcat (2)
- windows (1)
- HSQL (0)
- Open source framework (0)
- Web (6)
- Compass (0)
- Flex (1)
- OSGI (1)
- python (3)
- groovy (2)
- JPA (2)
- svn (1)
- jetty (1)
最新评论
-
zjfshowtime:
it doesn't work !why
Tomcat 和 Jetty 下 JNDI 配置 DBCP 连接池 -
coco5012:
Useful
sql server日期时间函数 datetime -
烟花弥散:
弱弱的问一句,您的第一个举例中else 后面可以跟判断条件吗? ...
Java高手论道:你还在用if else吗? -
coco5012:
Not very simple
使用assembly plugin实现自定义打包 -
mqlfly2008:
[color=red][size=medium][size=x ...
Java高手论道:你还在用if else吗?
1 Hibernate的单、双向关联
设计师L并不理解在Hibernate中单向关联与双向关联有什么区别。于是他也就无法告诉开发人员,在配置 实体间关系时究竟如何配置。从Hibernate的文档中来看,官方并没有特别告诉使用者哪种关联方式可靠。而且从L的实验结果来看,通常的一些行为使用 单向关联与双向关联的结果一样。那么究竟该如何看待这两种关联方式呢?
2 Hibernate的单向关联常规实现
依旧从实体房间(Room)与实体人(UserInfo)的一对多关联来看(这里将使用基于外键的一对多,对于连接表将放在另一个论题中),其两个实体如图6.2所示。
图6.2 两个实体的类图
实体房间(Room)与实体人(UserInfo)的代码实现见例6.8。
例6.8:实体房间(Room.java)与实体人(UserInfo.java)
Room.java
public class Room {
//房间实体的主键对应
private long id;
//房间号
private String roomnumber;
//房间名称
private String name;
//房间中的人(这是一个Set类型的集合)
private Set users;
//get/set方法
…
}
UserInfo.java
public class UserInfo {
//实体人的主键
private long id;
//人的名字
private String name;
//人的性别
private String sex;
//实体房间的主键
private long roomid;
//get/set方法
…
}
针对实体房间(Room)与实体人(UserInfo)的代码以及数据库表的单向一对多,其Hibernate映射文件见例6.9。
例6.9:实体房间(Room.java)与实体人(UserInfo.java)的映射文件
Room.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">
<!-- Room实体的package -->
<hibernate-mapping package="testhibernate">
<!-- Room实体的class和表 -->
<class name="Room" table="room">
<!-- 映射id主键 -->
<id name="id" column="id" type="java.lang.Long">
<!-- 映射为自增长类型 -->
<generator class="increment" />
</id>
<!-- 映射name属性 -->
<property name="name" column="NAME" type="java.lang.String" />
<!-- 映射roomnumber属性 -->
<property name="roomnumber" column="roomnumber" type="java.lang.String" />
<!-- 映射一对多 -->
<!-- 通过Room实体的users集合属性映射,级联动作为全部 -->
<set name="users" cascade="all">
<!-- 映射User表的外键roomid -->
<key column="roomid"></key>
<!-- 一对多映射class UserInfo -->
<one-to-many class="UserInfo"></one-to-many>
</set>
</class>
</hibernate-mapping>
UserInfo.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN""http://hibernate.sourceforge. net/hibernate-mapping-3.0.dtd">
<!-- UserInfo实体的package -->
<hibernate-mapping package="testhibernate">
<!-- UserInfo实体的class和表 -->
<class name="UserInfo" table="userinfo">
<!-- 映射id主键 -->
<id name="id" column="id" type="java.lang.Long">
<!-- 映射为自增长类型 -->
<generator class="increment" />
</id>
<!-- 映射name属性 -->
<property name="name" column="NAME" type="java.lang.String" not-null ="true" />
<!-- 映射roomnumber属性 -->
<property name="sex" column="SEX" type="java.lang.String"/>
<!-- <many-to-one name="room" column="roomid" class="Room" />-->
<!-- 映射roomid属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中 -->
<property name="roomid" column="roomid" type="java.lang.Long"/>
</class>
</hibernate-mapping>
3 单向关联的实现和问题
下面针对当前Hibernate映射文件给出了具体实现。为了代码完整,先给出Hibernate的配置文件,见例6.10。
例6.10:hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 数据库配置和映射文件配置 -->
<property name="connection.username">XXXX</property>
<property name="connection.url">jdbc:oracle:thin:@XXX:1521:XXX</property>
<property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
<property name="connection.password"> XXXX </property>
<propertyname="connection.driver_class">oracle.jdbc.driver. OracleDriver</property>
<!-- 显示SQL语句 -->
<property name="show_sql">true</property>
<!-- 显示格式化的SQL语句 -->
<property name="format_sql">true</property>
<!-- 显示哪个方法中执行的SQL语句 -->
<property name="use_sql_comments">true</property>
<!-- <mapping resource="Licencedata.hbm.xml" /> -->
<!-- 两个映射文件 -->
<mapping resource="UserInfo.hbm.xml" />
<mapping resource="Room.hbm.xml" />
</session-factory>
</hibernate-configuration>
接着给出的是Hibernate与数据库交互的测试代码,见例6.11。
例6.11:执行插入表
public void run() {
//创建Room实体
Room room = new Room();
//设置Room.name
room.setName("rwhome");
//设置Room.roomnumber
room.setRoomnumber("001");
//创建UserInfo实体
UserInfo userInfo = new UserInfo();
//设置UserInfo.name
userInfo.setName("rw");
//设置UserInfo.sex
userInfo.setSex("M");
//创建UserInfo集合userInfoSet
Set userInfoSet = new HashSet();
//添加UserInfo实体到集合userInfoSet
userInfoSet.add(userInfo);
//设置Room.users(这是一个集合类型)
room.setUsers(userInfoSet);
//创建Hibernate Session
Session session = HibernateSessionFactory.currentSession();
//启动事务
Transaction tx = session.beginTransaction();
//持久化Room实体
session.save(room);
session.flush();
//打印出持久化状态的各实体内容
System.out.println("RoomId:" + room.getId());
System.out.println("Name:" + room.getName());
System.out.println("Roomnumber:" + room.getRoomnumber());
Iterator it = room.getUsers().iterator();
while (it.hasNext()) {
UserInfo userInfoin = (UserInfo)it.next();
System.out.println("UserId:" + userInfoin.getId());
System.out.println("Name:" + userInfoin.getName());
System.out.println("Roomid:" + userInfoin.getRoomid());
System.out.println("Sex:" + userInfoin.getSex());
}
//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
}
由于在Hibernate映射文件中配置了级联(cascade="all"),因此只需要对Room实体进行持久化操作,会关联持久化UserInfo实体。
看起来现在一切都完好了,可惜这里却有一个小小的缺陷。这段代码在不同的前提下会生成两种结果。前提就是:room表与userinfo表是否存在外键关联。
(1)当room表与userinfo表未曾设置外键关联时,那么这段代码就是正确的,其执行后打印出来的SQL语句如下:
Hibernate:
//对room表自增长字段(主键)的最大值获取
select
max(id)
from
room
Hibernate:
//对userinfo表自增长字段(主键)的最大值获取
select
max(id)
from
userinfo
Hibernate:
/* 插入room表 */
insert
into
room
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/* 插入userinfo表,此时roomid字段为0 */
insert
into
userinfo
(NAME, SEX, roomid, id)
values
(?, ?, ?, ?)
Hibernate:
/* 通过一对多的关联映射更新userinfo表中当前记录的roomid字段
实现room表与userinfo表的关联 */
update
userinfo
set
roomid=?
where
id=?
关于这段SQL的执行过程见注释。可以看到,Hibernate在处理一对多单向关联时,是通过三句SQL来 完成的。首先是插入主表(room表),然后是插入子表(userinfo表),最后更新子表的关联字段(userinfo表的roomid字段)为主表 的主键(room表的id字段)。
(2)当room表与userinfo表设置外键关联时,那么这段代码就是错误的,其执行后打印出来的SQL语句如下:
Hibernate:
/*插入room表*/
insert
into
room
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/*插入userinfo表,此时roomid字段为0*/
insert
into
userinfo
(NAME, SEX, roomid, id)
values
(?, ?, ?, ?)
2007-03-27 13:47:28,822 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 2291, SQLState: 23000
2007-03-27 13:47:28,822 ERROR [org.hibernate.util.JDBCExceptionReporter] - ORA-02291: 违反完整约束条件 (TEST.FOREIGN) - 未找到父项关键字
和之前没有创建外键关联所不同的是,这里执行了第二句insert SQL后,由于插入roomid的时候插入的是0,而在主表room中没有这样的记录,因此会抛出“未找到父项关键字”的异常。为了解决这个问题,必须在 映射文件上做文章。修改UserInfo.hbm.xml文件,使得其在插入userinfo表时不对roomid进行插入,也即保证该字段是个“外源 性”的字段,见例6.12。
例6.12:修改后的UserInfo.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">
<!-- UserInfo实体的package -->
<hibernate-mapping package="testhibernate">
<!-- UserInfo实体的class和表 -->
<class name="UserInfo" table="userinfo">
<!-- 映射id主键 -->
<id name="id" column="id" type="java.lang.Long">
<!-- 映射为自增长类型 -->
<generator class="increment" />
</id>
<!-- 映射name属性 -->
<property name="name" column="NAME" type="java.lang.String" not-null="true" />
<!-- 映射roomnumber属性 -->
<property name="sex" column="SEX" type="java.lang.String"/>
<!-- <many-to-one name="room" column="roomid" class="Room" />-->
<!-- 映射roomid属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中 -->
<!-- 通过设置insert和update属性为false表示该字段是该外源性的字段,也即该字段的值来源于映射的其他字段 -->
<!-- insert和update属性默认状态为true,在当前操作中只需要设置insert="false"即可 -->
<property name="roomid" column="roomid" type="java.lang.Long" insert="false" update="false"/>
</class>
</hibernate-mapping>
关于修改的结果见注释,对于这样的改动,Hibernate执行的SQL语句如下:
Hibernate:
/*插入room表*/
insert
into
room
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/*插入userinfo表,此时由于设置了insert="false",所以roomid字段不被插入*/
insert
into
userinfo
(NAME, SEX, id)
values
(?, ?, ?)
Hibernate:
/*通过一对多的关联映射更新userinfo表中当前记录的roomid字段
实现room表与userinfo表的关联*/
update
userinfo
set
roomid=?
where
id=?
单向关联满足了一定的业务要求,但是当抓取UserInfo实体而又要同时抓取Room实体的业务时就无法被满足。此时就需要进行双向关联的设置。
4 Hibernate的双向关联常规实现
Hibernate的双向关联除了在主表(当前可以看作room表)的映射文件中设置一对多(one-to- many)外,还需要在从表(当前可以看作userinfo表)设置多对一(many-to-one)。首先需要在UserInfo.java实体类中增 加一个Room实体类型的属性,其代码实现见例6.13。
例6.13:增加Room实体类型属性的UserInfo.java
public class UserInfo {
//实体人的主键
private long id;
//人的名字
private String name;
//人的性别
private String sex;
//实体房间的主键(非必需的)
private long roomid;
//实体房间
private Room room;
public Room getRoom() {
return room;
}
public void setRoom(Room room) {
this.room = room;
}
//省略其他get/set方法
}
在保证Room实体的映射不变,即Room.hbm.xml不变的情况下,需要修改UserInfo.hbm.xml来实现多对一。其映射配置见例6.14。
例6.14:多对一配置的UserInfo.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">
<!-- UserInfo实体的package -->
<hibernate-mapping package="testhibernate">
<!-- UserInfo实体的class和表 -->
<class name="UserInfo" table="userinfo">
<!-- 映射id主键 -->
<id name="id" column="id" type="java.lang.Long">
<!-- 映射为自增长类型 -->
<generator class="increment" />
</id>
<!-- 映射name属性 -->
<property name="name" column="NAME" type="java.lang.String" not-null = "true" />
<!-- 映射roomnumber属性 -->
<property name="sex" column="SEX" type="java.lang.String"/>
<!-- 映射room属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中为roomid,其实体为Room -->
<many-to-one name="room" column="roomid" class="Room" />
</class>
</hibernate-mapping>
完成所有这些之后双向关联就配置完毕。
5 双向关联的实现和问题
接着该是双向关联的实现了,将例6.11的实现代码直接使用,为了呈现结果,可以通过UserInfo实体获取Room实体,再增加一个事务来打印结果,其代码实现见例6.15。
例6.15:双向关联的实现代码
public void run() {
//与例6.4相同
…
//创建一个新事务来获取Room实体
session = HibernateSessionFactory.currentSession();
tx = session.beginTransaction();
//根据room表的主键抓取Room持久化实体
Room room1 = (Room)session.get(Room.class, room.getId());
//打印各实体信息
System.out.println("Name:" + room1.getName());
System.out.println("Roomnumber:" + room1.getRoomnumber());
System.out.println("Id:" + room1.getId());
//抓取从表UserInfo实体的集合,并迭代打印结果
Iterator it = room1.getUsers().iterator();
while (it.hasNext()) {
UserInfo userInfoin = (UserInfo)it.next();
System.out.println("Id:" + userInfoin.getId());
System.out.println("Name:" + userInfoin.getName());
System.out.println("Roomid:" + userInfoin.getRoomid());
System.out.println("Sex:" + userInfoin.getSex());
//通过UserInfo实体的room属性获取Room实体的内容
System.out.println("RoomId:" + userInfoin.getRoom().getId());
System.out.println("Name:" + userInfoin.getRoom().getName());
System.out.println("Roomnumber:"+userInfoin.getRoom(). getRoomnumber());
}
tx.commit();
HibernateSessionFactory.closeSession();
}
下面执行例6.11的插入代码,来完成双向关联,其SQL语句如下:
Hibernate:
/* 插入room表 */
insert
into
room
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/*插入userinfo表,此时roomid为空*/
insert
into
userinfo
(NAME, SEX, roomid, id)
values
(?, ?, ?, ?)
Hibernate:
/*通过一对多的关联映射更新userinfo表中当前记录的roomid字段
实现room表与userinfo表的关联*/
update
userinfo
set
roomid=?
where
id=?
在执行例6.15的代码后打印结果如下:
Id:11117
Name:rw
Roomid:0
Sex:M
RoomId:25
Name:rwhome
Roomnumber:001
可以看到,通过Room实体获取UserInfo实体,再反向获取Room实体完成了。
只是这么实现的话,并没有达到最好的效果。因为SQL执行插入时总是要执行三句SQL,这样在效率上是有问题的。要达到效率上的提高就需要做另一个实现,那就是在配置文件中加入inverse属性。
6 inverse属性与双向关联
使用双向关联执行三句SQL的原因在于:插入room表后,需要插入根据一对多关联的userinfo表,但 是插入userinfo表的前提是session.save(room);,也即通过Room实体来维护二者之间的关系。这也就意味着Room实体需要通 过自身包含的UserInfo实体一一更新其外键,达到关联的目的。
而inverse属性就提供了另外一个更好的做法,它将关联关系反向交给UserInfo实体来完成,这也就 意味着虽然通过session.save(room);来执行插入,但是却是由UserInfo实体来维护二者之间的关系。所做的更改有两个地方,首先是 对Room. hbm.xml中一对多部分的修改,见例6.16。
例6.16:增加inverse属性的一对多
<!-- 通过Room实体的users集合属性映射,级联动作为全部 -->
<!-- 将inverse属性设置为true,表示维护动作由UserInfo实体来完成 -->
<set name="users" cascade="all" inverse="true">
<!-- 映射User表的外键roomid -->
<key column="roomid"></key>
<!-- 一对多映射class UserInfo -->
<one-to-many class="UserInfo"></one-to-many>
</set>
其次还需要在实现代码中,将UserInfo与Room实体的关系告诉UserInfo实体,也即让userinfo表的记录得到room表记录的主键。这段实现代码见例6.17。
例6.17:UserInfo实体参考Room实体
public void run() {
//创建Room实体
Room room = new Room();
//设置Room.name
room.setName("rwhome");
//设置Room.roomnumber
room.setRoomnumber("001");
//创建UserInfo实体
UserInfo userInfo = new UserInfo();
//设置UserInfo.name
userInfo.setName("rw");
//设置UserInfo.sex
userInfo.setSex("M");
//保证UserInfo实体得到与Room实体的关系,以帮助由UserInfo来维护外键关联
userInfo.setRoom(room);
//创建UserInfo集合userInfoSet
Set userInfoSet = new HashSet();
//添加UserInfo实体到集合userInfoSet
userInfoSet.add(userInfo);
//设置Room.users(这是一个集合类型)
room.setUsers(userInfoSet);
//创建Hibernate Session
Session session = HibernateSessionFactory.currentSession();
//启动事务
Transaction tx = session.beginTransaction();
//持久化Room实体
session.save(room);
//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
}
执行插入表操作,其显示出来的SQL语句如下:
Hibernate:
/* 插入room表 */
insert
into
room
(NAME, roomnumber, id)
values
(?, ?, ?)
Hibernate:
/*插入userinfo表,此时roomid通过UserInfo参考Room实体已经获取并插入了*/
insert
into
userinfo
(NAME, SEX, roomid, id)
values
(?, ?, ?, ?)
这样的SQL语句在批量插入userinfo表时效率高了许多,是双向关联中效率最高的一种插表方式。值得注 意的是,执行插表语句中的userInfo.setRoom(room);必须写在代码中,否则SQL语句同样是执行两句插入,但是在userinfo表 中将会插入一个为null的roomid。
7 结语
单向关联的功能比双向关联要弱,而且单向关联在操作数据库表时总是会执行三句SQL。因此在一般设计和实现中,通常应该优先选择使用双向关联。而使用双向关联时,inverse属性也是不能忽视的一个重点。通过多端来控制外键值的插入是值得推荐的。
相关推荐
多对多双向关联 <br>注意映射规则: <set name="roles" table="t_user_role"><br> <key column="userid"/><br> <many-to-many class="com.bjsxt.hibernate.Role" column="roleid"/> </set><br> table...
如果是双向关联,其中一段必须定义为Owner,另一端必须定义为inverse(在对关联表进行更新操作时这一端将被忽略) @Entity() public class Employer implements Serializable { private Integer id; private ...
如果在”一“一端维护一对多关联关系,hibernate会发出多余的udpate语句,所以我们一般在多的一端来维护关联关系。 加上inverse="true"这个属性,就可以强制在多的一端维护关系了。
7.2 映射一对多双向关联关系 156 7.2.1 [set]元素的inverse属性 161 7.2.2 级联删除 163 7.2.3 父子关系 164 7.3 映射一对多双向自身关联关系 165 7.4 改进持久化类 171 7.5 小结 175 7.6 思考题 176 第8章 ...
7.2 映射一对多双向关联关系 7.2.1 元素的inverse属性 7.2.2 级联删除 7.2.3 父子关系 7.3 映射一对多双向自身关联关系 7.4 改进持久化类 7.5 小结 7.6 思考题 第8章 通过Hibernate操纵对象(上) ...
这种策略支持双向的一对多关联,但不支持 IDENTIFY 生成器策略,因为ID必须在多个表间共享。一旦使用就不能使用AUTO和IDENTIFY生成器。 每个类层次结构一张表 @Entity @Inheritance(strategy=InheritanceType....
7.2 映射一对多双向关联关系 156 7.2.1 [set]元素的inverse属性 161 7.2.2 级联删除 163 7.2.3 父子关系 164 7.3 映射一对多双向自身关联关系 165 7.4 改进持久化类 171 7.5 小结 175 7.6 思考题 176 第8章 ...
7.2 映射一对多双向关联关系 7.2.1 元素的inverse属性 7.2.2 级联删除 7.2.3 父子关系 7.3 映射一对多双向自身关联关系 7.4 改进持久化类 7.5 小结 7.6 思考题 第8章 通过Hibernate操纵对象(上) ...
7.2 映射一对多双向关联关系 7.2.1 元素的inverse属性 7.2.2 级联删除 7.2.3 父子关系 7.3 映射一对多双向自身关联关系 7.4 改进持久化类 7.5 小结 7.6 思考题 第8章 通过Hibernate操纵对象(上) ...
7.2 映射一对多双向关联关系 7.2.1 元素的inverse属性 7.2.2 级联删除 7.2.3 父子关系 7.3 映射一对多双向自身关联关系 7.4 改进持久化类 7.5 小结 7.6 思考题 第8章 通过Hibernate操纵对象(上) ...
使用cascade和inverse优化区和街道关联关系 实现区和街道双向一对多关联关系
单向一对多关系......................................................39 ÿ 双向一对多关系......................................................44 多对多关联........................................
一般在做双向多对一(一对多)关联关系映射的时候,一般会设置让一的一方放弃对关联关系的维护,以减少不必要的更新语句 一对一: 基于外键的一对一 Wife Husband id id name name h_id references Husband...
12、写Hibernate的一对多和多对一双向关联的orm配置? 122 9、hibernate的inverse属性的作用? 122 13、在DAO中如何体现DAO设计模式? 123 14、spring+Hibernate中委托方案怎么配置? 123 15、spring+Hibernate中委托...
12、写Hibernate的一对多和多对一双向关联的orm配置? 9、hibernate的inverse属性的作用? 13、在DAO中如何体现DAO设计模式? 14、spring+Hibernate中委托方案怎么配置? 15、spring+Hibernate中委托方案怎么配置? ...
12、写Hibernate的一对多和多对一双向关联的orm配置? 122 9、hibernate的inverse属性的作用? 122 13、在DAO中如何体现DAO设计模式? 123 14、spring+Hibernate中委托方案怎么配置? 123 15、spring+Hibernate中委托...
12、写Hibernate的一对多和多对一双向关联的orm配置? 122 9、hibernate的inverse属性的作用? 122 13、在DAO中如何体现DAO设计模式? 123 14、spring+Hibernate中委托方案怎么配置? 123 15、spring+Hibernate中委托...
12、写Hibernate的一对多和多对一双向关联的orm配置? 122 9、hibernate的inverse属性的作用? 122 13、在DAO中如何体现DAO设计模式? 123 14、spring+Hibernate中委托方案怎么配置? 123 15、spring+Hibernate中委托...