SpringBoot MyBatisPlus 整合
# MyBatisPlus整合
MyBatis Plus是MyBatis的增强工具,在MyBatis的基础上做了增强不改变的开发实现,从而提高效率。
官方文档:https://baomidou.com (opens new window)
特点:
- 无侵入。只做增强不做改变
- 消耗小。启动只注入CURD
- 增强CRUD操作。内置通用Mapper
- 内置代码生成器。采用Maven插件生成映射
- 分页插件多功能。支持多种数据库
- 内置全局拦截器。可分析增删改过程进行阻断
# 首次应用
大致流程
- 引入pon.xml依赖
- 更改库数据相关配置
- 启动器类 添加@MapperScan注解
应用
引入依赖 pom.xml
<!-- MyBatisPlus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.48</version> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.20</version> </dependency>
配置文件 application
# MyBatis # 连接池 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource # 驱动 spring.datasource.driver-class-name=com.mysql.jdbc.Driver # url/账号/密码 spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=root mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
这里连接库的用户名和密码可以省略不写,或者随意设定
创建 库数据 和 创建实体类 (省略
创建 UserMapper接口
@Mapper public interface UserMapper extends BaseMapper<User> {}
继承实现Mapper通用方法
SpringBoot 启动类 添加注解 @MapperScan
@SpringBootApplication @MapperScan("com.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class , args); } }
@MapperScan
注解 扫描 Mapper文件夹 的包路径测试
// 启动器Spring测试启动环境 @RunWith(SpringRunner.class) // 启动类 @SpringBootTest(classes = Application.class) public class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testFindByAll() { List<User> users = userMapper.selectList(null); users.forEach(System.out::println); // Assert.assertEquals(26 , users.size()); } }
# 注解配置
常用注解说明:
注解 | 值 | 说明 |
---|---|---|
@TableName | value:纠正表名 | 表名注解 |
@TableId | value:纠正主键字段名 type:主键策略类型 | 主键注解 |
@TableField | value:纠正字段名 exist:是否为库字段 fill:默认值填充 | 字段注解(非主键) |
更多注解:https://baomidou.com/pages/223848/
PS : @TableId/@TableField 纠正后 set方法也需要修改 , 因 使用set注入 , 否则失效
# 内置 CRUD
官网说明 : 更多详细 (opens new window)
CRUD测试
增加
增加测试 展开
@Test
public void testInsert() {
Assert.assertTrue(userMapper.insert(new User().setName("张三").setAge(3)) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, phone=null)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
User(id=7, name=张三, age=3, email=null, phone=null)
*/
删除
删除测试 展开
@Test
public void testDelById() {
Assert.assertTrue(userMapper.deleteById(3) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testDeletes1() {
Assert.assertTrue(userMapper.delete(new QueryWrapper<User>().like("name","J")) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=3, name=Tom, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testDeletes2() {
Assert.assertTrue(userMapper.delete(Wrappers.<User>query().like("name","J")) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=3, name=Tom, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testDeletes3() {
Assert.assertTrue(userMapper.delete(Wrappers.<User>query().lambda().like(User::getName,"J")) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=3, name=Tom, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testDeletes4() {
Assert.assertTrue(userMapper.delete(new QueryWrapper<User>().lambda().like(User::getName,"J")) > 0);
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=3, name=Tom, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
更改
更改测试 展开
@Test
public void testUpdateById() {
userMapper.updateById(new User().setId(3L).setName("先科"));
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, phone=null)
User(id=3, name=先科, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testUpdate1() {
userMapper.update(null, Wrappers.<User>update().set("name","kkkBbb").like("name","Tom"));
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, phone=null)
User(id=3, name=先科, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
@Test
public void testUpdate2() {
userMapper.update(new User().setName("kkkBbb"), Wrappers.<User>update().like("name","Tom"));
userMapper.selectList(null).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, phone=null)
User(id=3, name=kkkBbb, age=28, email=test3@baomidou.com, phone=null)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, phone=null)
User(id=5, name=kaikeba, age=331, email=test4@baomidou.com, phone=null)
User(id=6, name=Billie, age=24, email=test5@baomidou.com, phone=null)
*/
查询
查询测试 展开
@Test
public void testFindByBasic() {
System.out.println(userMapper.selectList(Wrappers.<User>query().eq("name","Jone")));
}
/*
[User(id=1, name=Jone, age=18, email=test1@baomidou.com, phone=null)]
*/
@Test
public void testFindProjection1() {
userMapper.selectList(new QueryWrapper<User>().select("id","name")).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=null, email=null, phone=null)
User(id=2, name=Jack, age=null, email=null, phone=null)
User(id=3, name=Tom, age=null, email=null, phone=null)
User(id=4, name=Sandy, age=null, email=null, phone=null)
User(id=5, name=kaikeba, age=null, email=null, phone=null)
User(id=6, name=Billie, age=null, email=null, phone=null)
*/
@Test
public void testFindProjection2() {
userMapper.selectList(new QueryWrapper<User>().select("id","name").like("name","J")).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=null, email=null, phone=null)
User(id=2, name=Jack, age=null, email=null, phone=null)
*/
@Test
public void testFindProjection3() {
userMapper.selectList(new QueryWrapper<User>().select("id","name").lambda().like(User::getName,"J")).forEach(System.out::println);
}
/*
User(id=1, name=Jone, age=null, email=null, phone=null)
User(id=2, name=Jack, age=null, email=null, phone=null)
*/
# 自定义 SQL
自定义SQL是写在 Mapper接口的方法上 , 值直接写SQL语句
注解 | 说明 |
---|---|
@Insert | 插入 |
@Update | 更新 |
@Delete | 删除 |
@Select | 查询 |
CRUD按照指定的规则编写SQL即可 , 如果需要传递参数 点击跳转
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("select * from users")
List<User> findAll();
}
# 条件构造器
条件构造器主要作用在 SQL语句中添加Where条件 , 从而自动生成约束的SQL . 主要比较贴合 Hibernate 的使用方式
条件构造器两大分类 : (它们的父类 AbstractWrapper抽象类)
- QueryWrapper
- UpdateWrapper
以上有各自独有的属性详细自行官网查询 条件构造器更多详细 (opens new window)
应用 :
// 最终Where约束语句 (默认全参存在)
// select xxx from xxx where
// (ex.del_flag=0 AND ex.examine_user_id=su.id AND su.office_id=so.id) and
// (ex.type=?) and (su.name like '%?%') and (so.id=?);
@Override
public List<ExamineDo> selectByCondition(Map<String, Object> map) {
QueryWrapper<ExamineDo> query = new QueryWrapper<>();
// 三表连接的 关键字段的 SQL约束代码
query.apply("ex.del_flag=0 AND ex.examine_user_id=su.id AND su.office_id=so.id");
/**
* SQL约束拼接参数说明:
* 参数1.判断该对象是否可行(指该属性数据是有效且不为空
* 参数2.指定比较的字段 字符串(一般指定的字符串
* 参数3.指定比较的数据 外面获取到的
*/
query.eq(map.containsKey("type") && !ObjectUtils.isEmpty(map.get("type")),"ex.type",map.get("type"))
.like(map.containsKey("name") && !ObjectUtils.isEmpty(map.get("name")),"su.name",map.get("name"))
.eq(map.containsKey("officeId") && !ObjectUtils.isEmpty(map.get("officeId")),"so.id",map.get("officeId"));
// Mapper传递对象即可
return baseMapper.selectByCondition(query);
}
# 传参操作
手写SQL多多少少都会需要参数的传递 , 因此有以下三种方式进行传递参数 :
- 实体类 根据实体的 属性 进行匹配信息
- Map 根据Map哈希中的 K键 匹配 V值
- @Param 根据 参数值 映射匹配
- 条件构造器 只需添加一个对象即可自动生成 约束SQL 官方说明 : 条件构造器使用方式 (opens new window)
示例:
// 实体类
// 实体类包含的属性JSON展示 Users{id,name}
@Select("SELECT su.* " +
"FROM " +
"sys_user su " +
"WHERE " +
"su.id= #{id} " +
"AND su.name= #{name} " )
List<SysUser> findByid(Users user);
// Map
// map = {id:23,name:"张三"}
@Select("SELECT su.* " +
"FROM " +
"sys_user su " +
"WHERE " +
"su.id= #{id} " +
"AND su.name= #{name} " )
List<SysUser> findByid(Map<String,Object> map);
// @Param
@Select("SELECT su.* " +
"FROM " +
"sys_user su " +
"WHERE " +
"su.id= #{id} " )
List<SysUser> findByid(@Param("id")Integer id);
// 条件构造器
@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
# 多表查询
多个表查询难免会有些稍微复杂的SQL . 多表查询分为 一对多/一对一/多对多 三种类型
注解 | 以往标签 | 说明 |
---|---|---|
@Result | <id> /<Result> | 结果集封装 . 包含有以下属性 : - column : 数据表字段名称 - property : 类中对应的属性名 - one : 与@One搭配 , 进行一对一的映射 - many : 与@Many搭配 , 进行一对多的映射 |
@Results | <resultMap> | 和@Result 使用 , 封装 一个/多个 结果集 |
@One | <assocation> | 一对一结果集封装 , 使用格式如下 : @Result(column="xx",property="xx",one=@One(select="xx",...)) |
@Many | <collection> | 一对多结果集封装 , 使用格式如下 : @Result(column="xx",property="xx",many=@Many(select="xx",...)) |
注意 :
- 一旦使用了 @Results注解 封装的当前对象全部属性都要重新手写
- 使用 @One/@Many 注解 , 一般情况使用 select属性值 指定全限定名类的方法(如: ById/...)
应用
主要编写核心部分 , 其他代码简单表示即可
User 用户实体
类型 | 名称 | 表对应的字段 | 注解规则 |
---|---|---|---|
int | id | id | |
String | username | username | |
String | password | password | |
List<User> | orderList | oid(提取二查) | @TableField(exist = false) |
Order 订单实体
类型 | 名称 | 表对应的字段 | 注解规则 |
---|---|---|---|
String | id | id | |
Date | ordertime | ordertime | |
double | total | total | |
User | user | uid(提取二查) | @TableField(exist = false) |
一对多 一人有多个订单 UserMapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 查所用用户并且包含订单
@Select("select * from user")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "id", property = "orderList",javaType = List.class,
many = @Many(select = "com.sans.demoapplication.mapper.OrderMapper.findByUId"))
})
List<User> findAll();
}
@Many注解中属性seelct的值 : 是指定路径类的方法
多对多 每个订单必须有一个用户 OrderMapper接口
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
// 查所有
@Select("SELECT id,ordertime,total,uid FROM `order`")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(column = "uid", property = "user" ,javaType = User.class ,
one = @One(select = "com.sans.demoapplication.mapper.UserMapper.selectById"))
})
List<Order> findAllOrder();
// 按uid 查订单
@Select("select id,ordertime,total from `order` where uid=#{uid}")
List<Order> findByUId(int uid);
}
@One注解中属性seelct的值 : 是指定路径类的方法 (该类继承有 BaseMapper接口 里面有内置的CRUD)
# 分页插件
以下分页功能的实现 , 基于以上的基础进行实现
分页应用方式有3种形式:
三种分页区别
内置分页 | XML分页 | PageHelper分页 | |
---|---|---|---|
jar包引入 | 无 | 无 | 有 |
配置 | 拦截器配置 | 全局配置 + 手写SQL | 引入Bean |
分页信息 | 简略 | 自定义 | 详细 |
复杂度 | 低 | 高 | 一般 |
# 内置分页
创建 mp配置类
MybatisPlusConfig.java
,全限定名类 : com.config.MybatisPlusConfigpackage com.config; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 开启 count 的 join 优化,只针对 left join !!! paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } }
测试
@Autowired private UserMapper mapper; //···· @Test public void testSelectPage() { Page<User> page = new Page<>(2, 3); Page<User> result = mapper.selectPage(page, Wrappers.<User>query()); result.getRecords().forEach(System.out :: println); System.out.println("总数:" + result.getTotal()); System.out.println("总页数:" + result.getPages()); System.out.println("每行数:" + result.getSize()); /** * 验证分页信息 * 1. 总数 > 3 * 2. 页总数 = 3 */ assertThat(result.getTotal()).isGreaterThan(3); assertThat(result.getRecords().size()).isEqualTo(3); } /* User(id=4, name=Jack, age=20, email=test2@baomidou.com) User(id=5, name=Jack, age=20, email=test2@baomidou.com) User(id=6, name=Jack, age=20, email=test2@baomidou.com) 总数:17 总页数:6 每行数:3 */
# xml分页
添加属性 ,全局配置文件
application.yml
mybatis-plus : type-aliases-package : com.pojo.User mapper-locations: classpath:/mapper/*.xml
映射类添加方法
UserMapper.java
@Mapper public interface UserMapper extends BaseMapper<User> { /** * 用户列表分页查询 (xml) * Param()注解:替换了原旧名称进行应用参数属性 如 p.属性、c.属性 * @param page 分页对象 * @param conditioin 约束查询数据 * @return */ public Page<User> selectUserByPage(@Param ("p") IPage<User> page, @Param("c") User conditioin); }
创建映射文件
UserMapper.xml
,路径 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.mapper.UserMapper"> <sql id="selectSql"> SELECT `user`.id,`user`.name,`user`.age,`user`.email FROM user </sql> <select id="selectUserByPage" resultType="user"> <include refid="selectSql"></include> <where> <if test="c.age !=null"> age = #{c.age} </if> <if test="c.email !=null"> and email like '%${c.email}%' </if> </where> </select> </mapper>
注意: namespace 属性指定的值是 实现的接口
测试
//xml分页 @Test public void testSelectUserByPage() { Page<User> page = new Page<>(1,2); User u = new User(); u.setAge(20); u.setEmail("test2"); Page<User> pr = mapper.selectUserByPage(page , u); System.out.println("总数:" + pr.getTotal()); System.out.println("总页数:" + pr.getPages()); System.out.println("每行数:" + pr.getSize()); System.out.println("==========="); pr.getRecords().forEach(System.out::println); } /* 总数:13 总页数:7 每行数:2 =========== User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Jack, age=20, email=test2@baomidou.com) */
注意: 约束条件查询结果条数 必须要 大于 分页每页数
# pagehelper分页
引入独有依赖
pom.xml
<!--pagehelper分页--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency>
编辑 mp配置类
MybatisPlusConfig.java
,全限定名:com.config.MybatisPlusConfigpackage com.config; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; import com.github.pagehelper.PageInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan("com.mapper") public class MybatisPlusConfig { //··· /** * pagehelper的分页插件 */ @Bean public PageInterceptor pageInterceptor() { return new PageInterceptor(); } }
映射类添加方法
UserMapper.java
(可选测试)package com.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.sans.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @Mapper public interface UserMapper extends BaseMapper<User> { //··· /** * 用户列表分页查询 (pagehelper) * @param conditioin 约束查询数据 * @return */ public List<User> selectUserByPage2(User conditioin); }
编辑映射文件
UserMapper.xml
,路径 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.sans.mapper.UserMapper"> <sql id="selectSql"> SELECT `user`.id,`user`.name,`user`.age,`user`.email FROM user </sql> <!-- ····· --> <!-- pagehelper 映射分页查询--> <select id="selectUserByPage2" resultType="user"> <include refid="selectSql"></include> <where> <if test="age !=null"> age = #{age} </if> <if test="email !=null"> and email like '%${email}%' </if> </where> </select> </mapper>
测试
// pagehelper分页 @Test public void testSelectUserByPage2() { User u = new User(); u.setAge(20); u.setEmail("test2"); PageInfo<User> page = PageHelper.startPage(1,2).doSelectPageInfo(() ->{ // 方法选择测试 (他们测试结果一致) // 1. 自定义xml mapper.selectUserByPage2(u); // 2. mp 内置方法 // mapper.selectList(Wrappers.<User>query().eq("age",20).like("email","test2")); }); page.getList().forEach(System.out :: println); System.out.println("==========="); System.out.println("总行数=" + page.getTotal()); System.out.println("总页数=" + page.getPages()); System.out.println("每页行数=" + page.getPageSize()); System.out.println("当前页=" + page.getPageNum()); System.out.println("起始行数=" + page.getStartRow()); System.out.println("是第一页=" + page.isIsFirstPage()); System.out.println("是最后页=" + page.isIsLastPage()); System.out.println("还有下一页=" + page.isHasNextPage()); System.out.println("还有上一页=" + page.isHasPreviousPage()); System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums())); } /* User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Jack, age=20, email=test2@baomidou.com) =========== 总行数=13 总页数=7 每页行数=2 当前页=1 起始行数=1 是第一页=true 是最后页=false 还有下一页=true 还有上一页=false 页码列表[1, 2, 3, 4, 5, 6, 7] */
官方文档:MyBatis-Plus (baomidou.com) (opens new window)
# 反向工程
官方在 3.5.1 以上和以下 分别 生成方式都不一样,因此做了两个示例
工程配置文档:添加跳转 (opens new window)
# 3.5.1之前
步骤:
引入必要jar
freemarker
模板、lombok
简化实体、mybatis-plus-generator
生成核心<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency>
生成后可以直接删除
freemarker
模板、mybatis-plus-generator
生成核心反向工程启动类
必要提示说明:
- 数据源配置
- 作者
- 生成路径
- 模组名(启动输入
- 表名(启动输入
代码展开
import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.TableFill; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class MysqlGenerator { /** * <p> * 读取控制台内容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); /* 指定工程名称 指定作者 * */ gc.setOutputDir(projectPath + "/NewsSys/src/main/java"); gc.setAuthor("sasn"); gc.setOpen(false); gc.setIdType(IdType.AUTO);//设置全局id自增 gc.setBaseResultMap(true);//设置生成BaseResultMap // gc.setSwagger2(true); 实体属性 Swagger2 注解 mpg.setGlobalConfig(gc); // 数据源配置 /* 写自己的连接源 */ DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); // 库数据 和 实体数据字段手动匹配配置 dsc.setTypeConvert(new MySqlTypeConvert() { @Override public IColumnType processTypeConvert(@org.jetbrains.annotations.NotNull GlobalConfig globalConfig, @org.jetbrains.annotations.NotNull String fieldType) { // 比较库字段类型 if ("datetime".equals(fieldType)) { // 满足则返回需要对应的实体类型 return DbColumnType.DATE; } if ("float".equals(fieldType)) { return DbColumnType.BASE_DOUBLE; } switch (fieldType) { case "text" : case "mediumtext" : return DbColumnType.STRING; default: break; } return super.processTypeConvert(globalConfig, fieldType); } } mpg.setDataSource(dsc); // 包配置 /* 指定模块名称 */ PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名")); pc.setParent("com.sans"); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker 从依赖包的 templates下查找模板 String templatePath = "/templates/mapper.java.ftl"; // 如果模板引擎是 velocity // String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // 判断自定义文件夹是否需要创建 checkDir("调用默认方法创建的目录,自定义目录用"); if (fileType == FileType.MAPPER) { // 已经生成 mapper 文件判断存在,不想重新生成返回 false return !new File(filePath).exists(); } // 允许生成模板文件 return true; } }); */ cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 // TemplateConfig templateConfig = new TemplateConfig(); // 配置自定义输出模板 //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); //templateConfig.setXml(null); // mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!"); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // 公共父类 // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!"); // 写于父类中的公共字段 // strategy.setSuperEntityColumns("id"); strategy.setEnableSqlFilter(false);//开启支持输入正则表达式 strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); //设置生成自动填充注解的表字段 ArrayList<TableFill> list = new ArrayList<>(); list.add(new TableFill("update_date", FieldFill.INSERT_UPDATE)); list.add(new TableFill("create_date", FieldFill.INSERT)); list.add(new TableFill("del_flag", FieldFill.INSERT)); strategy.setTableFillList(list); strategy.setLogicDeleteFieldName("del_flag");//设置生成逻辑删除注解对应表字段名 mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
# 3.5.1之后
步骤:
引入必要jar
freemarker
模板、annotations
配置注解应用、mybatis-plus-generator
生成核心<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <dependency> <groupId>org.jetbrains</groupId> <artifactId>annotations</artifactId> <version>16.0.1</version> </dependency>
生成后可以直接删除
freemarker
模板、mybatis-plus-generator
生成核心反向工程启动类
注意参数的填充
代码展开
import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert; import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery; import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; import com.baomidou.mybatisplus.generator.config.rules.IColumnType; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; public class BuilderApplication { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC"; String username = "root"; String passwoed = "root"; String baomidou = "柏竹"; String packagePath = "com.sans"; String generatePath = "D:\\Code\\Java\\Drill\\Guangdong embedded\\01 Study\\JavaEE\\220607SpringCloud\\220607WebService\\220607WebService\\Restful-Server\\src\\main\\java"; DataSourceConfig.Builder dataSourceBuilder = new DataSourceConfig.Builder(url, username, passwoed) .dbQuery(new MySqlQuery()) .typeConvert(new MySqlTypeConvert() { @Override public IColumnType processTypeConvert(@org.jetbrains.annotations.NotNull GlobalConfig globalConfig, @org.jetbrains.annotations.NotNull String fieldType) { // 比较库字段类型 if ("datetime".equals(fieldType)) { // 满足则返回需要对应的实体类型 return DbColumnType.DATE; } return super.processTypeConvert(globalConfig, fieldType); } }); /** * 配置文档:https://baomidou.com/pages/981406/ */ FastAutoGenerator.create(dataSourceBuilder) // 全局配置 .globalConfig(builder -> { builder.author(baomidou) // .enableSwagger() .disableOpenDir() .fileOverride() .outputDir(generatePath); // 指定输出目录 }) // 包配置 .packageConfig(builder -> { builder.parent(packagePath); // 设置父包名 // .moduleName(moduleName); // 设置父包模块名 // .pathInfo(Collections.singletonMap(OutputFile.mapper, XMLPath)); // 设置mapperXml生成路径 }) // 策略配置 .strategyConfig(builder -> { // 指定表 builder.addInclude("book") // 设置需要生成的表名 // .addInclude("news_detail") .enableCapitalMode() // Entity 策略配置 // 逻辑删除/乐观锁 字段 .entityBuilder() .versionColumnName("version") .versionPropertyName("version") .logicDeleteColumnName("deleted") /.enableLombok() // 启动 lombok 模型 .mapperBuilder() .enableBaseResultMap() .enableMapperAnnotation() ; }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute(); } }
# SQL日志
日志打印通过 p6spy实现日志打印 , 通过数据源层级进行代理监听SQL , 代理驱动 com.mysql.cj.jdbc.Driver
maven依赖
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
配置
数据源配置 spring.datasource.dynamic.p6spy 设置为 true
(生产环境不建议启用该功能)
更多细节配置 spy.properties配置文件
# p6spy 性能分析插件配置文件
# mybatis-plus 也实现了支持p6spy的日志打印
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
# 日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
#deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# SQL语句打印时间格式
databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录 (超过2秒记录问题)
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 是否过滤 Log
filter=true
# 过滤 Log 时所排除的 sql 关键字,以逗号分隔 (排除检测数据库存活的SQL)
exclude=SELECT 1