上次我们了解了Mybatis的一些基本操作,接下来我们继续学习Mybatis更多知识
1.接口式编程
什么是接口式编程?简单来说就是利用接口的形式去编码。为什么要使用接口式编码呢?我们来看以下的案例
messageList = sqlSession.selectList("Message.queryMessageList",message);
让我们分析这行代码,在实际开发中,我们很难避免没有写错"Message.queryMessageList"或者说,我们也有可能会命名相同的命名空间或者相同的id,而参数部分传入的是对象,我们也很难避免不会穿入其他错的值。如果我们犯错了,编译器是不会给我们找出错误,而需要我们在执行的时候才能发现错误。这样会效率低下,这时候就需要利用接口式编程帮我们规避这些错误
创建一个接口
返回的类型和xml中对应,名字和ID相同
package com.mybatis.dao;
import com.mybatis.entity.Message;
import java.util.List;
public interface IMessage {
public List<Message> queryMessageList (Message message);
}
修改命名空间,内容为包名.接口的名字
<mapper namespace="com.mybatis.dao.IMessage">
DAO层修改
//messageList = sqlSession.selectList("Message.queryMessageList",message);
IMessage iMessage = sqlSession.getMapper(IMessage.class);//获得代理实例,不需要做强转,后台已经实现
messageList = iMessage.queryMessageList(message);
sqlSession.close();
测试结果:
分析:
1.接口的实例不用我们操心,Mybatis会自己帮我们实现,我们直接调用就好了
2.看起来我们代码还比以前多了,但是以后跟spring集合的话,sqlSession会交给spring托管,这样我们会少了很多代码,增加了开发效率
IMssage.queryMessageList();
MapperProxy.invoke() == sqlSession.selectList()?
IMssage imssage = Proxy.newProxyInstance();
2.分页功能的介绍
说到分页我们先来捋一捋有几项我们值得注意的参数:
- 条件
- 显示多少条
- 一共有多少条
- 当前第几页
- 一共有多少页
创建一个page类
package com.mybatis.util;
/**
* 分页查询的工具类
*/
public class Page {
private int totalNumber;//总条数
private int currentPage;//当前第几页
private int totalPage;//总页数
private int pageNumber = 5;//每页显示多少条
private int start;//从第几条开始取
private int number;//取多少条
public void count(){
//计算总页数
int totalPageTemp = totalNumber / pageNumber;
//是否要增加一页
int plus = (totalNumber % pageNumber == 0) ? 0 : 1;
totalPageTemp = totalPageTemp + plus;
//如果是只有一页,就设为1
if(totalPageTemp <= 0) {
totalPageTemp = 1;
}
//设置总页数
this.totalPage = totalPageTemp;
//设置当前页数
// 总页数小于当前页数,应将当前页数设置为总页数
if(this.totalPage < this.currentPage) {
this.currentPage = this.totalPage;
}
// 当前页数小于1设置为1
if(this.currentPage < 1) {
this.currentPage = 1;
}
//设置limit的数值
this.start = (this.currentPage - 1) * pageNumber;
this.number = pageNumber;
}
public int getTotalNumber() {
return totalNumber;
}
public void setTotalNumber(int totalNumber) {
this.totalNumber = totalNumber;
count();
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
Message.xml
<!--分页查询-->
<select id="queryMessageList" parameterType="java.util.Map" resultMap="MessageResult">
select ID,COMMAND,DESCRIPTION,CONTENT from MESSAGE
<where>
<if test="command != null and !"".equals(command.trim())">
and COMMAND like '%' #{message.command} '%'
</if>
<if test="description != null and !"".equals(description.trim())">
and DESCRIPTION like '%' #{message.description} '%'
</if>
</where>
order by id limit #{page.start},#{page.number}
</select>
<!--增加-->
<select id="count" parameterType="com.mybatis.entity.Message" resultType="int">
select count(*) from MESSAGE
<where>
<if test="command != null and !"".equals(command.trim())">
and COMMAND like '%' #{command} '%'
</if>
<if test="description != null and !"".equals(description.trim())">
and DESCRIPTION like '%' #{description} '%'
</if>
</where>
</select>
我们可以看到上面queryMessageList需要两个对象的参数,我们可以用Map实现
IMessage
public interface IMessage {
public List<Message> queryMessageList (Map<String,Object> parameter);
public int count(Message message);
}
DAO层
public List<Message> queryMessageList (Map<String, Object> parameter) {
//获得数据库的sqlsession,通过sqlsess进行数据库的操作
SqlSession sqlSession = DBUtil.getSqlSession();
List<Message> messageList;
//调用Message.xml中的queryMessageList查询
//messageList = sqlSession.selectList("Message.queryMessageList",message);
IMessage iMessage = sqlSession.getMapper(IMessage.class);//获得代理实例
messageList = iMessage.queryMessageList(parameter);
sqlSession.close();
return messageList;
}
service层
public class MessageService {
public List<Message> getMessageList( Message message, Page page) {
MessageDao md = new MessageDao();
int count = md.count(message);//获得总条数
page.setTotalNumber(count);
Map<String ,Object> parameter = new HashMap<String, Object>();
parameter.put("message",message);
parameter.put("page",page);
List<Message> list = md.queryMessageList(parameter);
return list;
}
}
servlet层
public class ListServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里的“/”表示整个web应用程序
//编码问题,如果不是就会???
request.setCharacterEncoding("UTF-8");
String command = request.getParameter("command");
String description = request.getParameter("description");
String currentPage = request.getParameter("currentPage");
Page page = new Page();
Pattern pattern = Pattern.compile("[0-9]{1,9}");
if(currentPage == null || !pattern.matcher(currentPage).matches()) {
page.setCurrentPage(1);
}else {
page.setCurrentPage(Integer.valueOf(currentPage));
}
System.out.println("currentPage is " + currentPage);
MessageService ms = new MessageService();
Message message = new Message();
message.setCommand(command);
message.setDescription(description);
System.out.println(command + " ------" + description);
request.setAttribute("messageList",ms.getMessageList(message,page));
request.setAttribute("page",page);
request.getRequestDispatcher("/WEB-INF/jsp/back/list.jsp").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
部分jsp代码
<div class='page fix'>
共 <b>${page.totalNumber}</b> 条
<c:if test="${page.currentPage != 1}">
<a href="javascript:changeCurrentPage('1')" class='first'>首页</a>
<a href="javascript:changeCurrentPage('${page.currentPage-1}')" class='pre'>上一页</a>
</c:if>
当前第<span>${page.currentPage}/${page.totalPage}</span>页
<c:if test="${page.currentPage != page.totalPage}">
<a href="javascript:changeCurrentPage('${page.currentPage+1}')" class='next'>下一页</a>
<a href="javascript:changeCurrentPage('${page.totalPage}')" class='last'>末页</a>
</c:if>
跳至 <input id="currentPageText" type='text' value='${page.currentPage}' class='allInput w28' /> 页
<a href="javascript:changeCurrentPage($('#currentPageText').val())" class='go'>GO</a>
</div>
</div>
JS
/**
* 修改当前页码,调用后台重新查询
*/
function changeCurrentPage(currentPage) {
$("#currentPage").val(currentPage);
$("#mainForm").submit();
}
效果:
image.png image.png
image.png
3.拦截器实现分页
上面我们实现了分页的功能。但是如果我们以后开发越来越多的页面,每次都要这么繁琐的开发步骤,显然是不合适的。那么接下来我们可以利用Mybatis的拦截器功能实现分页。在实现拦截器之前,我们要关注以下几点:
- 要拦截什么对象
- 拦截对象的什么行为
- 什么时候拦截
代码:
package com.mybatis.interceptor;
import com.mybatis.util.Page;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
/**
* 分页拦截器
*/
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class})})
public class PageInterceptor implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
//获得metaObject
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY);
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
//配置SQL语句中的ID
String id = mappedStatement.getId();
if(id.matches(".+ByPage$")) {
BoundSql boundSql = statementHandler.getBoundSql();
//原始的sql语句
String sql = boundSql.getSql();
//查询的总条数
String countSql = "select count(*) from (" + sql + ")a";
Connection connection = (Connection) invocation.getArgs()[0];
PreparedStatement countStatement = connection.prepareStatement(countSql);
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(countStatement);
ResultSet rs = countStatement.executeQuery();
Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
if(rs.next()) {
page.setTotalNumber(rs.getInt(1));
}
// 改造后带分页查询的SQL语句
String pageSql = sql + " limit " + page.getStart() + "," + page.getNumber();
metaObject.setValue("delegate.boundSql.sql", pageSql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
//获得被拦截的对象,然后调用intercept方法,判断拦截的行为
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {
}
}
原理可以参考文章
参考文章:
https://blog.csdn.net/u012089657/article/details/49763631
我们来总结一下拦截器的使用逻辑:
用买票的案例来总结
1.成立一家代购公司----inplements Interceptor
2.注册---- plugin(在配置文件中注册)
3.申报资产---- property
下面开始工作了
4.使用资产--- setProperties()
5.定位客户群体--- Plugin.wrap() /@Intercepts
6.定位客户群体后开始代购(intercept:定义客户的行为,看是不是真的要买票)
- 过滤客户 ----id.matches(".+ByPage$")
- 获取购票信息---- Page
- 购票--原始SQL----》pageSql
- 送票--- return invocation.proceed();
4.批量新增
没插入前
image.png
XML
<!--批量增加-->
<insert id="insertBath" parameterType="java.util.List">
insert into message(command,description) values
<foreach collection="list" item="item" separator=",">
(#{item.command},#{item.description})
</foreach>
</insert>
DAO
public void insertBath(List<Message> list) {
SqlSession sqlSession = DBUtil.getSqlSession();
IMessage iMessage = sqlSession.getMapper(IMessage.class);
iMessage.insertBath(list);
sqlSession.commit();
sqlSession.close();
}
public interface IMessage {
List<Message> queryMessageList (Map<String,Object> parameter);
int count(Message message);
List<Message> queryMessageListByPage (Map<String,Object> parameter);
void insertBath(List<Message> list);
}
测试:
public static void main(String[] args) {
MessageDao messageDao = new MessageDao();
Message message = new Message();
message.setDescription("测试修改");
message.setCommand("xixi");
Message message1 = new Message();
message1.setCommand("lalalal");
message1.setDescription("lalalal");
List<Message> list = new ArrayList<Message>();
list.add(message);
list.add(message1);
messageDao.insertBath(list);
}
结果
image.png
网友评论