一起来学习Mybatis:第二篇

上次我们了解了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 queryMessageList (Message message);
}


修改命名空间,内容为包名.接口的名字

  

DAO层修改

//messageList = sqlSession.selectList("Message.queryMessageList",message);
        IMessage iMessage = sqlSession.getMapper(IMessage.class);//获得代理实例,不需要做强转,后台已经实现
        messageList = iMessage.queryMessageList(message);
        sqlSession.close();

测试结果:

image.png

分析:
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





我们可以看到上面queryMessageList需要两个对象的参数,我们可以用Map实现
IMessage

public interface IMessage {
    public List queryMessageList (Map parameter);
    public int count(Message message);
}

DAO层

  public List queryMessageList (Map parameter) {
        //获得数据库的sqlsession,通过sqlsess进行数据库的操作
        SqlSession sqlSession = DBUtil.getSqlSession();
        List 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 getMessageList( Message message, Page page) {
        MessageDao md = new MessageDao();
        int count = md.count(message);//获得总条数
        page.setTotalNumber(count);
        Map parameter = new HashMap();
        parameter.put("message",message);
        parameter.put("page",page);
        List 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代码

${page.totalNumber} 首页 上一页 当前第${page.currentPage}/${page.totalPage} 末页 跳至  页  GO

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 into message(command,description) values
        
            (#{item.command},#{item.description})
        

    

DAO

public void insertBath(List list) {
        SqlSession sqlSession = DBUtil.getSqlSession();
        IMessage iMessage = sqlSession.getMapper(IMessage.class);
        iMessage.insertBath(list);
        sqlSession.commit();
        sqlSession.close();
    }
public interface IMessage {
     List queryMessageList (Map parameter);
     int count(Message message);
     List queryMessageListByPage (Map parameter);
     void insertBath(List 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 list = new ArrayList();
        list.add(message);
        list.add(message1);
        messageDao.insertBath(list);
    }

结果


image.png

你可能感兴趣的:(一起来学习Mybatis:第二篇)