SpringBoot 集成MyBatis-Plus
MyBatis-Plus简介
简介:
官网: https://baomidou.com/guide/
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
-
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
-
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
代码托管
Gitee (opens new window)| Github
相关注解
@Mapper
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
添加位置:接口类上面
如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan
@MapperScan
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的接口实现类
添加位置:是在Springboot启动类上面添加
表名注解
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名(@since 3.3.1) |
关于
autoResultMap
的说明:mp会自动构建一个
ResultMap
并注入到mybatis里(一般用不上).下面讲两句: 因为mp底层是mybatis,所以一些mybatis的常识你要知道,mp只是帮你注入了常用crud到mybatis里 注入之前可以说是动态的(根据你entity的字段以及注解变化而变化),但是注入之后是静态的(等于你写在xml的东西) 而对于直接指定typeHandler
,mybatis只支持你写在2个地方:
定义在resultMap里,只作用于select查询的返回结果封装
定义在
insert
和update
sql的#{property}
里的property
后面(例:#{property,typehandler=xxx.xxx.xxx}
),只作用于设置值
而除了这两种直接指定typeHandler
,mybatis有一个全局的扫描你自己的typeHandler
包的配置,这是根据你的property
的类型去找typeHandler
并使用.
主键注解
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 主键类型 |
值 | 描述 |
---|---|
AUTO | 数据库ID自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert前自行set主键值 |
ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认default方法) |
ID_WORKER | 分布式全局唯一ID 长整型类型(please use ASSIGN_ID ) |
UUID | 32位UUID字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR | 分布式全局唯一ID 字符串类型(please use ASSIGN_ID ) |
字段注解
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 数据库字段名 |
el | String | 否 | "" | 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,参考(opens new window) |
update | String | 否 | "" | 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性) |
insertStrategy | Enum | N | DEFAULT | 举例:NOT_NULL: insert into table_a(column) values (#{columnProperty}) |
updateStrategy | Enum | N | DEFAULT | 举例:IGNORED: update table_a set column=#{columnProperty} |
whereStrategy | Enum | N | DEFAULT | 举例:NOT_EMPTY: where column=#{columnProperty} |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC类型 (该默认值不代表会按照该值生效) |
typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
关于
jdbcType
和typeHandler
以及numericScale
的说明:
numericScale
只生效于 update 的sql.jdbcType
和typeHandler
如果不配合@TableName#autoResultMap = true
一起使用,也只生效于 update 的sql. 对于typeHandler
如果你的字段类型和set进去的类型为equals
关系,则只需要让你的typeHandler
让Mybatis加载到即可,不需要使用注解
FieldStrategy(opens new window)
值 | 描述 |
---|---|
IGNORED | 忽略判断 |
NOT_NULL | 非NULL判断 |
NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断) |
DEFAULT | 追随全局配置 |
值 | 描述 |
---|---|
DEFAULT | 默认不处理 |
INSERT | 插入时填充字段 |
UPDATE | 更新时填充字段 |
INSERT_UPDATE | 插入和更新时填充字段 |
乐观锁注解、标记 @Verison
在字段上
枚举类注解(注解在枚举字段上)
表字段逻辑处理注解(逻辑删除)
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 逻辑未删除值 |
delval | String | 否 | "" | 逻辑删除值 |
序列主键策略 oracle
属性:value、resultMap
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 序列名 |
clazz | Class | 否 | Long.class | id的类型, 可以指定String.class,这样返回的Sequence值是字符串"1" |
详见 插件主体
配置
详见官网: https://baomidou.com/config/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE
条件构造器
AbstractWrapper
我们主要说他的两个子类,QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) ,其主要用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
基本写法:基本写法如下,构造我们需要查询的条件,然后把wrapper传入对应的查询方法即可。
QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.allEq(Map<R, V> params) wrapper.eq(R column, Object val) wrapper.ne(R column, Object val) ....... wrapper.ge(R column, Object val) wrapper.lt(R column, Object val) //或者使用LambdaQueryWrapper 以链式调用 //LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery().allEq(); //Wrappers.lambdaQuery().eq() //Wrappers.lambdaQuery().ne() ...... //Wrappers.lambdaQuery().ge() //Wrappers.lambdaQuery().lt()
allEq
allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull) allEq(BiPredicate<R, V> filter, Map<R, V> params) allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
假设我们要查询[id=1,username=李白] 的人,写法如下
QueryWrapper<User> wrapper = new QueryWrapper<>(); Map map = new HashMap<>(); map.put("id",1); map.put("username","李白"); wrapper.allEq(map); //或者使用LambdaQueryWrapper 以链式调用 //LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery().allEq(map); //List<User> list = userService.list(lambdaQueryWrapper); List<User> list = userService.list(wrapper);
eq
eq(R column, Object val) eq(boolean condition, R column, Object val)
示例:其他例子是如下写法,后边会简写
假设查询[username=李白] 的人,
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>().eq(User::getUsername,"李白"); List<User> list = userService.list(lambdaQueryWrapper); //或者 //QueryWrapper<User> wrapper = new QueryWrapper<User>(); //wrapper.eq("username","李白"); //List<User> list = userService.list(lambdaQueryWrapper);
ne
ne(R column, Object val) ne(boolean condition, R column, Object val) //例:查询年龄不等于18的人--> wrapper.ne("age",18);
gt
gt(R column, Object val) gt(boolean condition, R column, Object val) //例:查询年龄大于18的人--> wrapper.gt("age",18);
ge
ge(R column, Object val) ge(boolean condition, R column, Object val) //例:查询年龄大于等于18的人--> wrapper.ge("age",18);
lt
lt(R column, Object val) lt(boolean condition, R column, Object val) //例:查询年龄小于18的人--> wrapper.lt("age",18);
le
le(R column, Object val) le(boolean condition, R column, Object val) //例:查询年龄小于等于18的人--> wrapper.le("age",18);
between
between(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2) //例:查询年龄范围在18-28的人--> wrapper.between("age",18,28);
notBetween
notBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2) //例:查询年龄范围不在18-28的人--> wrapper.notBetween("age",18,28);
like
like(R column, Object val) like(boolean condition, R column, Object val) //wrapper.like("username","李") ---> username like '%李%'
notLike
notLike(R column, Object val) notLike(boolean condition, R column, Object val) //wrapper.notLike("username","李") ---> username not like '%李%'
likeLeft
likeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val) //wrapper.likeLeft("username", "李") ---> name like '%李'
likeRight
likeRight(R column, Object val) likeRight(boolean condition, R column, Object val) //wrapper.likeRight("username", "李") ---> name like '李%'
isNull
isNull(R column) isNull(boolean condition, R column) //wrapper.isNull("username") ---> username is null
isNotNull
isNotNull(R column) isNotNull(boolean condition, R column) //wrapper.isNotNull("username") ---> username is not null
in
in(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value) //wrapper.in("age",{18,19,20}) ---> age in (18,19,20)
in(R column, Object... values) in(boolean condition, R column, Object... values) //wrapper.in("age", 18,19,20) ---> age in (18,19,20)
notIn
notIn(R column, Collection<?> value) notIn(boolean condition, R column, Collection<?> value) //wrapper.in("age",{18,19,20}) ---> age in (18,19,20)
notIn(R column, Object... values) notIn(boolean condition, R column, Object... values) //wrapper.notIn("age", 18,19,20) ---> age not in (18,19,20)
inSql
inSql(R column, String inValue) inSql(boolean condition, R column, String inValue) //wrapper.inSql("age", "1,2,3,4,5,6") ---> age in (1,2,3,4,5,6) //wrapper.inSql("id", "select id from user where id < 3") ---> id in (select id from user where id < 3)
notInSql
notInSql(R column, String inValue) notInSql(boolean condition, R column, String inValue) //wrapper.notInSql("age", "1,2,3,4,5,6") ---> age in (1,2,3,4,5,6) //wrapper.notInSql("id", "select id from user where id < 3") ---> id not in (select id from user where id < 3)
groupBy
groupBy(R... columns) groupBy(boolean condition, R... columns) //wrapper.groupBy("id", "username") ---> group by id,username
orderByAsc
orderByAsc(R... columns) orderByAsc(boolean condition, R... columns) //wrapper.orderByAsc("id", "username") ---> order by id ASC,username ASC
orderByDesc
orderByDesc(R... columns) orderByDesc(boolean condition, R... columns) //wrapper.orderByDesc("id", "username") ---> order by id DESC,username DESC
orderBy
orderBy(boolean condition, boolean isAsc, R... columns) //wrapper.orderBy(true, true, "id", "username") ---> order by id ASC,username ASC
having
having(String sqlHaving, Object... params) having(boolean condition, String sqlHaving, Object... params) //wrapper.having("sum(age) > 10") ---> having sum(age) > 10 //wrapper.having("sum(age) > {0}", 11) ---> having sum(age) > 11
func
func(Consumer<Children> consumer) func(boolean condition, Consumer<Children> consumer) //主要方便在出现if...else下调用不同方法能不断链 //wrapper.func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
or
or拼接
or() or(boolean condition) //wrapper.eq("id",1).or().eq("username","李白")--->id = 1 or username = '李白'
or嵌套
or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer) //wrapper.or(i -> i.eq("username", "李白").ne("age", 18)) ---> or (username = '李白' and age <> 18)
and
and(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer) //wrapper.and(i -> i.eq("username", "李白").ne("age", 18)) ---> and (username = '李白' and age <> 18)
nested
nested(Consumer<Param> consumer) nested(boolean condition, Consumer<Param> consumer) //正常嵌套 不带 AND 或者 OR //wrapper.nested(i -> i.eq("username", "李白").ne("age", 18)) ---> (username = '李白' and age <> 18)
apply
apply(String applySql, Object... params) apply(boolean condition, String applySql, Object... params)
拼接 sql
注意事项:
该方法可用于数据库函数 动态入参的
params
对应前面applySql
内部的{index}
部分.这样是不会有sql注入风险的,反之会有!
//wrapper.apply("id = 1") ---> id = 1 //wrapper.apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'") ---> date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'") //wrapper.apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08") ---> date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last
last(String lastSql) last(boolean condition, String lastSql) //wrapper.last("limit 1")
-
无视优化规则直接拼接到 sql 的最后
注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
exists
exists(String existsSql) exists(boolean condition, String existsSql) //wrapper.exists("select id from table where age = 18") ---> exists (select id from table where age = 18)
notExists
notExists(String existsSql) notExists(boolean condition, String existsSql) //wrapper.notExists("select id from table where age = 18") ---> not exists (select id from table where age = 18)
QueryWrapper
说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件 及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
select
设置查询字段
select(String... sqlSelect) select(Predicate<TableFieldInfo> predicate) select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) //wrapper.select("id", "name", "age")
说明:
以上方法分为两类. 第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要
wrapper
内的entity
属性有值! 这两类方法重复调用以最后一次为准
UpdateWrapper
说明:
继承自
AbstractWrapper
,自身的内部属性entity
也用于生成 where 条件 及LambdaUpdateWrapper
, 可以通过new UpdateWrapper().lambda()
方法获取!
set
set(String column, Object val) set(boolean condition, String column, Object val) //wrapper.set("name", "李白")
setSql
setSql(String sql) //wrapper.setSql("name = '李白'")
lambda
-
获取
LambdaWrapper
在QueryWrapper
中是获取LambdaQueryWrapper
在UpdateWrapper
中是获取LambdaUpdateWrapper
使用 Wrapper 自定义SQL
注意事项:
需要
mybatis-plus
版本 >=3.0.7
param 参数名要么叫ew
,要么加上注解@Param(Constants.WRAPPER)
使用${ew.customSqlSegment}
不支持Wrapper
内的entity生成where语句
用注解
@Select("select * from mysql_data ${ew.customSqlSegment}") List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
用XML
List<MysqlData> getAll(Wrapper ew);
<select id="getAll" resultType="MysqlData"> SELECT * FROM mysql_data ${ew.customSqlSegment} </select>
SpringBoot 集成 MyBatis-Plus
1、新建工程
2、改pom文件
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--数据源已MySQL为例--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <!-- mybatis plus 代码生成器依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!-- 代码生成器模板 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> </dependencies>
3、编写yml文件
#指定端口,默认8080 server: port: 9000 #配置数据源 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=true username: root password: root #配置mybatisplus mybatis-plus: configuration: map-underscore-to-camel-case: true #开启驼峰功能 auto-mapping-behavior: full #mybatis核心配置文件中settings中配置,指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。默认是partial,这是一种全局设置 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: banner: true #是否控制台 print mybatis-plus 的 LOGO db-config: id-type: auto #id 类型 table-underline: true #表名是否使用下划线命名,默认数据库表使用下划线命名 # logic-delete-value: 1 #逻辑已删除值默认1 # logic-not-delete-value: 0 #逻辑未删除值默认0 mapper-locations: classpath*:mapper/**/*Mapper.xml type-aliases-package: com.lovemio.mybatisplus.entity #实体的包路径,写resultType时可以直接写类名,不用再写全类名 #设置日志输出级别 logging: level: root: info com.lovemio.mybatisplus: debug
4、启动类
启动类添加注解@MapperScan 扫描mapper路径
@SpringBootApplication @MapperScan("com.lovemio.mybatisplus.mapper") public class SpringbootMybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(SpringbootMybatisPlusApplication.class, args); } }
5、配置分页插件
分页插件从3.4.0版本对此部分有更新,如果是旧版本升级,会出现分页失效问题,详见官网:
https://baomidou.com/guide/interceptor.html#mybatisplusinterceptor
/** * 配置分页插件 * */ @Configuration @MapperScan("com.lovemio.mybatisplus.mapper")//mapper所在位置 public class MybatisPlusConfig { /**3.4.0版本对此部分有更新,如果是旧版本升级,会出现分页失效问题, * 同时idea会提示PaginationInterceptor过时,新版本改用了MybatisPlusInterceptor * 旧版本配置 * 分页插件 */ /*@Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }*/ /** * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 */ @Bean public MybatisPlusInterceptor myBatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } }
6、代码生成
测试表:user
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `nickname` varchar(255) DEFAULT NULL, `user_status` tinyint(4) DEFAULT NULL, `dep_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
代码生成器相关配置: https://baomidou.com/config/generator-config.html#datasource
官网有详细实例,参考: https://baomidou.com/guide/generator.html#%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B
注意:如果是子module形式,生成代码的位置需要加当前子Module的项目名
String projectPath = System.getProperty("user.dir")+"/springboot-mybatis-plus";//子项目名为springboot-mybatis-plus
public class CodeGenerator { /** * <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")+"/springboot-mybatis-plus";//如果是子项目,加上子项目名称,不然无法定位到当前项目的位置; gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("lwj"); gc.setOpen(false);//生成后打开文件目录 //覆盖文件 gc.setFileOverride(false); // gc.setSwagger2(true); 实体属性 Swagger2 注解 mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&useSSL=false&characterEncoding=utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); // pc.setModuleName(scanner("模块名")); pc.setParent("com.lovemio.mybatisplus"); pc.setEntity("entity"); pc.setMapper("mapper"); pc.setService("service"); pc.setServiceImpl("service.impl"); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker String templatePath = "/templates/mapper.xml.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.setController("/ftl/controller.java");//如不自定义,注释即可 templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setSuperEntityClass("com.baomidou.mybatisplus.extension.activerecord.Model");//父类Entity,没有注释即可 strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // 公共父类 // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!"); // 写于父类中的公共字段 strategy.setSuperEntityColumns("id"); strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
执行main方法,在控制台输入表名即可,表必须存在啊,不然生成个寂寞啊。
自定义模板示例:可根据自己需求生成对应的方法,具体写法可参考下边模板。
package ${package.Controller}; import org.springframework.web.bind.annotation.*; import org.springframework.beans.factory.annotation.Autowired; import com.lovemio.mybatisplus.service.${table.serviceName}; import com.lovemio.mybatisplus.entity.${entity}; import lombok.extern.slf4j.Slf4j; import com.lovemio.mybatisplus.utils.Result; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.List; <#if restControllerStyle> import org.springframework.web.bind.annotation.RestController; <#else> import org.springframework.stereotype.Controller; </#if> <#if superControllerClassPackage??> import ${superControllerClassPackage}; </#if> /** * <p> * ${table.comment!} 前端控制器 * </p> * * @author ${author} * @since ${date} * @version v1.0 */ <#if restControllerStyle> @RestController <#else> @Controller </#if> @Slf4j @RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if><#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>") <#if kotlin> class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if> <#else> <#if superControllerClass??> public class ${table.controllerName} extends ${superControllerClass} { <#else> public class ${table.controllerName} { </#if> @Autowired private ${table.serviceName} ${(table.serviceName?substring(1))?uncap_first}; /** * 查询分页数据 */ @RequestMapping(value = "/list") public Result findListByPage(@RequestParam(name = "pageNum", defaultValue = "1") int pageNum,@RequestParam(name = "pageSize", defaultValue = "20") int pageSize){ Page<${entity}> page = ${(table.serviceName?substring(1))?uncap_first}.page(new Page<>(pageNum, pageSize)); return Result.success(page); } /** * 根据id查询 */ @RequestMapping(value = "/getById") public Result getById(@RequestParam("id") String id){ return Result.success(${(table.serviceName?substring(1))?uncap_first}.getById(id)); } /** * 新增 */ @RequestMapping(value = "/add", method = RequestMethod.POST) public Result add(@RequestBody ${entity} ${entity?uncap_first}){ return Result.success(${(table.serviceName?substring(1))?uncap_first}.save(${entity?uncap_first})); } /** * 删除 */ @RequestMapping(value = "/del") public Result del(@RequestParam("id") Integer id){ return Result.success(${(table.serviceName?substring(1))?uncap_first}.removeById(id)); } /** * 批量删除 */ @RequestMapping(value = "/batchDel") public Result batchDel(@RequestParam("ids") List<String> ids){ return Result.success(${(table.serviceName?substring(1))?uncap_first}.removeByIds(ids)); } /** * 修改 */ @RequestMapping(value = "/update", method = RequestMethod.POST) public Result update(@RequestBody ${entity} ${entity?uncap_first}){ return Result.success(${(table.serviceName?substring(1))?uncap_first}.saveOrUpdate(${entity?uncap_first})); } } </#if>
其中,模板位置在resources/ftl 目录下
生成示例
7、效果展示