浅谈MyBatis以及MyBatis-Plus
MyBatis
MyBatis的作用,简单来说,就是:
- 提供一种让应用访问数据库的方式(比如CRUD)
- 概念:Mapper
- 并且传入传出的都是Java对象。
- 概念:ORM
总的来说就是使应用通过调用Java方法访问数据库。
组成部分
先来个项目结构。
Entity
就是Data Object,i.e. 表里的字段 <==> Java对象里的字段。
@Data
public class UserDO {
private Integer id;
private String username;
private String password;
private Date createTime;
}
Mapper
在Mapper中自定义访问数据库的方法。
第一次学的时候不知道Mapper为什么叫Mapper。后来的理解是把Java方法Map到SQL语句。只要设置了这些Mappings,之后只要调用Java方法,就相当于调用了相应的SQL语句操纵数据库。
比如下面的insert会map到UserMapper.xml
的 insert元素 中的SQL语句。
@Mapper
public interface UserMapper {
int insert(UserDO user);
int updateById(UserDO user);
// 生产请使用标记删除,除非有点想不开,嘿嘿。
// 标记删除的意思是新开一个字段,标记该行是不是被删除了。
int deleteById(@Param("id") Integer id);
UserDO selectById(@Param("id") Integer id);
UserDO selectByUsername(@Param("username") String username);
List<UserDO> selectByIds(@Param("ids")Collection<Integer> ids);
}
<insert id="insert" parameterType="UserDO" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (
username, password, create_time
) VALUES (
#{username}, #{password}, #{createTime}
)
</insert>
这里#{username}代表参数。我们指定了parameterType=”UserDO”,所以username就会从传入的UserDO里面拿。
配置
application.yaml
mybatis:
config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径
mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址
type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径
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>
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="cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper">
<sql id="FIELDS">
id, username, password, create_time
</sql>
<insert id="insert" parameterType="UserDO" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (
username, password, create_time
) VALUES (
#{username}, #{password}, #{createTime}
)
</insert>
<update id="updateById" parameterType="UserDO">
UPDATE users
<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 users
WHERE id = #{id}
</delete>
<select id="selectById" parameterType="Integer" resultType="UserDO">
SELECT
<include refid="FIELDS" />
FROM users
WHERE id = #{id}
</select>
<select id="selectByUsername" parameterType="String" resultType="UserDO">
SELECT
<include refid="FIELDS" />
FROM users
WHERE username = #{username}
LIMIT 1
</select>
<select id="selectByIds" resultType="UserDO">
SELECT
<include refid="FIELDS" />
FROM users
WHERE id IN
<foreach item="id" collection="ids" separator="," open="(" close=")" index="">
#{id}
</foreach>
</select>
</mapper>
最后一个文件中其实是有一些操作之前没说的,不过嗯背API其实也没啥意思,大家自己看一下也能理解这些语句是什么意思。
MyBatis-Plus
对CRUD的优化
可以将你的Mapper继承MBP提供的BaseMapper<T>
,其中T就是你的数据对象DO。
好处就是BaseMapper里面提供了一些常用的CRUD操作,可以直接用而不用自己在xml里写SQL了。
【添加数据:(增)】
int insert(T entity); // 插入一条记录
注:
T 表示任意实体类型
entity 表示实体对象
【删除数据:(删)】
int deleteById(Serializable id); // 根据主键 ID 删除
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 map 定义字段的条件删除
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 根据实体类定义的 条件删除对象
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 进行批量删除
注:
id 表示 主键 ID
columnMap 表示表字段的 map 对象
wrapper 表示实体对象封装操作类,可以为 null。
idList 表示 主键 ID 集合(列表、数组),不能为 null 或 empty
【修改数据:(改)】
int updateById(@Param(Constants.ENTITY) T entity); // 根据 ID 修改实体对象。
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); // 根据 updateWrapper 条件修改实体对象
注:
update 中的 entity 为 set 条件,可以为 null。
updateWrapper 表示实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
【查询数据:(查)】
T selectById(Serializable id); // 根据 主键 ID 查询数据
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 进行批量查询
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据表字段条件查询
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据实体类封装对象 查询一条记录
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询记录的总条数
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 entity 集合)
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 map 集合)
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(但只保存第一个字段的值)
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 entity 集合),分页
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 map 集合),分页
注:
queryWrapper 表示实体对象封装操作类(可以为 null)
page 表示分页查询条件
使用的时候继承一下就好了。
更改后的Mapper方法长这样:
default UserDO selectByUsername(@Param("username") String username) {
return selectOne(new QueryWrapper<UserDO>().eq("username", username));
}
等价于select “username” = username这一语句。
上面的QueryWrapper可以将一系列query组合起来,以java对象的形式。用法一看就懂:
// Step1:创建一个 QueryWrapper 对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// Step2: 构造查询条件
queryWrapper
.select("id", "name", "age")
.eq("age", 20)
.like("name", "j");
// Step3:执行查询
userService
.list(queryWrapper)
.forEach(System.out::println);
这里的userService继承了IService
,可以看做是加强版的BaseMapper
. 具体可以看这篇
https://www.cnblogs.com/l-y-h/p/12859477.html
一些features
常用注解
【@TableName】
@TableName 用于定义表名
注:
常用属性:
value 用于定义表名
【@TableId】
@TableId 用于定义表的主键
注:
常用属性:
value 用于定义主键字段名
type 用于定义主键类型(主键策略 IdType)
主键策略:
IdType.AUTO 主键自增,系统分配,不需要手动输入
IdType.NONE 未设置主键
IdType.INPUT 需要自己输入 主键值。
IdType.ASSIGN_ID 系统分配 ID,用于数值型数据(Long,对应 mysql 中 BIGINT 类型)。
IdType.ASSIGN_UUID 系统分配 UUID,用于字符串型数据(String,对应 mysql 中 varchar(32) 类型)。
【@TableField】
@TableField 用于定义表的非主键字段。
注:
常用属性:
value 用于定义非主键字段名
exist 用于指明是否为数据表的字段, true 表示是,false 为不是。
fill 用于指定字段填充策略(FieldFill)。
字段填充策略:(一般用于填充 创建时间、修改时间等字段)
FieldFill.DEFAULT 默认不填充
FieldFill.INSERT 插入时填充
FieldFill.UPDATE 更新时填充
FieldFill.INSERT_UPDATE 插入、更新时填充。
【@TableLogic】
@TableLogic 用于定义表的字段进行逻辑删除(非物理删除)
注:
常用属性:
value 用于定义未删除时字段的值
delval 用于定义删除时字段的值
【@Version】
@Version 用于字段实现乐观锁
数据自动填充
比如现在每行插入的时候都要填充一个createTime字段,如果每次都手动写User.setCreateTime()有一些没必要。
这时可以在DO上注解一下:
@TableField(fill = FieldFill.INSERT)
private Date createTime;
然后创建一个handler:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
}
}
就可以自动填充了。
逻辑删除
新建一个字段delete_flags
,当删除某行是把该行的flag置为1即可,不需要真的删除。
好处是防止误删。
分页插件
分页的意思其实就是select的时候数据太多了分页显示。一开始我还以为是os里的paging。
实现乐观锁
新建一个字段,在上面用@Version
就可以了,每一次更新都会+1
代码生成器
没看懂,感觉很高级的样子。