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>