一起来学习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<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();

测试结果:

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

<!--分页查询-->
<select id="queryMessageList" parameterType="java.util.Map" resultMap="MessageResult">
        select ID,COMMAND,DESCRIPTION,CONTENT from MESSAGE
        <where>
            <if test="command != null and !&quot;&quot;.equals(command.trim())">
                and COMMAND like '%' #{message.command} '%'
            </if>
            <if test="description != null and !&quot;&quot;.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 !&quot;&quot;.equals(command.trim())">
                and COMMAND like '%' #{command} '%'
            </if>
            <if test="description != null and !&quot;&quot;.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>
                    跳至&nbsp;<input id="currentPageText" type='text' value='${page.currentPage}' class='allInput w28' />&nbsp;页&nbsp;
                    <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
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容