几个简单实例

在这一节中,我们将通过几个实例来学习 MyBatis 的用法,提供的例子中不仅仅只有 MyBatis 部分的代码,还包含 Service、Controller、JSP 的代码。这一节会通过一整套的代码来演示 MyBatis 在项目开发中的基本用法。按照自下而上的顺序进行开发,从 Mapper 开始,依次开发 Service 层、Controller 层、JSP 前端页面。在实际开发过程中,可能会根据每一层的需求对各层接口进行调整。

基本准备

在开始之前需要先准备数据库、表和数据,9.2节配置的数据源连接的仍是本地的 mybatis 数据库,这一节会新增一个字典表,然后针对这个字典表进行增、删、改、查 4 类操作。字典表的建表语句和基础数据的 SQL 如下。

DROP TABLE IF EXISTS sys_dict;

CREATE TABLE sys_dict(
  id BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  code VARCHAR(64) NOT NULL COMMENT '类别',
  name VARCHAR(64) NOT NULL COMMENT '字典名',
  value VARCHAR(64) NOT NULL COMMENT '字典值',
  PRIMARY KEY (id)
)ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

INSERT INTO sys_dict VALUES (1,'性别','男','男');
INSERT INTO sys_dict VALUES (2,'性别','女','女');
INSERT INTO sys_dict VALUES (3,'季度','第一季度','1');
INSERT INTO sys_dict VALUES (4,'季度','第二季度','2');
INSERT INTO sys_dict VALUES (5,'季度','第三季度','3');
INSERT INTO sys_dict VALUES (6,'季度','第四季度','4');

在 src/main/java 中新建 tk.mybatis.web.model 包,然后新建 SysDict 实体类。

public class SysDict implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String code;
    private String name;
    private String value;
    //setter和getter方法
}

开发Mapper层(Dao层)

Mapper 层也就是常说的数据访问层(Dao 层)。使用 Mapper 接口和 XML 映射文件结合的方式进行开发,在9.3节集成 MyBatis 的配置中,自动扫描接口的包名为 tk.mybatis.**.mapper,因此在创建 Mapper 接口所在的包时也要参照这个命名规则。在 src/main/java 中新建 tk.mybatis.web.mapper 包,然后新建 DictMapper 接口。

public interface DictMapper {

}

同时,9.3 节的 SqlSessionFactoryBean 中也配置了扫描 XML 映射文件的目录 classpath:tk/mybatis/*/mapper/.xml。

在 src/main/resources 中新建 tk/mybatis/web/mapper/ 目录,然后新建 DictMapper.xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zccoder.mybatis2.ch9.spring.mapper.DictMapper">
</mapper>

经过前面几章的学习,此处直接将接口和对应的 XML 代码同时列出,不再对代码内容做详细的说明。

首先,当需要查看或修改某个具体的字典信息时,要有一个通过主键获取字典信息的方法,在 Mapper 接口和对应的 XML 中分别添加如下代码。

/**
 * 根据主键查询
 *
 * @param id ID
 * @return 字典
 */
SysDict selectByPrimaryKey(Long id);

<select id="selectByPrimaryKey" resultType="com.zccoder.mybatis2.ch9.spring.model.SysDict">
  select id, code, name, `value` from sys_dict where id = #{id}
</select>

因为9.3节配置 SqlSessionFactoryBean 时,将 typeAliasesPackage 配置为 com.isea533.mybatis.model,所以这里设置 resultType 时可以直接使用类名,省略包名。

以下代码依次是根据字典参数和分页参数查询字典信息的方法,新增、更新、删除字典的接口的方法。

/**
 * 条件查询
 *
 * @param sysDict 字典
 * @param rowBounds 分页
 * @return 字典列表
 */
List<SysDict> selectBySysDict(SysDict sysDict, RowBounds rowBounds);

/**
 * 新增
 *
 * @param sysDict 字典
 * @return 影响条数
 */
int insert(SysDict sysDict);

/**
 * 根据主键更新
 *
 * @param sysDict 字典
 * @return 影响条数
 */
int updateById(SysDict sysDict);

/**
 * 根据主键删除
 *
 * @param id ID
 * @return 影响条数
 */
int deleteById(Long id);

这 4 个接口方法对应的 XML 代码如下。

<select id="selectBySysDict" resultType="com.zccoder.mybatis2.ch9.spring.model.SysDict">
    select * from sys_dict
    <where>
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="code != null and code != ''">
            and code = #{code}
        </if>
    </where>
    order by code, `value`
</select>

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    insert into sys_dict(code, name, value)
    values (#{code}, #{name}, #{value})
</insert>

<update id="updateById">
    update sys_dict
    set code = #{code},
        name = #{name},
        value = #{value}
    where id = #{id}
</update>

<delete id="deleteById">
    delete from sys_dict where id = #{id}
</delete>

这 5 个方法都是很基础的方法,通过这 5 个方法就可以实现字典表的基本操作。下面将在这 5 个接口的基础上,继续编写 Service 层的代码。

开发业务层(Service层)

虽然针对接口编程对小的项目来说并不重要,但是这里为了形式上的需要仍然提供了 Service 接口。在 src/main/java 中新建 tk.mybatis.web.service 包,然后添加 DictService 接口,代码如下。

public interface DictService {
    /**
     * 通过ID查询字典
     *
     * @param id ID
     * @return 字典
     */
    SysDict findById(Long id);

    /**
     * 分页查询
     *
     * @param sysDict 字典
     * @param offset  开始位置
     * @param limit   长度
     * @return 字典列表
     */
    List<SysDict> findBySysDict(SysDict sysDict, Integer offset, Integer limit);

    /**
     * 保存或更新
     *
     * @param sysDict
     * @return 结果
     */
    boolean saveOrUpdate(SysDict sysDict);

    /**
     * 删除
     *
     * @param id ID
     * @return 结果
     */
    boolean deleteById(Long id);
}

Service 层的 saveOrUpdate 方法对应 Mapper 中的 insert 和 updateById 方法,其他 3 个方法和 Mapper 层方法一一对应。在 tk.mybatis.web.service 包下创建 impl 包,然后新建 DictService 接口的实现类 DictServiceImpl,代码如下。

@Service
public class DictServiceImpl implements DictService {

    @Autowired
    private DictMapper dictMapper;

    @Override
    public SysDict findById(Long id) {
        return dictMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<SysDict> findBySysDict(SysDict sysDict, Integer offset, Integer limit) {
        RowBounds rowBounds = RowBounds.DEFAULT;
        if (offset != null && limit != null) {
            rowBounds = new RowBounds(offset, limit);
        }
        return dictMapper.selectBySysDict(sysDict, rowBounds);
    }

    @Override
    public boolean saveOrUpdate(SysDict sysDict) {
        if (sysDict.getId() == null) {
            return dictMapper.insert(sysDict) == 1;
        } else {
            return dictMapper.updateById(sysDict) == 1;
        }
    }

    @Override
    public boolean deleteById(Long id) {
        if (id == null) {
            throw new NullPointerException("id");
        }
        return dictMapper.deleteById(id) == 1;
    }
}

Service 的实现类中需要添加 @Service 注解,在9.2节集成 Spring 时配置过自动扫描包,包名是 tk.mybatis.web.service.impl,DictServiceImpl 实现类所在的包就是符合这个包名规则的,加上注解后,Spring 在初始化时就会扫描到这个类,然后由 Spring 管理这个类。因为配置了自动扫描 Mapper 接口,所以在 Service 层可以直接通过以下代码注入 Mapper。

@Autowired
private DictMapper dictMapper;

通过自动扫描 Mapper 和自动注入可以更方便地使用 MyBatis。

开发控制层(Controller层)

在 tk.mybatis.web.controller 包下,新建 DictController 类,代码如下。

@Controller
@RequestMapping("/dicts")
public class DictController {

    @Autowired
    private DictService dictService;

    /**
     * 显示字典数据列表
     *
     * @param sysDict 字典
     * @param offset  开始位置
     * @param limit   长度
     * @return 字典列表
     */
    @RequestMapping
    public ModelAndView dicts(SysDict sysDict, Integer offset, Integer limit) {
        ModelAndView mv = new ModelAndView("dicts");
        List<SysDict> dicts = dictService.findBySysDict(sysDict, offset, limit);
        mv.addObject("dicts", dicts);
        return mv;
    }

    /**
     * 新增或修改字典信息页面,使用 get 跳转到页面
     *
     * @param id ID
     * @return 列表页
     */
    @RequestMapping(value = "add", method = RequestMethod.GET)
    public ModelAndView add(Long id) {
        ModelAndView mv = new ModelAndView("dict_add");
        SysDict sysDict;
        if (id == null) {
            //如果 id 不存在,就是新增数据,创建一个空对象即可
            sysDict = new SysDict();
        } else {
            //如果 id 存在,就是修改数据,把原有的数据查询出来
            sysDict = dictService.findById(id);
        }
        mv.addObject("model", sysDict);
        return mv;
    }

    /**
     * 新增或修改字典信息,通过表单 post 提交数据
     *
     * @param sysDict 字典
     * @return 编辑页
     */
    @RequestMapping(value = "add", method = RequestMethod.POST)
    public ModelAndView save(SysDict sysDict) {
        ModelAndView mv = new ModelAndView();
        try {
            dictService.saveOrUpdate(sysDict);
            mv.setViewName("redirect:/dicts");
        } catch (Exception e) {
            mv.setViewName("dict_add");
            mv.addObject("msg", e.getMessage());
            mv.addObject("model", sysDict);
        }
        return mv;
    }

    /**
     * 通过 id 删除字典信息
     *
     * @param id ID
     * @return 结果
     */
    @RequestMapping(value = "delete", method = RequestMethod.POST)
    @ResponseBody
    public ModelMap delete(@RequestParam Long id) {
        ModelMap modelMap = new ModelMap();
        try {
            boolean success = dictService.deleteById(id);
            modelMap.put("success", success);
        } catch (Exception e) {
            modelMap.put("success", false);
            modelMap.put("msg", e.getMessage());
        }
        return modelMap;
    }

}

上面这段代码中使用了两个视图,分别为 dicts 和 dict_add。在下一节中,我们将继续编写视图层代码。

开发视图层(View层)

按照9.2节中的视图配置,需要在 src/main/webapp 下面的 WEB-INF 中创建 jsp 目录,然后在 jsp 中创建 dicts.jsp 和 dict_add.jsp,dicts.jsp 代码如下。

<%@ page language="java" contentType="text/html; charset=UTF8" pageEncoding="UTF8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <c:set var="path" value="${pageContext.request.contextPath}"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF8">
    <title>字典信息</title>
    <script src="${path}/static/jquery-3.1.1.min.js"></script>
</head>
<body>
<table>
    <tr>
        <th colspan="4">字典管理</th>
    </tr>
    <tr>
        <th>类别名</th>
        <th>字典名</th>
        <th>字典值</th>
        <th> 操作  [<a href="${path}/dicts/add">新增</a>]</th>
    </tr>
    <c:forEach items="${dicts}" var="dict">
        <tr id="dict-${dict.id}">
            <td>${dict.code}</td>
            <td>${dict.name}</td>
            <td>${dict.value}</td>
            <td>
                [<a href="${path}/dicts/add?id=${dict.id}">编辑</a>]
                [<a href="javascript:;" onclick="deleteById(${dict.id}, '${dict.name}')">删除</a>]
            </td>
        </tr>
    </c:forEach>
</table>
<script>
    function deleteById(id, label){
        var r = confirm('您确定要删除“' + label + '”吗?');
        if(r){
            $.ajax({
                url: '${path}/dicts/delete',
                data: {
                    id: id
                },
                dataType: 'json',
                type: 'POST',
                success: function(data){
                    if(data.success){
                        $('#dict-' + id).remove();
                    } else {
                        alert(data.msg);
                    }
                }
            })
        }
    }
</script>
</body>
</html>

dict_add.jsp 代码如下。

<%@ page language="java" contentType="text/html; charset=UTF8" pageEncoding="UTF8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <c:set var="path" value="${pageContext.request.contextPath}"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF8">
    <title>字典维护</title>
</head>
<body>
<form action="${path}/dicts/add" method="post">
    <input type="hidden" name="id" value="${model.id}">
    <table>
        <c:if test="${msg != null}">
            <tr>
                <th colspan="2" style="color:red;max-width:400px;">${msg}</th>
            </tr>
        </c:if>
        <tr>
            <th colspan="2">字典维护</th>
        </tr>
        <tr>
            <th>类别名</th>
            <td><input type="text" name="code" value="${model.code}"></td>
        </tr>
        <tr>
            <th>字典名</th>
            <td><input type="text" name="name" value="${model.name}"></td>
        </tr>
        <tr>
            <th>字典值</th>
            <td><input type="text" name="value" value="${model.value}"></td>
        </tr>
        <tr>
            <th colspan="2">
                <input type="submit" value="保存">
                <input type="button" onclick="backToList()" value="取消">
            </th>
        </tr>
    </table>
</form>
<script>
    function backToList(){
        location.href = '${path}/dicts';
    }
</script>
</body>
</html>

dicts.jsp 代码中使用了 jquery-3.1.1.min.js,大家可以从地址 https://code.jquery.com/jquery-3.1.1.min.js 中下载 jQuery,然后放在 webapp/static/ 目录下面。

部署和运行应用

将项目部署到 Tomcat 下,然后启动服务。服务器启动完成后,在浏览器中输入 http://localhost:8080/mybatis-spring/dicts 并访问,简单的字典管理操作界面便实现了,如图9-7所示。

点击【新增】,打开如图9-8所示的表单。

image 2024 05 24 12 13 03 829
Figure 1. 图9-7 字典管理
image 2024 05 24 12 13 27 231
Figure 2. 图9-8 字典维护

依次输入 “是否”、“是”、“1”,然后点击【保存】,保存后会跳转到字典管理界面,此时字典中就多了刚刚新增的数据。

点击刚刚新增的数据后面的【编辑】,打开如图9-9所示的界面。

image 2024 05 24 12 14 07 341
Figure 3. 图9-9 编辑字典

将字典值改为 “是” 后点击【保存】,返回主界面后可以看到编辑后的效果。

点击新增数据后面的【删除】,会弹出提示框,询问是否删除该数据,点击【确定】后,该数据会被删除。

这些只是最简单的操作,也是业务中最普遍存在的功能。掌握基础功能开发后,结合前面几章学习的 MyBatis 的各种技巧后,我们就能实现各种各样的功能了。