MyBatis-Plus 为简化开发而生
类似作用的还有JPA 、 tk-mapper、MyBatisPlus
官网:https://mp.baomidou.com
MyBatis简化JDBC,MyBatis Plus简化 MyBatis。
特性
框架结构
以下代码直接来自官网
1、新建数据库表结构及数据
-- 新建表结构
DROP TABLE IF EXISTS user;
CREATE TABLE user(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
-- 新建表数据
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
2、在SpringBoot项目的基础上,引入相关依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.38version>
dependency>
3、编写代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
//使用数据库id自增,此时数据库表的该字段一定要设置为自增
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
//主要是继承BaseMapper接口,并且将他的范型设置为我们的参数
@Repository
public interface UserMapper extends BaseMapper<User> {
}
4、主启动类添加扫描包
@MapperScan("pers.mobian.mybatisplus.mapper")
@SpringBootApplication
public class TestspringbootApplication {
public static void main(String[] args) {
SpringApplication.run(TestspringbootApplication.class, args);
}
}
5、编写自带的测试类
@SpringBootTest
class TestspringbootApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//直接调用baseMapper中的方法。此处我们的构造条件为空
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
6、测试结果
在我们的aplication.properties添加相关的日志配置即可。
跟绝这个配置我们应该明白,这是MyBatis Plus为我们提供的日志实现接口。直接将MyBatis的StdOutImpl日志配置使用
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
至此我们就能够看到我们sql执行的细节了
为方便后面的学习测试,请务必将执行的细节打印出来
@Test
public void testInsert() {
User user = new User();
user.setName("mobian");
user.setAge(13);
user.setEmail("[email protected]");
//依然调用的是我们BaseMapper接口中的方法
int result = userMapper.insert(user); // 帮我们自动生成id
System.out.println(result); // 受影响的行数
System.out.println(user); // 新增的对象会自动回填
}
由于数据库表user的id字段为主键,所以我们在不指定主键的情况下,就会使用默认的主键生成策略(雪花算法)
测试用例为三种不同的删除数据的方式
// 测试删除:根据id删除数据
@Test
public void testDelete() {
// 删除id为1的数据
int result = userMapper.deleteById(1);
// 受影响的行数
System.out.println(result);
}
// 测试删除:批量删除数据
@Test
public void testBatchDelete() {
// 删除id为2,3,4的数据,传入参数为一个集合
int i = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
// 受影响的行数
System.out.println(i);
}
//测试删除:构造条件map删除数据
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name","Billie");
map.put("age",12);
//删除name = Billie且age = 12的数据
int i = userMapper.deleteByMap(map);
System.out.println(i);
}
根据id更新
或者根据条件构造器更新数据
// 根据主键id,更新表中的数据
@Test
public void testUpdate() {
User user = new User();
user.setId(1L);
user.setName("nihao mobian");
user.setEmail("[email protected]");
//传入新对象,根据id进行修改
int result = userMapper.updateById(user);
System.out.println(result); // 受影响的行数
System.out.println(user); // 新增的对象会自动回填
}
//直接更新所有的数据
@Test
public void testUpdate3() {
User user = new User();
user.setAge(27);
user.setName("mobian");
user.setEmail("[email protected]");
int result = userMapper.update(user,null);
System.out.println(result); // 受影响的行数
System.out.println(user); // 新增的对象会自动回填
}
如果后面的wrapper构造器设置为null,类似于update的SQL语句的where过滤条件为空。
根据打印的SQL可以看出,是更新了所有的数据
//测试简单查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
//测试批量查询
@Test
public void testSelectBatch(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
//测试条件封装为map查询
@Test
public void testSelctByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","mobian");
map.put("age",111);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
MyBatis-Plus可以根据我们配置的策略,选择数据记录的主键字段以何种方式生成
雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯 一!
// 在数据库设置了主键自增的基础上,遵循数据库主键的生成策略,即主键自增
@TableId(type = IdType.AUTO)
private Long id;
主键生成策略的配置方法有:
@Getter
public enum IdType {
/**
* 数据库ID自增
* 该类型请确保数据库设置了 ID自增 否则无效
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类(雪花算法)
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use
*/
@Deprecated
UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
我测试MyBatis-Plus版本为3.4.2,在3.3.0版本中,部分枚举值已经被标记为过期。在使用雪花算法和UUID做主键策略时,注意新版枚举类型的变化。
我们希望添加和修改数据的时候能够自动更新相关的字段(创建时间和修改时间)
测试步骤分为三步:
1、第一步
实体类和数据库字段分别添加对应属性和字段。并且添加对应的TableField标识,创建时间在新增的时候修改,更新时间在插入和修改的时候修改。
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
2、第二步
添加对应的修改策略。
新建一个Handler包,新建一个Handler类,实现MetaObjectHandler接口,重写对应的更新和插入方法。
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//使用插入注解的会处理的方法
log.info("执行到了插入方法...");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//使用更新操作的会处理的方法
log.info("执行到了更新方法...");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
3、第三步
编写测试用例
// 测试插入数据时,创建时间和修改时间是否会自动添加
@Test
public void testInsert1() {
User user = new User();
user.setId(666L);
user.setName("999");
user.setEmail("[email protected]");
int result = userMapper.insert(user);
}
// 测试修改数据时,修改时间字段的数据是否会修改
@Test
public void test() {
User user = new User();
user.setId(666L);
user.setName("222");
int result = userMapper.updateById(user);
}
使用MyBatis-Plus实现乐观锁
1、第一步
实体类和数据库字段分别添加对应属性和字段。数据库的version字段可以给出一个默认值1,实体类属性添加@Vsersion注解
@Version
private Integer version;
2、第二步
新建一个config包,新建一个配置类。
新版本的MyBatis-Plus使用责任链的形式,替代了老版本的添加一个插件配置一个Bean的方法,这在后面处理分页是还会体现。
@Configuration
@EnableTransactionManagement
@MapperScan("pers.mobian.mybatisplus.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
3、第三步
编写测试用例
//测试乐观锁
@Test
public void testVersionLock() {
User user = userMapper.selectById(33L);
user.setName("mobian");
user.setEmail("[email protected]");
int i = userMapper.updateById(user);
System.out.println(i);
}
当我们看到where后面除了跟id条件之外,还有version字段进行过滤时,就代表测试成功。查看数据库version数据自动+1
使用MyBatis-Plus实现分页操作
1、第一步
编写对应的config,文件的目录结构与乐观锁的的目录结构相同。前文已经说到,分页与乐观锁的实现类似(新老版本配置方式有出入)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 注释了4.7中的乐观锁实现
//mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 配置分页操作
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
2、第二步
编写测试用例
//测试分页查询
@Test
public void testSelectLimit() {
// 参数一:当前页
// 参数二:页面大小
Page<User> userPage = new Page<>(1, 3);
userMapper.selectPage(userPage, null);
userPage.getRecords().forEach(System.out::println);
}
当打印的SQL中,含有limit参数时,代表我们的分页操作成功
使用MyBatis-Plus实现逻辑删除操作
第一步
实体类和数据库字段分别添加对应属性和字段。
@TableLogic
private Integer deleted;
第二步
修改properties配置文件
#删除值为1
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除值为0
mybatis-plus.global-config.db-config.logic-not-delete-value=0
第三步
编写测试用例
//测试逻辑删除
@Test
public void test1() {
User user = new User();
user.setId(33L);
userMapper.deleteById(user.getId());
}
根据其打印的SQL可以看出,此次的删除操作本质上是使用了修改操作,且修改方式根据我们配置的方式进行修改(由0变为1)
仅含有部分测试用例
1、测试like和isNotNull(等同于SQL中的like和is not null )
@Test
public void testSelect2() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("create_time")
.like("name", "mo");
userMapper.selectList(wrapper).forEach(System.out::println);
}
打印SQL
SELECT
id,
name,
age,
email,
create_time,
update_time,
version,
deleted
FROM user
WHERE (create_time IS NOT NULL
AND
name LIKE ?)
-- 参数值
Parameters:%mo%(String)
2、测试between和子查询SQL(等同于SQL中的between和 in( xxx ))
@Test
public void testSelect2() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.between("id",1,100)
.inSql("age","select age from user where age > 20");
userMapper.selectList(wrapper).forEach(System.out::println);
}
控制台打印的SQL
SELECT
id,
name,
age,email,
create_time,
update_time,
version,
deleted
FROM user
WHERE (id BETWEEN ? AND ?
AND
age IN (select age from user where age > 20))
-- 参数值
Parameters: 1(Integer), 100(Integer)
3、下列测试案例参照官网手册
更详情讲解,请参考官方文档
此功能需要引入两个maven依赖
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>2.2version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.4.1version>
dependency>
测试代码
public class CodeConfig {
public static void main(String[] args) {
// 构建一个代码自动生成器对象
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("默辨");
gc.setOpen(false);
gc.setFileOverride(false); // 是否覆盖
gc.setServiceName("%sService"); // 去Service的I前缀
gc.setIdType(IdType.AUTO); //设置生成的主键策略
gc.setDateType(DateType.ONLY_DATE);//设置数据库时间类型的映射策略
gc.setSwagger2(true); //设置是否开启swagger
mpg.setGlobalConfig(gc);
// 2、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 3、包结构的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("blog");
pc.setParent("com.mobian");
pc.setEntity("pojo");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("product"); // 设置要映射的表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true); // 自动lombok;
strategy.setLogicDeleteFieldName("deleted");
// 4.1、自动填充配置
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified",FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 4.2、乐观锁配置
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true); //驼峰转连字符
mpg.setStrategy(strategy);
// 执行
mpg.execute();
}
}
生成的代码结构如图。
可以将该测试代码直接复制到自己的项目中,用于生成相关的目录结构,提高开发效率