天天看點

(經典)Hibernate多對多關系映射(五)

多對多關系是産生在三張表的關系中的,必須有一張中間表,必須保證中間表隻有兩個字段,必須是複合主鍵,必須是另兩張表的外鍵。

一、用多對多關系來設計類

例如:學生選課

這裡隻建立學生和課程類,中間表不生成對應的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 
);
           

三張表一起選擇,來生成映射。

(經典)Hibernate多對多關系映射(五)

注意,選中多對多關系的多選框,同時,由于多張表的主鍵生成方式不同,是以建議在下一步單獨為每張表選擇。

(經典)Hibernate多對多關系映射(五)

每張表單獨選擇主鍵生成方式,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的意思是 關聯關系由對方進行維護。

多對多關系中,關鍵關系是中間表,需要根據業務邏輯判斷,現在是學生選課功能,學生是主動方,課程被動方,是以可以說,學生在維護中間表的資料,也就是說學生維護兩者的關聯關系,對于課程來說,關聯關系由對方(學生)進行維護。

二、實作選課功能:

(經典)Hibernate多對多關系映射(五)

選課前需要先登陸系統(學生的選課資訊隻存在于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中編寫跳轉到成功的頁面。

繼續閱讀