SpringBoot整合mybatis-plus并实现代码自动生成 ,自定义数据库字段类型转换
1、引入maven
(代码是连接mysql)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.20</version> </dependency>
DruidConfig.java
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.*; import javax.sql.DataSource; import java.io.IOException; /** * druid数据源配置类 */ @Configuration public class DruidConfig { /** * 去除监控页面底部的广告 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Bean @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) { // 获取web监控页面的参数 DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); // 提取common.js的配置路径 String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); final String filePath = "support/http/resources/js/common.js"; // 创建filter进行过滤 Filter filter = new Filter() { @Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); // 重置缓冲区,响应头不会被重置 response.resetBuffer(); // 获取common.js String text = Utils.readFromResource(filePath); // 正则替换banner, 除去底部的广告信息 text = text.replaceAll("<a.*?banner\"></a><br/>", ""); text = text.replaceAll("powered.*?shrek.wang</a>", ""); response.getWriter().write(text); } @Override public void destroy() { } }; FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(filter); registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; } }
MybatisPlusConfig.java 配置类(配置项根据自己的来)
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * Mybatis Plus 配置 * * @author */ @EnableTransactionManagement(proxyTargetClass = true) @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(paginationInnerInterceptor()); // 乐观锁插件 interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); // 阻断插件 interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); return interceptor; } /** * 分页插件,自动识别数据库类型 https://baomidou.com/pages/97710a/#paginationinnerinterceptor */ public PaginationInnerInterceptor paginationInnerInterceptor() { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); // 设置数据库类型为mysql paginationInnerInterceptor.setDbType(DbType.MYSQL); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInnerInterceptor.setMaxLimit(-1L); return paginationInnerInterceptor; } /** * 乐观锁插件 https://baomidou.com/pages/0d93c0/#optimisticlockerinnerinterceptor */ public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { return new OptimisticLockerInnerInterceptor(); } /** * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/pages/c571bc/#blockattackinnerinterceptor */ public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { return new BlockAttackInnerInterceptor(); } }
加了上面这个 BlockAttackInnerInterceptor 拦截器的话
如果进行全表操作会报错:Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
application.yml 增加
mybatis-plus: #扫描mapper文件所在位置 mapper-locations: classpath*:mapper/**/*Mapper.xml #可以指定实体类所在包路径 typeAliasesPackage: com.rnce.model global-config: banner: false db-config: # 主键类型 0:数据库ID自增 1.未定义 2.用户输入 3 id_worker 4.uuid 5.id_worker字符串表示 id-type: AUTO #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断" field-strategy: NOT_NULL # 默认数据库表下划线命名 table-underline: true # configuration: # map-underscore-to-camel-case: false # cache-enabled: true #配置的缓存的全局开关 # lazyLoadingEnabled: true #延时加载的开关 # multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用 spring: #DATABASE CONFIG #autoconfigure: # exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure datasource: #driver-class-name: com.mysql.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver username: dsk password: Hz56 url: jdbc:mysql://106.103:3306/dwk?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=Asia/Shanghai type: com.alibaba.druid.pool.DruidDataSource #这里是配置druid连接池,以下都是druid的配置信息 druid: # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最大生存的时间,单位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置检测连接是否有效 testWhileIdle: true validationQuery: SELECT 1 FROM DUAL testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 设置白名单,不填则允许所有访问 allow: url-pattern: /druid/* # 控制台管理用户名和密码 login-username: admin login-password: 123 filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
如果需要批量操作
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。 MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。 只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
如果是postsql的话 依赖替换为
<!-- postgresql依赖 --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.5</version> </dependency>
驱动改为
driver-class-name: org.postgresql.Driver
启动器上增加 要把Mapper接口文件的包扫描进去
伪代码示例
entity
@Data public class History implements Serializable { @TableId(type = IdType.AUTO) private Integer id; private String name;
mapper
@Mapper public interface HistoryMapper extends BaseMapper<History> {
service
@Service public class HistoryService extends ServiceImpl<HistoryMapper,History> implements IService<History> { }
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; //交给spring管理 @Component public class MyMetaObjectHandler implements MetaObjectHandler { //使用mp实现添加操作,这个方法会执行,metaObject元数据(表中的名字,表中的字段) @Override public void insertFill(MetaObject metaObject) { //根据名称设置属性值 this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //使用mp实现修改操作,这个方法会执行 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",new Date(),metaObject); } }
字段增加
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
除了用自带的方法,也可以自定义sql
参考:https://baomidou.com/pages/10c804/#%E4%BD%BF%E7%94%A8-wrapper-%E8%87%AA%E5%AE%9A%E4%B9%89sql
分页转换参考
protected ResultBodyPageVO successPageResp(IPage pageImpl) { ResultBodyPageVO.CommonPageResultVOBuilder builder = new ResultBodyPageVO.CommonPageResultVOBuilder(); builder.success().data(pageImpl.getRecords()).totalSize(pageImpl.getTotal()) .pageNumber((int) (pageImpl.getCurrent())).totalPages((int) pageImpl.getPages()) .pageSize((int) pageImpl.getSize()); return builder.build(); }
import com.test.framework.rsps.ResultBody; import com.test.framework.rsps.ResultStatus; import lombok.Data; @Data public class ResultBodyPageVO<T> extends ResultBody<T> { /** * 当前页面 */ private long pageNumber;// 当前页 /** * 当前页尺寸 */ private long pageSize; /** * 总页数 */ private long totalPages;// 一共多少页 /** * 总行数 */ private long totalSize; public ResultBodyPageVO(int code, String message, T data,long pageNumber,long pageSize,long totalPages,long totalSize) { super(code, message, data); this.pageNumber = pageNumber; this.pageSize = pageSize; this.totalSize = totalSize; this.totalPages = totalPages; } public static class CommonPageResultVOBuilder<T>{ private int code; private String message; private T data; /** * 当前页面 */ private long pageNumber;// 当前页 private long pageSize; /** * 总页数 */ private long totalPages;// 一共多少页 private long totalSize; public ResultBodyPageVO.CommonPageResultVOBuilder code(int code){ this.code = code; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder success(){ this.code = ResultStatus.SUCCESS.getCode(); return this; } public ResultBodyPageVO.CommonPageResultVOBuilder fail(){ this.code = ResultStatus.FAILED.getCode(); return this; } public ResultBodyPageVO.CommonPageResultVOBuilder fail(String message){ this.code = ResultStatus.FAILED.getCode(); this.message = message; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder message(String message){ this.message = message; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder data(T data){ this.data = data; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder pageNumber(long pageNumber){ this.pageNumber = pageNumber; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder pageSize(long pageSize){ this.pageSize = pageSize; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder totalPages(long totalPages){ this.totalPages = totalPages; return this; } public ResultBodyPageVO.CommonPageResultVOBuilder totalSize(long totalSize){ this.totalSize = totalSize; return this; } public ResultBodyPageVO<T> build(){ return new ResultBodyPageVO<T>(code,message,data,pageNumber,pageSize,totalPages,totalSize); } } }
链式调用 lambda 式
// 区分:
// 链式调用 普通
UpdateChainWrapper<T> update();
// 链式调用 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();
// 等价示例:
query().eq("id", value).one();
lambdaQuery().eq(Entity::getId, value).one();
// 等价示例:
update().eq("id", value).remove();
lambdaUpdate().eq(Entity::getId, value).remove();
上面这个要在service层调用,可以直接获取lambdaQuery()这些对象
或者使用 Wrappers.lambadQuery(User.class).
代码生成类
CodeGenerator.java
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; public class CodeGenerator { public static void main(String[] args) { // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); //获取工作目录 String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); //作者 gc.setAuthor("yvioo"); //生成后是否打开资源管理器 gc.setOpen(false); //重新生成时文件是否覆盖 gc.setFileOverride(false); //去掉Service接口的首字母 gc.setServiceName("%sService"); gc.setIdType(IdType.AUTO); //主键策略 gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 gc.setSwagger2(false);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://1003:3306/mazc?serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("mc"); dsc.setPassword("Hz56"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); //模块名 pc.setModuleName(null); //指定生成的根目录 pc.setParent("com.test.cms.mybatisplus"); //以下设置的包名要在上面的目录下 //控制器的包名 pc.setController("controller"); //实体类的包名 pc.setEntity("entity"); pc.setService("service"); //实现类的包名 pc.setServiceImpl("service.impl"); //映射文件的包名 pc.setMapper("mapper");
//xml文件路径
pc.setXml("mapper.xml");
mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); //设置要生成的代码表名 strategy.setInclude("mrevice"); //数据库表映射到实体的命名策略 strategy.setNaming(NamingStrategy.underline_to_camel); //生成实体时去掉表前缀 strategy.setTablePrefix(pc.getModuleName() + "_"); //数据库表字段映射到实体的命名策略 strategy.setColumnNaming(NamingStrategy.underline_to_camel); // lombok 模型 @Accessors(chain = true) setter链式操作 strategy.setEntityLombokModel(true); //restful api风格控制器 strategy.setRestControllerStyle(true); //url中驼峰转连字符 strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } }
如果报错
Exception in thread "main" java.lang.NoClassDefFoundError: freemarker/template/Configuration
at com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine.init(FreemarkerTemplateEngine.java:41)
at com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine.init(FreemarkerTemplateEngine.java:34)
at com.baomidou.mybatisplus.generator.AutoGenerator.execute(AutoGenerator.java:106)
at com.kotei.backend.service.gen.GeneratorCodeConfig.main(GeneratorCodeConfig.java:72)
Caused by: java.lang.ClassNotFoundException: freemarker.template.Configuration
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more
添加依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> <scope>compile</scope> </dependency>
自定义数据表和java属性类型
dsc.setTypeConvert(new MySqlTypeConvert() { @Override public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) { System.out.println("转换类型:" + fieldType); //tinyint转换成Boolean if ( fieldType.toLowerCase().contains( "tinyint" ) ) { return DbColumnType.BOOLEAN; } //将数据库中datetime转换成date if ( fieldType.toLowerCase().contains( "datetime" ) ) { return DbColumnType.DATE; } return (DbColumnType) super.processTypeConvert(globalConfig, fieldType); } });
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了