resultmap中association實現的是多對一的查詢(數據庫的列對類屬性的映射):eg:多個作者對應同一個文章
resultMap中的collection實現一對多的查詢 :eg:一個文章對應多個評論
數據庫中常用的數據類型:
int <=> int
varchar <=> String
datetime <=> Date
collection本身是集合
一。collection的相關知識:
<resultMap id="newsResultMapComplex" type="com.bean.News"> <result property="id" column="id"/> <result property="title" column="title"/> <result property="content" column="content"/> <result property="keywords" column="keywords"/> <result property="pubtime" column="pubtime"/> <result property="usersId" column="users_id"/> <result property="checkUsersId" column="check_users_id"/> <result property="categoryId" column="category_id"/> <result property="checkUsersName" column="checkUsersName"/> <result property="categoryName" column="categoryName"/> <association property="owner" javaType="com.bean.Users"> <result property="id" column="u_id"/> <result property="nickname" column="nickname"/> <result property="realname" column="realname"/> <result property="phone" column="phone"/> <result property="email" column="email"/> <result property="address" column="address"/> <result property="createTime" column="u_create_time"/> </association> <collection property="replys" ofType="com.bean.Replys"> <result property="id" column="r_id"/> <result property="newsId" column="r_news_id"/> <result property="content" column="r_content"/> <result property="usersId" column="r_users_id"/> <result property="replysId" column="r_replys_id"/> <result property="createTime" column="r_create_time"/> </collection> </resultMap>
對於u.id u_id這種,u_id表示的是重命名,並且在resultmap中的column也需要反復更改。(重命名的需要更改)property代表的是類里的屬性
以下代碼實現的目的:是以新聞為主題,查詢新聞的作者(類)里信息和分類信息(等值連接),然后與回復進行左連接,之后以新聞id主查詢它的回復的前兩名。
<select id="findNewsWithReplyss" resultMap="newsResultMapComplex" parameterType="int"> select n.id, n.title, n.content, n.users_id, n.category_id, n.pubtime, n.keywords, n.state, n.check_users_id, n.check_time checkTime, n.is_elite isElite, u.id u_id, u.nickname, u.realname, u.phone, u.email, u.address, u.create_time u_create_time, r.id r_id, r.content r_content, r.users_id r_users_id, r.news_id r_news_id, r.create_time r_create_time, c.name categoryName, u1.nickname checkUsersName from n_news n inner join n_users u on n.users_id = u.id inner join n_category c on n.category_id = c.id inner join n_users u1 on n.check_users_id = u1.id left join n_replys r on r.news_id = n.id where n.id = #{id} order by r.id desc limit 0,2 </select>
企業派做法:不要外鍵,雖真實存在。
當查詢很復雜時,resultMap會過於復雜。
於是當過於復雜時,我們不要外鍵,把復雜的數據庫語句拆分。縱向發展,拆分為簡單的數據庫語句,之后使用set方法來設置屬性。
對於過於復雜的查詢語句我們建議不用association與collection,使用簡單的數據庫語句進行查詢。
需完善NewsServlet:
package com.web; import com.bean.Categorys; import com.bean.News; import com.dao.CategorysMapper; import com.dao.NewsMapper; import com.util.MybatisUtils; import com.util.StringUtil; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @WebServlet(urlPatterns = "/news/list",name="NewsServlet") public class NewsServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String op = req.getParameter("op"); if("list".equals(op)){ list(req,resp); }else if("find".equals(op)){ find(req,resp); } /*NewsMapper newDao=MybatisUtils.getMapper(NewsMapper.class); List<News> list=newDao.findAllComplex(); //將查詢到的數據庫信息存儲得到“請求作用域” req.setAttribute("list",list);//前面一個 req.getRequestDispatcher("/news/list.jsp").forward(req,resp);*/ } private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { NewsMapper newDao=MybatisUtils.getMapper(NewsMapper.class); List<News> list=newDao.findAllComplex(); //將查詢到的數據庫信息存儲得到“請求作用域” req.setAttribute("list",list);//前面一個 req.getRequestDispatcher("/news/list.jsp").forward(req,resp); } private void find(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { NewsMapper newsDao = MybatisUtils.getMapper(NewsMapper.class); int newsId = StringUtil.getInt(req,"id"); News news = newsDao.findNewsWithReplys(newsId); // 將查詢到的數據庫信息存儲到“請求作用域” req.setAttribute("news", news); req.getRequestDispatcher("/news/detail.jsp").forward(req, resp); } }
完善新聞詳情界面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>新聞內容</title> </head> <body> <p>首頁/新聞/內容</p> <h1>${news.title}</h1> <p> 關鍵字:<span>${news.keywords}</span> 作者:<span>${news.owner.nickname}</span> 發布時間:<span>${news.pubtimeString}</span> </p> <div> ${news.content} </div> <h3>評論</h3> <hr/> <div> <ul> <c:forEach items="${news.replys}" var="reply"> <li>${reply.content} - ${reply.createTimeString}</li> </c:forEach> </ul> </div> </body> </html>
二。動態查詢:where條件可變的查詢,即where條件可變。
目標:動態查詢。
實現方法是:可以在mybatis的映射文件中使用if,choose(when,otherwise),where,set,trim,foearch標簽實現動態SQL.
if:判斷
choose:子標簽(when,otherwise),一般可以被if替換。
where:專用於select查詢
set:專用於update處理。
trim:有替換的功能。
foearch:一般用於in的情況下。
背景及需求:假定需要按照新聞分類(category_id)進行查詢,如果用戶沒有提供分類查詢則查詢所有的新聞,否則查詢指定分類的新聞。
1.xml文件添加的內容
為什么使用map類型,而不使用類類型,是由map類型沒有預設,傳輸需要的值更加方便。(此處的參數也有可能會被換為更加合適:查詢bean)
<!--java.util.Map,映射,鍵值對。為啥不用News類? pubtime:pubtime>='2020-03-20'、pubtime<='2020-03-25' bean:實體bean、業務bean、查詢bean --> <select id="find" resultMap="newsResultMapComplex" parameterType="map"> select n.id, concat(substring(n.title,1,8),if(char_length(n.title)>8,'...','')) title, concat(substring(n.content,1,8),if(char_length(n.content)>8,'...','')) content, n.users_id, n.category_id, n.pubtime, n.keywords, n.state, n.check_users_id, n.check_time checkTime, n.is_elite isElite, u.id u_id, u.nickname, u.realname, u.phone, u.email, u.address, u.create_time u_create_time, c.name categoryName, u1.nickname checkUsersName from n_news n inner join n_users u on n.users_id = u.id inner join n_category c on n.category_id = c.id inner join n_users u1 on n.check_users_id = u1.id <where> <if test="keywords != null"> and keywords like concat('%',#{keywords},'%') </if> <if test="category_id != null"> and category_id = #{category_id} </if> <if test="startTime != null"> and n.pubtime >= #{startTime} </if> <if test="endTime != null"> and n.pubtime <= #{endTime} </if> <if test="title != null and title.trim().length() > 0"> and n.title = #{title} </if> </where> order by n.id desc </select>
多條件查詢的
第一種方法如上,把if標簽放在where標簽內,mybatis特有的,並且要求把if標簽括在where中。
第二種方法:添加一個一定實現的條件,把sql語句進行拼裝。
from n_news n inner join n_users u on n.users_id = u.id inner join n_category c on n.category_id = c.id inner join n_users u1 on n.check_users_id = u1.id where 1=1 <if test="keywords != null"> and keywords like concat('%',#{keywords},'%') </if> <if test="category_id != null"> and category_id = #{category_id} </if> <if test="startTime != null"> and n.pubtime >= #{startTime} </if> <if test="endTime != null"> and n.pubtime <= #{endTime} </if> <if test="title != null and title.trim().length() > 0"> and n.title = #{title} </if>
2.添加方法:
List<News> find(Map<String, Object> params);
3.單元測試
@Test public void find(){ NewsMapper dao=MybatisUtils.getMapper(NewsMapper.class); Map<String ,Object> params=new HashMap<>(); List<News> result=dao.find(params); Assert.assertEquals(3,result.size()); params.put("keywords","java"); result=dao.find(params); Assert.assertEquals(2,result.size()); params.put("category_id","1"); result=dao.find(params); Assert.assertEquals(1,result.size()); // params.put("category_id",1); // result = dao.find(params); // Assert.assertEquals(2,result.size()); params.clear(); params.put("startTime","2020-06-03"); params.put("endTime","2020-06-30"); result = dao.find(params); Assert.assertEquals(2,result.size()); params.clear(); params.put("title","spring"); List<News> results = dao.find(params); Assert.assertEquals(1,results.size()); }
三。將動態查詢,並作為一個獨立功能開發出來。
首先一個小功能:在登陸時,如果已登錄顯示歡迎,沒登陸則提示登錄
<h1>${user !=null ? "歡迎".concat(user.nickname):"請"}登陸</h1>
輸出參數:問方法要結果。c#中有ref。 這里直接定義了一個map傳值進去,然后再方法里進行賦值,之后直接使用即可,如下outparams.
eg:
Map<String ,Object> params=new HashMap<>();//請求參數 StringUtil.getParameters(req,new String[]{"keywords","category_id"},params);//此時params是空的,是一個輸出參數,在方法中進行了獲取參數
public static void getParameters(HttpServletRequest req, String[] strings, Map<String, Object> outParams) { for(int i=0;i<strings.length;i++) { String name = strings[i]; // 當前請求參數名字 String value = req.getParameter(name); if (value != null) { value = value.trim(); if (value.length() > 0) { outParams.put(name, value); } } } }
目標:在新聞列表頁,增加新聞分類,時間的選擇,關鍵詞進行篩選新聞。
1,實現以關鍵字進入,並且把關鍵字展示,之后通過新聞分類進行篩選,並且點的新聞分類會變紅。
實現如下
<p><a href="/index.jsp">首頁</a>/新聞</p> <h1>新聞列表</h1> <div> <div>全部結果>${params.keywords}</div> <div> <table> <tr> <td>新聞分類:</td> <td> <span><a ${ params.category_id ==null?"style=\"color:red;\"":""} //看是否為空,為空即為紅色 href="/news/list?op=list&keywords=${params.keywords}">全部</a></span> //傳遞關鍵詞 <c:forEach items="${categorys}" var="category"> //循環 <span > <a ${category.id == params.category_id ?"style=\"color:red;\"":""} //驗證分類是否為紅色 href="/news/list?op=list&category_id=${category.id}&keywords=${params.keywords}"> //傳遞參數 ${category.name} </a> </span>  </c:forEach> </td> </tr> </table> </div> </div>
2.增加時間段篩選
首先增加對時間段的處理方法:
public static void getInterval(HttpServletRequest req, String interval, String startTime, String endTime, Map<String, Object> outParams) { // 如何把時間段解析為時間點?時間如何轉換? int intValue = getInt(req,interval); //獲取時間段 Calendar cl = Calendar.getInstance(); //獲取當前時間 String endTimeValue = format(cl.getTime()); // 沒有傳遞interval值或者查詢最近 if(intValue <= 0){ cl.add(Calendar.WEEK_OF_YEAR,-1); //上一周 }else{ // 若干月以前 cl.add(Calendar.MONTH,-intValue); } // 將起始截止時間點存儲到輸出集合中 String startTimeValue = format(cl.getTime()); outParams.put(interval,intValue); // 回顯參數 // 傳遞給dao層的 outParams.put(startTime,startTimeValue); outParams.put(endTime,endTimeValue); } static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String format(Date pubtime) { return pubtime != null ? df.format(pubtime) : ""; } public static int getInt(HttpServletRequest req, String id) { String value = req.getParameter(id); try { int intValue = Integer.parseInt(value); return intValue; }catch (NumberFormatException e){ } return -1; }
然后再servlet中調用此方法:
private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 兩次查詢打開了幾次數據庫? 一次 NewsMapper newsDao = MybatisUtils.getMapper(NewsMapper.class); CategorysMapper categorysDao = MybatisUtils.getMapper(CategorysMapper.class); // bean params作為查詢bean使用的,實體bean,業務bean // params作用:接收參數、回顯參數 Map<String,Object> params = new HashMap<>(); // 請求參數 // 獲取請求參數 StringUtil.getParameters(req,new String[]{"keywords","category_id"},params); StringUtil.getInterval(req,"interval","startTime","endTime",params); List<News> list = newsDao.find(params); List<Categorys> categorys = categorysDao.findAll(); // 將查詢到的數據庫信息存儲到“請求作用域” req.setAttribute("categorys",categorys); req.setAttribute("params",params); req.setAttribute("list", list); req.getRequestDispatcher("/news/list.jsp").forward(req, resp); }
在頁面中進行聯合檢索,關鍵字,分類,時間聯合查詢:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>新聞列表</title> <style type="text/css"> .usersInfo{ display:none; width:200px; background:#eee; border:1px solid #333; position:absolute;/*把定位改為絕對定位,即彈出usersinfo中的內容*/ } </style> <script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script> <script type="text/javascript"> $(function(){ // 保證頁面加載完成后在執行 // 選擇器.動作,hover表示是由兩個函數組合而成的。其中的this指當前單元格 $(".users").hover(function(){ $(".usersInfo",this).css("display","block"); },function(){ $(".usersInfo",this).css("display","none"); }); }); </script> </head> <body> <p><a href="/index.jsp">首頁</a>/新聞</p> <h1>新聞列表</h1> <div> <div>全部結果>${params.keywords}</div> <div> <table> <tr> <td>新聞分類:</td> <td> <span><a ${ params.category_id ==null?"style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=12">全部</a></span> <c:forEach items="${categorys}" var="category"> <span > <a ${category.id == params.category_id ?"style=\"color:red;\"":""} href="/news/list?op=list&category_id=${category.id}&keywords=${params.keywords}"> ${category.name} </a> </span>  </c:forEach> </td> </tr> <tr> <td>發布時間</td> <td> <span><a ${params.interval<=0 || params.intarval==null ?"style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=0&category_id=${params.category_id}">最近</a></span> <span><a ${params.interval==1 ? "style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=1&category_id=${params.category_id}">1月內</a></span> <span><a ${params.interval==3 ? "style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=3&category_id=${params.category_id}">3月內</a></span> <span><a ${params.interval==6 ? "style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=6&category_id=${params.category_id}">半年內</a></span> <span><a ${params.interval==12 ? "style=\"color:red;\"":""} href="/news/list?op=list&keywords=${params.keywords}&interval=12&category_id=${params.category_id}">1年內</a></span> </td> </tr> </table> </div> </div> <table width="800" border="1"> <tr> <th>編號</th> <th>標題</th> <th>內容</th> <th>關鍵字</th> <th>作者</th> <th>發布</th> <th>審批</th> <th>分類</th> </tr> <c:forEach items="${list}" var="news"> <tr> <td><a href="/news/list?op=find&id=${news.id}">${news.id}</a></td> <td>${news.title}</td> <td>${news.content}</td> <td>${news.keywords}</td> <td class="users">${news.owner.nickname} <div class="usersInfo"> <table> <tr> <td>賬號</td> <td>${news.owner.nickname}</td> </tr> <tr> <td>姓名</td> <td>${news.owner.realname}</td> </tr> <tr> <td>電話</td> <td>${news.owner.phone}</td> </tr> <tr> <td>郵件</td> <td>${news.owner.email}</td> </tr> <tr> <td>地址</td> <td>${news.owner.address}</td> </tr> </table> </div> </td> <td>${news.pubtimeString}</td> <td>${news.checkUsersName}</td> <td>${news.categoryName}</td> </tr> </c:forEach> </table> </body> </html>