概览
上一节介绍了Spring Boot是如何访问数据库的,首先介绍JDBC以及如何通过原生的JDBC API访问数据库,我们通过Demo看到了原生JDBC使用起来非常的麻烦,需要我们自己维护JDBC的核心对象、事务的管理等,进而我们引入了Spring Boot提供的封装JdbcTemplate工具类,虽然JdbcTemplate通过模板设计模式简化了共性的代码逻辑,不过使用起来后还是有很多不方便的地方。比如:
- SQL语句硬编码到代码修改SQL不方便
- 对位符和参数设置映射也是硬编码,不利于后续的维护
- 动态SQL语句拼装,既麻烦又很容易出错
- ...
这篇文章我们引入一个全新的ORM框架—MyBatis,MyBatis的前身是Apache基金会中的iBatis项目,由于各种原因,2010年脱离Apache,并且更名为MyBatis,从此发展迅速,并且在2013年,MyBatis将源代码迁移到了GitHub。
关于MyBatis的使用,有几种方式可以组合使用,分别是:
- MyBatis + XML
- MyBatis + 注解
- MyBatisPlus
- TkMyBatis
前两年开发项目的时候,我们团队使用的MyBatis +XML组合,开发过程中每个表的CURD操作,都需要手写代码和SQL,为了减少了开发工作量,我们引入MyBatis Generator代码生成器,虽然减少了开发工作量,不过也造成了项目中很多无用的代码。
在做上个项目的时候,我把tkMyBatis引进了配合PageHelper插件,大大提升开发效率,同时也没引入无用的代码片段。
目前公司的这个项目用上了MyBatisPlus,相比tkMyBatis,MyBatisPlus提供了更多的实用的功能,比如支持物理删除、可以配置多数据源等
下面,我们上面列的四种方式,逐个来入门。
MyBatis+XML
实例代码对应的仓库地址:github.com/dragon8844/…
引入依赖
在pom.xml的文件中引入相关依赖:
<!-- 实现对数据库连接池的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 本示例,我们使用 MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 实现对 MyBatis 的自动化配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- 方便用单元测试验证-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok简化代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
添加配置
-
应用配置
在resources目录下创建应用的配置文件application.yml,添加如下配置内容:
spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root # mybatis 配置内容 mybatis: config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径 mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址 type-aliases-package: com.dragon.mybatisxml.entity # 配置数据库实体包路径这里指定了mybatis配置文件,在类根路径下mybatis-config.xml文件,同时也指定了存放SQL文件的路径,类根路径下的mapper目录
-
mybatis的配置
在resources目录下创建mybatis-config.xml,并且添加如下配置:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 使用驼峰命名法转换字段。 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <typeAlias alias="Integer" type="java.lang.Integer"/> <typeAlias alias="Long" type="java.lang.Long"/> <typeAlias alias="HashMap" type="java.util.HashMap"/> <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/> <typeAlias alias="ArrayList" type="java.util.ArrayList"/> <typeAlias alias="LinkedList" type="java.util.LinkedList"/> </typeAliases> </configuration>在config包下创建MyBatisConfig配置类,添加如下配置内容:
/**
* @author lilong
*/
@MapperScan(basePackages = "com.dragon.mybatisxml.mapper")
@Configuration
public class MybatisConfig {
}
这个配置类用来指定mapper接口的包路径
编写代码
-
编写实体类
@Data public class User { /** * 主键 */ private Integer id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 创建时间 */ private Date createTime; }实体类对应的DDL语句:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `deleted` tinyint(1) DEFAULT NULL COMMENT '是否删除 0-未删除;1-删除', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; -
编写Mapper类
com.dragon.mybatisxml.mapper包下创建接口UserMapper
@Repository public interface UserMapper { /** * 新增 * * @param user * @return */ Integer insert(User user); /** * 根据ID查询 * * @param id * @return */ User selectById(Integer id); /** * 根据username查询 * * @param username * @return */ User selectByUsername(String username); /** * 根据ID更新 * * @param user * @return */ Integer updateById(User user); /** * 根据ID删除 * * @param id * @return */ Integer deleteById(Integer id); }在 resources/mapper 路径下,创建 UserMapper.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.dragon.mybatisxml.mapper.UserMapper"> <sql id="FIELDS"> id, username, password, create_time </sql> <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user ( username, password, create_time ) VALUES ( #{username}, #{password}, #{createTime} ) </insert> <select id="selectById" parameterType="Integer" resultType="User"> SELECT <include refid="FIELDS"/> FROM user WHERE id = #{id} </select> <select id="selectByUsername" parameterType="String" resultType="User"> SELECT <include refid="FIELDS"/> FROM user WHERE username = #{username} LIMIT 1 </select> <update id="updateById" parameterType="User"> UPDATE user <set> <if test="username != null"> , username = #{username} </if> <if test="password != null"> , password = #{password} </if> </set> WHERE id = #{id} </update> <delete id="deleteById" parameterType="Integer"> DELETE FROM user WHERE id = #{id} </delete> </mapper> -
对于绝大多数查询,我们是返回统一字段,所以可以使用
<sql />标签,定义 SQL 段。对于性能或者查询字段比较大的查询,按需要的字段查询。 -
为了阅读,对于数据库的关键字,使用大写。例如说,
SELECT、WHERE等等
单元测试
创建 UserMapperTest 测试类,我们来测试一下简单的 UserMapper 的每个操作。代码如下:
@SpringBootTest
@Slf4j
class UserMapperTest {
@Resource
private UserMapper userMapper;
@Test
void insert() {
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
user.setCreateTime(new Date());
Integer count = userMapper.insert(user);
log.info("count:{}", count);
}
@Test
void selectById() {
User user = userMapper.selectById(8);
log.info("user:{}", user.toString());
}
@Test
void selectByUsername() {
User user = userMapper.selectByUsername("张三");
log.info("user:{}", user.toString());
}
@Test
void updateById() {
User user = new User();
user.setUsername("李四");
user.setPassword("111111");
Integer count = userMapper.updateById(user);
log.info("count:{}", count);
}
@Test
void deleteById() {
Integer count = userMapper.deleteById(8);
log.info("count:{}", count);
}
}
到此,基于MyBatis + XML入门的小例子就已经搞完了,配置不算复杂,不过单表通用SQL写起来比较麻烦,强烈推荐大家使用MyBatis Generator,自动生成单表的SQL。
MyBatis + 注解
MyBatis同时可以支持使用注解编写SQL,不过在实际项目开发过程,并不推荐大家这样做,一方面SQL在Java代码中很难排版,另外在编写复杂的SQL的时候,会造成Mapper接口很乱。总之虽然可以不用再写SQL的xml文件了,但是强烈不推荐大家使用,另外我也询问过一些朋友,大家也很少使用这种组合方式,不过鉴于内容,我还是写了一个Demo
实例代码对应的仓库地址:github.com/dragon8844/…
差异部分
-
application.yml
application.yml配置文件中,可以移除 mapper-locations 配置项。
-
UserMapper.xml
可以删除resources/mapper 路径下SQL的配置文件,同时需要使用注解把SQL声明到UserMapper的接口上。
编写代码
在com.dragon.mybatisannotation包下创建接口UserMapper,同时我们使用注解的方式将SQL声明到相应的方法上去,代码如下:
@Repository
public interface UserMapper {
/**
* 新增
*
* @param user
* @return
*/
@Insert("INSERT INTO user(username, password, create_time) VALUES(#{username}, #{password}, #{createTime})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
Integer insert(User user);
/**
* 根据ID查询
*
* @param id
* @return
*/
@Select("SELECT username, password, create_time FROM user WHERE id = #{id}")
User selectById(Integer id);
/**
* 根据username查询
*
* @param username
* @return
*/
@Select("SELECT username, password, create_time FROM user WHERE username = #{username}")
User selectByUsername(String username);
/**
* 根据ID更新
*
* @param user
* @return
*/
@Update(value = {
"<script>",
"UPDATE user",
"<set>",
"<if test='username != null'>, username = #{username}</if>",
"<if test='password != null'>, password = #{password}</if>",
"</set>",
"</script>"
})
Integer updateById(User user);
/**
* 根据ID删除
*
* @param id
* @return
*/
@Insert("DELETE FROM user WHERE id = #{id}")
Integer deleteById(Integer id);
}
单表的CRUD,使用注解的方式还算可以,如何要是很复杂的SQL,那看起来就很乱。特别是在处理动态SQL的时候,需要引入script标签来处理,在编写和阅读的时候都十分的费劲。
单元测试
走一个单元测试,代码如下:
@Slf4j
@SpringBootTest
class UserMapperTest {
@Resource
private UserMapper userMapper;
@Test
void insert() {
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
user.setCreateTime(new Date());
Integer count = userMapper.insert(user);
log.info("count:{}", count);
}
@Test
void selectById() {
User user = userMapper.selectById(12);
log.info("user:{}", user.toString());
}
@Test
void selectByUsername() {
User user = userMapper.selectByUsername("张三");
log.info("user:{}", user.toString());
}
@Test
void updateById() {
User user = new User();
user.setUsername("李四");
user.setPassword("111111");
Integer count = userMapper.updateById(user);
log.info("count:{}", count);
}
@Test
void deleteById() {
Integer count = userMapper.deleteById(12);
log.info("count:{}", count);
}
}
小结
MyBatis的这4种使用方式全部介绍完的话内容比较多,所以本篇先介绍两种方式,即MyBatis+XML和MyBatis+注解,通过这两种方式我们已经对MyBatis的基本使用有了一个大体的了解。不过在使用过程中,对应单表的操作我们还是会用上代码生成器,代码生成器会产生很多冗余代码,下一篇我们将介绍MyBatisPlus和TkMyBatis,这两个框架在MyBatis的基础上做了一定封装和扩展,减少冗余的代码,同时能够极大的提升我们的开发效率,我们下篇见~
最后说一句
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。
此外,关注公众号:黑色的灯塔,专注Java后端技术分享,涵盖Spring,Spring Boot,SpringCloud,Docker,Kubernetes中间件等技术。