SpringBoot教程&笔记|Demo04-整合MyBatis-Plus 有更新!

  heardfate

本文主要讲解如何在springboot下整合mybatis-plus,并访问mysql数据库。

表结构

现有一张 User 表,其表结构如下:

id name age email
1 Jone 18 test1@baomidou.com
2 Jack 20 test2@baomidou.com
3 Tom 28 test3@baomidou.com
4 Sandy 21 test4@baomidou.com
5 Billie 24 test5@baomidou.com

其对应的数据库 Schema 脚本如下:

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)
);

其对应的数据库 Data 脚本如下:

DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

如果执行delete提示Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect. 就先执行下SET SQL_SAFE_UPDATES =0;命令,接着就可以了!

创建工程

创建一个 Spring Boot 工程
参考SpringBoot教程&笔记|Demo01-构建一个简单的Web应用程来创建

添加依赖

引入 mybatis-plus-boot-starterlombokmysql-connector-javafreemarkerswagger2 依赖:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
	<version>8.0.12</version>
    </dependency>
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.28</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
</dependencies>

添加依赖

配置

application.yml 配置文件中添加 mysql 数据库的相关配置:

# DataSource Config
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useUnicode=yes&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
	username: testuser
	password: 
	driver-class-name: com.mysql.cj.jdbc.Driver

配置

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Dao、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

package com.heardfate.springboot.demo;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
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.Collections;
import java.util.List;

/**
 * @program: springboot-demo04
 * @since: 2018/10/23
 * @author: Mr.HeardFate
 */
public class CodeGenerator {
	/**
	 * 包名 
	 */  
	 private static final String PACKAGE_NAME = "com.heardfate.springboot.demo";
	/**
	 * 模块名称 
	 */  
	 private static final String MODULE_NAME = "demo04";
	 private static final String NAME = "";
	/**
	 * 输出文件的路径 
	 */  
	 private static final String OUT_PATH = System.getProperty("user.dir") + NAME + "/src/main/java";
	/**
	 * 输出文件的路径 
	 */  private static final String OUT_PATH_MAPPING = System.getProperty("user.dir") + NAME + "/src/main/resources/mapper";
	/**
	 * 代码生成者 
	 */  
	 private static final String AUTHOR = "Heardfate";

	/**
	 * JDBC相关配置 
	 */  
	 private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
	 private static final String URL = "jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&useSSL=false" + "&characterEncoding=UTF-8";
	 private static final String USER_NAME = "testuser";
	 private static final String PASSWORD = "";

	/**
	 * MySQL 生成演示 
	 */  
	 public static void main(String[] args) {
	 // 自定义需要填充的字段
	 List tableFillList = new ArrayList();
	 //如 每张表都有一个创建时间、修改时间
	 //而且这基本上就是通用的了,新增时,创建时间和修改时间同时修改 
	 //修改时,修改时间会修改, 
	 //虽然像Mysql数据库有自动更新几只,但像ORACLE的数据库就没有了, 
	 //使用公共字段填充功能,就可以实现,自动按场景更新了。 
	 //如下是配置 
	 //TableFill createField = new TableFill("gmt_create", FieldFill.INSERT); 
	 //TableFill modifiedField = new TableFill("gmt_modified", FieldFill.UPDATE); 
	 //tableFillList.add(createField); 
	 //tableFillList.add(modifiedField);
	 // 代码生成器  
	 AutoGenerator mpg = new AutoGenerator();
	  mpg.setGlobalConfig(// 全局配置
	  new GlobalConfig().setOutputDir(OUT_PATH)// 输出目录
	  .setFileOverride(true)// 是否覆盖文件
	  .setActiveRecord(true)// 开启 activeRecord 模式
	  .setEnableCache(false)// XML 二级缓存
	  .setBaseResultMap(false)// XML ResultMap
	  .setBaseColumnList(true)// XML columList
	  .setAuthor(AUTHOR)// 自定义文件命名,注意 %s 会自动填充表实体属性!
	  .setXmlName("%sMapper")
	  .setMapperName("%sDao")
	  .setSwagger2(true)
	  // .setServiceName("MP%sService")
	  // .setServiceImplName("%sServiceDiy") 
	  // .setControllerName("%sAction")  );

	mpg.setDataSource(// 数据源配置
	new DataSourceConfig().setDbType(DbType.MYSQL)// 数据库类型
	.setDriverName(DRIVER).setUsername(USER_NAME).setPassword(PASSWORD).setUrl(URL));
	mpg.setStrategy(// 策略配置
	new StrategyConfig()
	// .setCapitalMode(true)// 全局大写命名
	 //.setTablePrefix(new String[]{"table_", "test_"})// 此处可以修改为您的表前缀  
	 .setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
	.setColumnNaming(NamingStrategy.underline_to_camel)
	//.setLogicDeleteFieldName("is_delete") //逻辑删除字段
	// .setInclude(new String[] {"user"}) // 需要生成的表 
	// .setExclude(new String[]{"test"}) // 排除生成的表 
	// 自定义实体,公共字段 
	//.setSuperEntityColumns(new String[]{"gmt_create", "gmt_modified", "create_user", "executor", "is_delete", "pass_key"}) 
	//.setTableFillList(tableFillList)//和68行对应,设置公共填充字段 
	// 自定义实体父类 
	//.setSuperEntityClass("com.baomidou.demo.base.BsBaseEntity") 
	// 自定义 mapper 父类 
	// .setSuperMapperClass("com.baomidou.demo.base.BsBaseMapper") 
	// 自定义 service 父类 
	// .setSuperServiceClass("com.baomidou.demo.base.BsBaseService") 
	// 自定义 service 实现类父类 
	// .setSuperServiceImplClass("com.baomidou.demo.base.BsBaseServiceImpl") 
	// 自定义 controller 父类 
	// .setSuperControllerClass("com.baomidou.demo.TestController") 
	// 【实体】是否生成字段常量(默认 false) 
	// public static final String ID = "test_id";  
	.setEntityColumnConstant(true)
	// 【实体】是否为构建者模型(默认 false)
	// public User setName(String name) {this.name = name; return this;}  
	.setEntityBuilderModel(true)
	// 【实体】是否为lombok模型(默认 false)document
	.setEntityLombokModel(true)
	// Boolean类型字段是否移除is前缀处理
	.setEntityBooleanColumnRemoveIsPrefix(true)
	//生成 @RestController 控制器
	.setRestControllerStyle(true) 
	//驼峰转连字符 
	//.setControllerMappingHyphenStyle(true)
	);
	mpg.setPackageInfo(// 包配置
	new PackageConfig().setModuleName(MODULE_NAME).setParent(PACKAGE_NAME)// 自定义包路径
	.setController("controller")// 这里是控制器包名,默认 web
	.setXml("mapper").setMapper("dao")
	);
	mpg.setCfg(
	// 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
	new InjectionConfig() {
	@Override
	public void initMap() {}}
	.setFileOutConfigList(
	Collections.singletonList(new FileOutConfig("/templates/mapper.xml" + ".ftl") {
	  // 自定义输出文件目录
	  @Override
	  public String outputFile(TableInfo tableInfo) { return OUT_PATH_MAPPING + "/" + MODULE_NAME + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; }
	  })));
	mpg.setTemplate(
	// 关闭默认 xml 生成,调整生成 至 根目录
	new TemplateConfig().setXml(null)
	// 自定义模板配置,模板可以参考源码 /mybatis-plus/src/main/resources/template 使用 copy
	// 至您项目 src/main/resources/template 目录下,模板名称也可自定义如下配置: 
	// .setController("..."); 
	// .setEntity("..."); 
	// .setMapper("..."); 
	// .setXml("..."); 
	// .setService("..."); 
	// .setServiceImpl("...");  );
	mpg.setTemplateEngine(new FreemarkerTemplateEngine());

	// 执行生成
	mpg.execute();
	}
}

运行Main方法后,自动生成对应代码
代码生成器
代码生成器

MybatisPlus配置

现在,我们添加MybatisPlus配置类
src/main/java/com/heardfate/springboot/demo/demo04/config/MybatisPlusConfig

package com.heardfate.springboot.demo.demo04.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

/**
 * @since: 2018/10/23
 * @author: Mr.HeardFate
 */
@Configuration
@MapperScan("com.heardfate.springboot.demo.demo04.dao")
public class MybatisPlusConfig {

}

MybatisPlus配置

开始使用

添加测试类,进行功能测试:

package com.heardfate.springboot.demo;

import com.heardfate.springboot.demo.demo04.dao.UserDao;
import com.heardfate.springboot.demo.demo04.entity.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

/**
 * @since: 2018/10/23
 * @author: Mr.HeardFate
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
  @Autowired
  private UserDao userDao;

  @Test
  public void testSelect() {
    System.out.println(("----- selectAll method test ------"));
    List userList = userDao.selectList(null);
    Assert.assertEquals(5, userList.size());
    userList.forEach(System.out::println);
  }
}

测试通过获取到数据库数据

2018-10-23 20:25:05.172  INFO 2483 --- [           main] c.heardfate.springboot.demo.SampleTest   : Started SampleTest in 9.063 seconds (JVM running for 10.273)
----- selectAll method test ------
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
2018-10-23 20:25:05.838  INFO 2483 --- [       Thread-1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2018-10-23 20:25:05.839  INFO 2483 --- [       Thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2018-10-23 20:25:05.852  INFO 2483 --- [       Thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

添加测试

BIGINT UNSIGNED映射为BigIntger(2018-11-03更新)

以上配置,如果数据库有字段类型为BIGINT UNSIGNED,自动映射为LONG类型,,通过设置,映射为BigIntger
BIGINT ,BIGINT UNSIGNED。
如果不是无符号类型,BIGINT(20)的取值范围为-9223372036854775808~9223372036854775807。
与Java.lang.Long的取值范围完全一致,可映射为Long;
而BIGINT(20) UNSIGNED的取值范围是0 ~ 18446744073709551615,其中一半的数据超出了Long的取值范围,需将其映射为BigInteger。

支持BigInteger

更新后完整的代码生成器代码为:

package com.heardfate.springboot.demo;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
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.Collections;
import java.util.List;

/**
 * @program: springboot-demo04
 * @since: 2018/10/23
 * @author: Mr.HeardFate
 */
public class CodeGenerator {
	/**
	 * 包名 
	 */  
	 private static final String PACKAGE_NAME = "com.heardfate.springboot.demo";
	/**
	 * 模块名称 
	 */  
	 private static final String MODULE_NAME = "demo04";
	 private static final String NAME = "";
	/**
	 * 输出文件的路径 
	 */  
	 private static final String OUT_PATH = System.getProperty("user.dir") + NAME + "/src/main/java";
	/**
	 * 输出文件的路径 
	 */  private static final String OUT_PATH_MAPPING = System.getProperty("user.dir") + NAME + "/src/main/resources/mapper";
	/**
	 * 代码生成者 
	 */  
	 private static final String AUTHOR = "Heardfate";

	/**
	 * JDBC相关配置 
	 */  
	 private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
	 private static final String URL = "jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&useSSL=false" + "&characterEncoding=UTF-8";
	 private static final String USER_NAME = "testuser";
	 private static final String PASSWORD = "";

	/**
	 * MySQL 生成演示 
	 */  
	 public static void main(String[] args) {
	 // 自定义需要填充的字段
	 List tableFillList = new ArrayList();
	 //如 每张表都有一个创建时间、修改时间
	 //而且这基本上就是通用的了,新增时,创建时间和修改时间同时修改 
	 //修改时,修改时间会修改, 
	 //虽然像Mysql数据库有自动更新几只,但像ORACLE的数据库就没有了, 
	 //使用公共字段填充功能,就可以实现,自动按场景更新了。 
	 //如下是配置 
	 //TableFill createField = new TableFill("gmt_create", FieldFill.INSERT); 
	 //TableFill modifiedField = new TableFill("gmt_modified", FieldFill.UPDATE); 
	 //tableFillList.add(createField); 
	 //tableFillList.add(modifiedField);
	 // 代码生成器  
	 AutoGenerator mpg = new AutoGenerator();
	  mpg.setGlobalConfig(// 全局配置
	  new GlobalConfig().setOutputDir(OUT_PATH)// 输出目录
	  .setFileOverride(true)// 是否覆盖文件
	  .setActiveRecord(true)// 开启 activeRecord 模式
	  .setEnableCache(false)// XML 二级缓存
	  .setBaseResultMap(false)// XML ResultMap
	  .setBaseColumnList(true)// XML columList
	  .setAuthor(AUTHOR)// 自定义文件命名,注意 %s 会自动填充表实体属性!
	  .setXmlName("%sMapper")
	  .setMapperName("%sDao")
	  .setSwagger2(true)
	  // .setServiceName("MP%sService")
	  // .setServiceImplName("%sServiceDiy") 
	  // .setControllerName("%sAction") 
	  );

	mpg.setDataSource(// 数据源配置
	new DataSourceConfig().setDbType(DbType.MYSQL)// 数据库类型
	.setDriverName(DRIVER).setUsername(USER_NAME).setPassword(PASSWORD).setUrl(URL)
	.setTypeConvert(new MySqlTypeConvert() {
	  // 自定义数据库表字段类型转换【可选】
	  @Override
	  public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
		System.out.println("待转换类型:" + fieldType);
		// 注意!!!processTypeConvert存在默认类型转换,如果不是你要的效果请自定义返回。
		String t = fieldType.toLowerCase();
		if (t.contains("unsigned")) {
		  if (t.contains("bigint")) {
			return DbColumnType.BIG_INTEGER;
		  }else{
			System.err.println("I am not know!");
			return super.processTypeConvert(globalConfig, fieldType);
		  }
		}else{
		  return super.processTypeConvert(globalConfig, fieldType);
		}
	  }
	 })
	);
	mpg.setStrategy(// 策略配置
	new StrategyConfig()
	// .setCapitalMode(true)// 全局大写命名
	 //.setTablePrefix(new String[]{"table_", "test_"})// 此处可以修改为您的表前缀  
	 .setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
	.setColumnNaming(NamingStrategy.underline_to_camel)
	//.setLogicDeleteFieldName("is_delete") //逻辑删除字段
	// .setInclude(new String[] {"user"}) // 需要生成的表 
	// .setExclude(new String[]{"test"}) // 排除生成的表 
	// 自定义实体,公共字段 
	//.setSuperEntityColumns(new String[]{"gmt_create", "gmt_modified", "create_user", "executor", "is_delete", "pass_key"}) 
	//.setTableFillList(tableFillList)//和68行对应,设置公共填充字段 
	// 自定义实体父类 
	//.setSuperEntityClass("com.baomidou.demo.base.BsBaseEntity") 
	// 自定义 mapper 父类 
	// .setSuperMapperClass("com.baomidou.demo.base.BsBaseMapper") 
	// 自定义 service 父类 
	// .setSuperServiceClass("com.baomidou.demo.base.BsBaseService") 
	// 自定义 service 实现类父类 
	// .setSuperServiceImplClass("com.baomidou.demo.base.BsBaseServiceImpl") 
	// 自定义 controller 父类 
	// .setSuperControllerClass("com.baomidou.demo.TestController") 
	// 【实体】是否生成字段常量(默认 false) 
	// public static final String ID = "test_id";  
	.setEntityColumnConstant(true)
	// 【实体】是否为构建者模型(默认 false)
	// public User setName(String name) {this.name = name; return this;}  
	.setEntityBuilderModel(true)
	// 【实体】是否为lombok模型(默认 false)document
	.setEntityLombokModel(true)
	// Boolean类型字段是否移除is前缀处理
	.setEntityBooleanColumnRemoveIsPrefix(true)
	//生成 @RestController 控制器
	.setRestControllerStyle(true) 
	//驼峰转连字符 
	//.setControllerMappingHyphenStyle(true)
	);
	mpg.setPackageInfo(// 包配置
	new PackageConfig().setModuleName(MODULE_NAME).setParent(PACKAGE_NAME)// 自定义包路径
	.setController("controller")// 这里是控制器包名,默认 web
	.setXml("mapper").setMapper("dao")
	);
	mpg.setCfg(
	// 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
	new InjectionConfig() {
	@Override
	public void initMap() {}}
	.setFileOutConfigList(
	Collections.singletonList(new FileOutConfig("/templates/mapper.xml" + ".ftl") {
	  // 自定义输出文件目录
	  @Override
	  public String outputFile(TableInfo tableInfo) { return OUT_PATH_MAPPING + "/" + MODULE_NAME + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; }
	  })));
	mpg.setTemplate(
	// 关闭默认 xml 生成,调整生成 至 根目录
	new TemplateConfig().setXml(null)
	// 自定义模板配置,模板可以参考源码 /mybatis-plus/src/main/resources/template 使用 copy
	// 至您项目 src/main/resources/template 目录下,模板名称也可自定义如下配置: 
	// .setController("..."); 
	// .setEntity("..."); 
	// .setMapper("..."); 
	// .setXml("..."); 
	// .setService("..."); 
	// .setServiceImpl("..."); 
	);
	mpg.setTemplateEngine(new FreemarkerTemplateEngine());

	// 执行生成
	mpg.execute();
	}
}

BIGINT UNSIGNED插入报错问题(2018-11-09更新)

如果数据库字段类型为BIGINT UNSIGNED,映射为BigIntger。获取数据会正常,添加数据会报错 Could not set property 'id' of 'class XXX.entity.User' with value '1060015479998566402' Cause: java.lang.IllegalArgumentException: argument type mismatch
因为没有指定ID生成策略,mybatis-plus默认为ID_WORKER,是Long类型!
所以指定为BigIntger,可以通过手动设置ID,如bean.setId(new BigInteger(IdWorker.getIdStr()));
Issues:UNSIGNED BIGINT(20)映射问题

参考

MyBatis-Plus 快速开始
MyBatis-Plus 代码生成器