多對多關系是産生在三張表的關系中的,必須有一張中間表,必須保證中間表隻有兩個字段,必須是複合主鍵,必須是另兩張表的外鍵。
一、用多對多關系來設計類
例如:學生選課
這裡隻建立學生和課程類,中間表不生成對應的pojo。
類中通過以下方式表示關系:
1) 學生會選擇多門課程,是以在學生類中包含了多個課程類的對象,使用Set集合來儲存。
2) 一門課程有多個學生選擇,是以課程類中也要包含多個學生的對象,使用Set集合來儲存。
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 user_course (
userid varchar2(40) ,
course_id number(8) ,
primary key (userid,course_id),
foreign key (userid) references t_user (userid) on delete cascade ,
foreign key (course_id) references course (id) on delete cascade
);
三張表一起選擇,來生成映射。
注意,選中多對多關系的多選框,同時,由于多張表的主鍵生成方式不同,是以建議在下一步單獨為每張表選擇。
每張表單獨選擇主鍵生成方式,course 采用increment,T_user采用的是asigned;
檢視生成的vo實體類:
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);
public class Course implements java.io.Serializable {
private Integer id;
private String title;
private Set TUsers = new HashSet(0);//可以發現,互相包含了Set集合。
映射檔案中也描述了這個關系。
TUser映射:
<?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映射:
<?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需要設定該學生對應的課程資訊。
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;
}
}
測試登陸功能時,也提示懶漢式異常,因為根據學生查找了課程資訊。
解決方法也是修改映射檔案。
<set name="courses" lazy="false" order-by="course_id" table="USER_COURSE" schema="SUNXUN">
下面實作選課功能。
需要先完成課程的查詢功能,取得全部課程,在頁面上顯示。
先加入連接配接
<a href="user!selectCoursePre.action" target="_blank" rel="external nofollow" >選課</a>
完成Action中的操作
private List<Course> allCourse;
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";
}
編寫struts:
<result name="select">/pages/user/user_select_course.jsp</result>
完成頁面顯示
<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>
<!--
如果使用的是普通表單,必須在頁面上兩層疊代循環,而且還要在标簽中嵌套标簽完成。
<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>
-->
<br/>
<s:submit value="送出"></s:submit>
</s:form>
</center>
送出後,需要在Action中接收選擇的課程編号,并将這些課程加入到目前登陸的使用者資訊中。
最後需要修改這個使用者。
// 接收使用者所選擇的課程編号
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";
}
最後在struts中編寫跳轉到成功的頁面。