案例數據庫介紹
- 最近學習了ssm框架,所以準備把之前用原生Servlet做的一個小項目,改成ssm框架形式。沒想到帶來了這麼多問題,但是解決後還是學到了很多東西,這是根據我自己的理解記錄的一篇白話博文。
- 這個案例中,表與表之間的關係是很典型的稍微複雜一點點的多對多關係。一個選課系統。
- 我的數據庫設計是這樣的:學生選課時,選擇的其實選的是教師和課程綁定的關係。三個實體課程、學生、教師兩兩之間都是多對多關係。廢話不多說直接上圖,直接明瞭。
未改進代碼
在沒有學習mybatis框架前,我們如果要查詢表與表的關係時多對多的情況時,只能採取連接查詢,這樣就是的我們的sql語句特別複雜,看起來很難受,而且在寫這種sql語句時,也是一種挑戰,尤其是當多對多關係複雜時,我的這個小案例就是這樣。
mybatis讓我們只關心sql語句,簡化了開發。尤其是他可以使用嵌套查詢,讓我們不用再去使用複雜的連接查詢語句,這樣其實不僅簡化了sql語句,而且也降低了耦合,讓sql語句有有了很多的調用機會。
沒有改進前
學生類、教師類和課程類
public class Student {
private Integer sid;//學生編號
private String snum;//學號
private String sname;//學生姓名
private String spassword;// 密碼
private String syear;//年級
private String smajor;//專業
private String sdept;//院系
public class Course {
private Integer cid;//課程代碼
private String cname;//課程名
private String ctype;//課程性質
private Integer ccredit;//學分
private Integer ctime;//學時
public class Teacher {
private Integer tid;// 教師編號
private String tnum;//教師號
private String tname;//教師姓名
private String tpassword;//密碼
private String tdept;//院系
//get/set、toString方法省略
選課類(這是改進前分析的實體類截圖,改進後這個類就不存在了)
教師課程綁定關係類
public class TeacherAndCourse {
private Integer tcid;//授課代碼
private String tcplace;//上課地點
private String tctime;//上課時間
private String tcgrade;//學年
private List<Teacher> teacherList;//授課教師
private List<Course> courseList;//授課課程
//get/set、toString方法省略
這是查詢學生選課情況的各個Dao之間的調用關係:
這樣是可以查出數據的,但是數據是有問題的。
- 由於實體類的設計,使得查出來的數據很難在頁面上顯示,因爲由上圖我們知道最終返回給頁面的是一個List< Curricula > ,Curricula中有List< TeacherAndCourse > 和List< Studnet > ,在TeacherAndCourse 類中還有List< Course >和List< Teacher > 。頁面真的很難去顯示這些數據。
- 由於這種Dao層的設計,讓嵌套查詢了多次,使得查到的數據冗餘重複。
我這裏根據數據庫得到的是三條相同的數據,因爲已經改進,所以數據只有截圖,用不同顏色表示了三段數據,馬賽克是姓名。
兩個成功的案例
發現問題後,我回顧了一下課程里老師寫過的兩個案例。讓我受益匪淺,開始按照這種方式做。
訂單案例
權限、角色、用戶案例
改進後的代碼
開始改進代碼。
實體類改進
刪除選課類Curricula ,
給Student類添加 private List teacherAndCourseList;屬性,
給Teacher類添加private List courseList;屬性
給Course類添加private List teacherList;屬性
public class Student {
private Integer sid;//學生編號
private String snum;//學號
private String sname;//學生姓名
private String spassword;// 密碼
private String syear;//年級
private String smajor;//專業
private String sdept;//院系
private List<TeacherAndCourse> teacherAndCourseList;
public class TeacherAndCourse {
private Integer tcid;//授課代碼
private String tcplace;//上課地點
private String tctime;//上課時間
private String tcgrade;//學年
private List<Teacher> teacherList;//授課教師
private List<Course> courseList;//授課課程
public class Teacher {
private Integer tid;// 教師編號
private String tnum;//教師號
private String tname;//教師姓名
private String tpassword;//密碼
private String tdept;//院系
private List<Course> courseList;
public class Course {
private Integer cid;//課程代碼
private String cname;//課程名
private String ctype;//課程性質
private Integer ccredit;//學分
private Integer ctime;//學時
private List<Teacher> teacherList;
Dao層改進
StudentDao#findBySid(String “學生id”):
/**
* 通過學號查詢選課記錄
*
* @return
*/
@Select("SELECT * FROM t_stu WHERE sid=#{sid}")
@Results({
@Result(property = "oid",column = "oid"),
@Result(property = "snum",column = "snum"),
@Result(property = "sname",column = "sname"),
@Result(property = "spassword",column = "spassword"),
@Result(property = "syear",column = "syear"),
@Result(property = "smajor",column = "smajor"),
@Result(property = "sdept",column = "sdept"),
@Result(property = "teacherAndCourseList",column = "sid",javaType = java.util.List.class,
many = @Many(select = "com.bjwlxy.choosecourse.dao.TeacherAndCourseDao.findBySid",fetchType = FetchType.EAGER))
})
public Student findBySid(String sid);
TeacherAndCourseDao#findBySid(String “學生id”):
注意這裏的"select * from t_tc where tcid in (select tcid from t_option where sid=#{sid}
)",是通過sql去查tcid。
@Select("select * from t_tc where tcid in (select tcid from t_option where sid=#{sid})")
@Results({
@Result(id = true,property = "tcid",column = "tcid"),
@Result(property = "tcplace",column = "tcplace"),
@Result(property = "tctime",column = "tctime"),
@Result(property = "tcgrade",column = "tcgrade"),
@Result(property = "teacherList",column = "tid",javaType = java.util.List.class,
many = @Many(select = "com.bjwlxy.choosecourse.dao.TeacherDao.findById",fetchType = FetchType.EAGER)),
@Result(property = "courseList",column = "cid",javaType = java.util.List.class,
many = @Many(select = "com.bjwlxy.choosecourse.dao.CourseDao.findById",fetchType = FetchType.EAGER)),
})
public List<TeacherAndCourse> findBySid(Integer sid);
TeacherDao#findById(String “教師id”)
@Select("select * from t_teacher where tid=#{tid}")
public List<Teacher> findById(Integer tid);
CourseDao#findById(String “課程id”)
@Select("select * from t_course where cid=#{cid}")
public List<Course> findById(Integer cid);
這樣得到的數據是比較合理,也比較方便展示的。
分析一下數據:
- 現在由於domain層中的設計,得到的就是一個學生對象,而學生對象中包含了選課信息。這是比較合理的。
- Dao層的設計,使得方法重用性變高了,
Student{
sid=null, snum='201796084111', sname='xxxx', spassword='123456', syear='2017', smajor='軟件工程', sdept='計算機學院',
teacherAndCourseList=
[TeacherAndCourse
{tcid=1, tcplace='石鼓校區綜合樓605', tctime='週二4,5節,週四1,2節', tcgrade='2019-2020',
teacherList=[Teacher{tid=2, tnum='1022', tname='xxxx', tpassword='123456', tdept='計算機學院',
courseList=null}], courseList=[Course{cid=1, cname='計算機原理', ctype='專業核心課', ccredit=2, ctime=18, teacherList=null}]},
TeacherAndCourse
{tcid=2, tcplace='石鼓校區綜合樓605', tctime='週二4,5節,週五4,5節', tcgrade='2019-2020',
teacherList=[Teacher{tid=1, tnum='1021', tname='xxxx', tpassword='123456', tdept='計算機學院',
courseList=null}], courseList=[Course{cid=1, cname='計算機原理', ctype='專業核心課', ccredit=2, ctime=18, teacherList=null}]},
TeacherAndCourse
{tcid=3, tcplace='石鼓校區綜合樓605', tctime='週三5,6節,週四1,2節', tcgrade='2019-2020',
teacherList=[Teacher{tid=1, tnum='1021', tname='xxxx', tpassword='123456', tdept='計算機學院',
courseList=null}],courseList=[Course{cid=2, cname='java程序設計', ctype='專業核心課', ccredit=2, ctime=16, teacherList=null}]}]}