(九)从零搭建后端框架——MyBatis使用和不足思考

1,541 阅读4分钟

前言

在项目的数据库开发中,不可避免地会使用到持久层框架。 当前主流的持久层框架有Spring Data、Hibernate、MyBatis等,这里使用MyBatis。

本文,集成MyBatis并实现了简单的增删改查。在使用过程中,指出MyBatis和项目的不足,并思考解决方案。

如果对MyBaits的使用很熟悉的,可以直接跳到总结。

具体实现

Maven依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.1</version>
    </dependency>

    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

参数配置

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://xxxxxxxx:3306/framework?useUnicode=true&characterEncoding=utf-8
    username: xxxxxxxx
    password: xxxxxxxx
mybatis:
  mapper-locations: classpath:sql-mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true
  • mybatis.mapper-locations 用于指定mapper文件的位置

  • mybatis.configuration.map-underscore-to-camel-case 用于开启驼峰功能

    数据库列名:create_time 实体类属性:createTime

数据库表

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
	`id` INT (11) NOT NULL AUTO_INCREMENT,
	`account` VARCHAR (40) NOT NULL COMMENT '用户名',
	`password` VARCHAR (255) NOT NULL COMMENT '密码',
	`nickname` VARCHAR (60) DEFAULT NULL COMMENT '昵称',
	`email` VARCHAR (40) DEFAULT NULL COMMENT '邮箱',
	`phone` VARCHAR (11) DEFAULT NULL COMMENT '电话',
	`create_time` datetime DEFAULT NULL COMMENT '创建时间',
	`create_user` VARCHAR (11) DEFAULT NULL COMMENT '创建人',
	`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
	`modify_user` VARCHAR (11) DEFAULT NULL COMMENT '修改人',
	PRIMARY KEY (`id`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4;

保存路径:classpath:resources/sqls/sys.sql

实体类

@Getter
@Setter
public class User extends Common {

    private Long id;

    @NotNull(message = "用户账号不能为空")
    @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
    private String account;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")
    private String password;

    @Size(max = 40, message = "用户昵称不能超过40个字符")
    private String nickname;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Phone(message = "手机号格式不正确")
    private String phone;
}

Mapper文件

<?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.zhuqc.framework.dao.UserDao">

    <select id="getUser" resultType="com.zhuqc.framework.entity.User">
        select * from sys_user where id = #{id, jdbcType = NUMERIC}
    </select>

    <insert id="addUser" parameterType="com.zhuqc.framework.entity.User">
        insert into sys_user
          (account,
           password,
           nickname,
           email,
           phone,
           create_user,
           create_time,
           modify_user,
           modify_time)
        values
          (#{account, jdbcType = VARCHAR},
           #{password, jdbcType = VARCHAR},
           #{nickname, jdbcType = VARCHAR},
           #{email, jdbcType = VARCHAR},
           #{phone, jdbcType = VARCHAR},
           #{createUser, jdbcType = VARCHAR},
           #{createTime, jdbcType = TIMESTAMP},
           #{modifyUser, jdbcType = VARCHAR},
           #{modifyTime, jdbcType = TIMESTAMP})
    </insert>

    <delete id="deleteUser">
        delete from sys_user
         where id = #{id, jdbcType = NUMERIC}
    </delete>

    <update id="updateUser" parameterType="com.zhuqc.framework.entity.User">
        update sys_user
           set nickname       = #{nickname, jdbcType = VARCHAR},
               email          = #{email, jdbcType = VARCHAR},
               phone          = #{phone, jdbcType = VARCHAR},
               modify_user    = #{modifyUser, jdbcType = VARCHAR},
               modify_time    = #{modifyTime, jdbcType = TIMESTAMP}
         where id = #{id, jdbcType = NUMERIC}
    </update>

</mapper>

持久层UserDao

@Mapper
public interface UserDao {

    User getUser(@Param("id") Long id);

    int addUser(User user);

    int deleteUser(@Param("id") Long id);

    int updateUser(User user);
}

Mapper文件与Dao类需要一一对应

  • Mapper文件中的namespace对应DAO接口的全路径
  • Mapper文件中statement的id对应DAO接口中的方法名
  • Mapper文件中statement的parameterType对应DAO接口中方法的输入参数类型
  • Mapper文件中statement的resultType对应DAO接口中方法的返回类型

服务层 UserService

public interface UserService {

    User getUser(Long id);

    int addUser(User user);

    int deleteUser(Long id);

    int updateUser(User user);
}
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public User getUser(Long id) {
        return userDao.getUser(id);
    }

    @Override
    public int addUser(User user) {
        return userDao.addUser(user);
    }

    @Override
    public int deleteUser(Long id) {
        return userDao.deleteUser(id);
    }

    @Override
    public int updateUser(User user) {
        return userDao.updateUser(user);
    }
}

控制层 UserController

@RestController
@RequestMapping("/user")
public class UserController extends BaseController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public ApiResult getUser(@PathVariable("id") Long id) {
        return ApiResult.success(userService.getUser(id));
    }

    @PostMapping("/add")
    public ApiResult addUser(@RequestBody @Valid User user) {
        setCreateInfo(user);
        return ApiResult.success(userService.addUser(user));
    }

    @DeleteMapping("/{id}")
    public ApiResult deleteUser(@PathVariable("id") Long id) {
        return ApiResult.success(userService.deleteUser(id));
    }

    @PutMapping("/{id}")
    public ApiResult updateUser(@RequestBody @Valid User user) {
        setModifyInfo(user);
        return ApiResult.success(userService.updateUser(user));
    }
}

编写完成后,访问Swagger地址对接口进行测试,如下:

接口测试

总结

至此,成功的集成了MyBatis并实现了简单的增删改查。

但是在使用过程中我们很容易发现一些问题:

  1. 每个实体都需要写增删改查SQL,感觉重复劳动
  2. 没有SQL监控,不能统计SQL的运行情况
  3. 没有分页插件,分页查询比较麻烦
  4. 没有单元测试,接口测试比较麻烦

以上问题的解决方案:

  1. 使用代码生成器、集成MyBatis-plus。
  2. 使用阿里数据源Druid
  3. 集成分页插件PageHelper
  4. 单元测试通过MockMvc类调用Controller接口

解决方案均会在后面的文章中实现。

以上,感谢阅读。如果感觉有帮助的话,不妨随手点个赞!

源码

github.com/zhuqianchan…

往期回顾