您的位置:首页 > 其它

(经典)Hibernate多对多关系映射(五)

2014-06-02 21:57 302 查看
多对多关系是产生在三张表的关系中的,必须有一张中间表,必须保证中间表只有两个字段,必须是复合主键,必须是另两张表的外键。

一、用多对多关系来设计类

例如:学生选课

这里只创建学生和课程类,中间表不生成对应的pojo。

类中通过以下方式表示关系:

1) 学生会选择多门课程,因此在学生类中包含了多个课程类的对象,使用Set集合来保存。

2) 一门课程有多个学生选择,因此课程类中也要包含多个学生的对象,使用Set集合来保存。

[java]
view plaincopy





CREATE TABLE T_User (
userid varchar2(40) primary key ,
real_name varchar2(20) not null,
password varchar2(32) not null,
regist_date date default sysdate,
last_login_date date
);
INSERT INTO t_USER (userid,real_name,password)
VALUES ('zhangsan','张三','123');
commit;

CREATE TABLE course (
cid number(8) primary key ,
title varchar2(50) not null
);
INSERT INTO course VALUES (1,'Java编程');
INSERT INTO course VALUES (2,'JavaWeb');
INSERT INTO course VALUES (3,'Java面向对象编程');
INSERT INTO course VALUES (4,'Android ');
commit;

CREATE TABLE <span style="color:#cc0000;">user_course</span> (
userid varchar2(40) ,
course_id number(8) ,
primary key (userid,course_id),
<span style="color:#cc0000;">foreign key (userid) references t_user (userid) on delete cascade ,
foreign key (course_id) references course (id) on delete cascade </span>
);

三张表一起选择,来生成映射。



注意,选中多对多关系的多选框,同时,由于多张表的主键生成方式不同,因此建议在下一步单独为每张表选择。



每张表单独选择主键生成方式,course 采用increment,T_user采用的是asigned;

查看生成的vo实体类:

[java]
view plaincopy





public class TUser implements java.io.Serializable {
private String userid;
private String realName;
private String password;
private Date registDate;
private Date lastLoginDate;
private Set courses = new HashSet(0);

[java]
view plaincopy





public class Course implements java.io.Serializable {

private Integer id;
private String title;
private Set TUsers = new HashSet(0);//可以发现,互相包含了Set集合。

映射文件中也描述了这个关系。
TUser映射:

[java]
view plaincopy





<?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="org.liky.pojo.TUser" table="T_USER" schema="SUNXUN">
<id name="userid" type="java.lang.String">
<column name="USERID" length="40" />
<generator class="assigned"></generator>
</id>
<property name="realName" type="java.lang.String">
<column name="REAL_NAME" length="20" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="PASSWORD" length="32" not-null="true" />
</property>
<property name="registDate" type="java.util.Date">
<column name="REGIST_DATE" length="7" />
</property>
<property name="lastLoginDate" type="java.util.Date">
<column name="LAST_LOGIN_DATE" length="7" />
</property>
<!--
在TUser中包含一个名称为courses的Set集合,该数据是依据User_COURSE表关联查询出来的.
-->
<set name="courses" table="USER_COURSE" schema="SUNXUN">
<!--
中间表中通过USERID与当前表(TUSER)外键关联
-->
<key>
<column name="USERID" length="40" not-null="true" />
</key>
<!--
中间表通过COURSE_ID与Course外键关联,因此当前类与Course是多对多关系
-->
<many-to-many entity-name="org.liky.pojo.Course">
<column name="COURSE_ID" precision="8" scale="0" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>

Course映射:

[java]
view plaincopy





<?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="org.liky.pojo.Course" table="COURSE" schema="SUNXUN">
<id name="id" type="java.lang.Integer">
<column name="ID" precision="8" scale="0" />
<generator class="increment"></generator>
</id>
<property name="title" type="java.lang.String">
<column name="TITLE" length="50" not-null="true" />
</property>
<set name="TUsers" inverse="true" table="USER_COURSE" schema="SUNXUN">
<key>
<column name="COURSE_ID" precision="8" scale="0" not-null="true" />
</key>
<many-to-many entity-name="org.liky.pojo.TUser">
<column name="USERID" length="40" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>

Inverse的意思是 关联关系由对方进行维护。

多对多关系中,关键关系是中间表,需要根据业务逻辑判断,现在是学生选课功能,学生是主动方,课程被动方,因此可以说,学生在维护中间表的数据,也就是说学生维护两者的关联关系,对于课程来说,关联关系由对方(学生)进行维护。

二、实现选课功能:



选课前需要先登陆系统(学生的选课信息只存在于user_course表中,只能通过登录来区别个人),这里可以直接使用之前写好的登陆功能来完成登陆。

但登陆的DAO需要设置该学生对应的课程信息。

[java]
view plaincopy





public class TUserDAOImpl implements ITUserDAO {
public boolean isLogin(TUser user) throws Exception {
String hql = "FROM TUser AS u WHERE u.userid = ? AND u.password = ?";
Query query = HibernateSessionFactory.getSession().createQuery(hql);

query.setString(0, user.getUserid());
query.setString(1, user.getPassword());

List<User> allUser = query.list();

if (allUser != null && allUser.size() > 0) {
TUser result = (TUser) allUser.get(0);

// 将结果设置到user中,根据按引用传递,外面的对象也自动设置好了属性.
user.setRealName(result.getRealName());
user.setRegistDate(result.getRegistDate());
user.setLastLoginDate(result.getLastLoginDate());

// 取得该用户所选择的所有课程信息
user.setCourses(result.getCourses());
return true;
}
return false;
}
}

测试登陆功能时,也提示懒汉式异常,因为根据学生查找了课程信息。

解决方法也是修改映射文件。

[java]
view plaincopy





<pre code_snippet_id="329570" snippet_file_name="blog_20140506_7_1927285" name="code" class="java"><set name="courses" lazy="false" order-by="course_id" table="USER_COURSE" schema="SUNXUN"> </pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>

下面实现选课功能。

需要先完成课程的查询功能,取得全部课程,在页面上显示。

先加入连接

[java]
view plaincopy





<a href="user!selectCoursePre.action">选课</a>

完成Action中的操作

[java]
view plaincopy





<span style="white-space:pre"> </span>private List<Course> allCourse;
<pre code_snippet_id="329570" snippet_file_name="blog_20140506_9_6224637" name="code" class="java">public String selectCoursePre() {
allCourse = ServiceFactory.getITUserServiceInstance().selectCoursePre();

// 取得之前选择的课程信息,先取得当前登陆用户
TUser loginUser = (TUser) ServletActionContext.getRequest()
.getSession().getAttribute("user");
Set<Course> userCourse = loginUser.getCourses();
int index = 0;
courseIds = new int[userCourse.size()];
Iterator<Course> iter = userCourse.iterator();
while(iter.hasNext()) {
Course c = iter.next();
courseIds[index++] = c.getId();
}
return "select";
}</pre>
<pre></pre>
编写struts:
<p></p>
<pre code_snippet_id="329570" snippet_file_name="blog_20140506_10_2397390" name="code" class="java"><result name="select">/pages/user/user_select_course.jsp</result></pre>
<p></p>
<p>完成页面显示</p>
<p></p>
<pre code_snippet_id="329570" snippet_file_name="blog_20140506_11_2473837" name="code" class="java"><span style="white-space:pre"> </span><center>
用户登陆成功,当前用户为: ${user.realName } <br/>
<br/>
<br/>
<hr/>
<s:form action="user!selectCourse.action" method="post" theme="simple" namespace="/">
请选择课程:
<s:checkboxlist list="allCourse" name="courseIds" listKey="id" listValue="title"></s:checkboxlist>
<span style="white-space:pre"> </span><!--
如果使用的是普通表单,必须在页面上两层迭代循环,而且还要在标签中嵌套标签完成。
<span style="white-space:pre"> </span><c:forEach var="c" items="${allCourse}">
<input type="checkbox" name="courseIds" value="${c.id }"
<c:forEach var="cid" items="${courseIds}">
${cid==c.id?"checked":"" }
</c:forEach>
/> ${c.title }
</c:forEach>
<span style="white-space:pre"> </span>-->
<br/>
<s:submit value="提交"></s:submit>
</s:form>
</center></pre>
<p></p>
<p>提交后,需要在Action中接收选择的课程编号,并将这些课程加入到当前登陆的用户信息中。</p>
最后需要修改这个用户。
<p></p>
<pre code_snippet_id="329570" snippet_file_name="blog_20140506_12_6694742" name="code" class="java">// 接收用户所选择的课程编号
private int[] courseIds;
private String message;
private String url;

public String selectCourse() {
// 取得当前登陆用户
TUser loginUser = (TUser) ServletActionContext.getRequest()
.getSession().getAttribute("user");

// 将原有的取消
loginUser.getCourses().clear();

// 把选择的课程信息加入到这个User对象中
for (int i = 0; i < courseIds.length; i++) {
int id = courseIds[i];
Course c = new Course();
c.setId(id);
loginUser.getCourses().add(c);
}

// 修改
ServiceFactory.getITUserServiceInstance().selectCourse(loginUser);

message = "选课成功";
url = "pages/suc.jsp" ;

return "forward";
}</pre>最后在struts中编写跳转到成功的页面。
<p></p>
<pre></pre>
<pre></pre>
<pre></pre>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: