com.baomidou
mybatis-plus
com.baomidou
mybatis-plus-boot-starter
spring:
application:
name: user
#自定义模块名name-ip,logback通过该字段区分日志来源
moduleName: user-dev
# 出现404错误时,抛出异常给应用层处理
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false #告诉 SpringBoot 不要为我们工程中的资源文件建立映射
redis:
host: 192.168.1.1 # 服务器地址
port: 6379 # 服务端口号
database: 0 # 数据库索引
password: 1 # 服务器连接密码默认为空
jedis:
pool:
max-active: 1000 # 连接池最大连接数
max-wait: 3000 # 连接池最大阻塞等待时间
max-idle: 500 # 连接池中最大的空闲连接
min-idle: 100 # 连接池中最小的空闲连接
time-between-eviction-runs: 60000
timeout: 200 # 连接超时时间
jackson:
time-zone: Asia/Shanghai
date-format: yyyy-MM-dd HH:mm:ss
# jdbc_config datasource
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
datasource:
master:
url: jdbc:mysql://192.168.1.1:3306/user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
maximum-pool-size: 15
auto-commit: true
idle-timeout: 30000
pool-name: springHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
slave:
url: jdbc:mysql://192.168.1.1:3306/user2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
maximum-pool-size: 15
auto-commit: true
idle-timeout: 30000
pool-name: springHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
mybatis-plus:
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapper-locations: classpath:mybatis/mapper/**/*Mapper.xml
global-config:
# 主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
# configuration:
# # 是否将sql打印到控制面板(该配置会将sql语句和查询的结果都打印到控制台)
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List getMethodList(Class> mapperClass) {
List methodList = super.getMethodList(mapperClass);
methodList.removeIf(e -> e instanceof Insert);
methodList.add(new com.user.common.injector.method.Insert());
return methodList;
}
}
@SuppressWarnings("all")
public class Insert extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
KeyGenerator keyGenerator = new NoKeyGenerator();
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
keyGenerator = new Jdbc3KeyGenerator();
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource, keyGenerator, keyProperty, keyColumn);
}
}
@EnableTransactionManagement
@Configuration
@MapperScan("com.user.*.*.mapper")
public class MyBatisConfig {
@Resource
private DataSource myRoutingDataSource;
@Autowired
private MybatisPlusInterceptor mybatisPlusInterceptor;
@Value("${mybatis-plus.mapper-locations}")
private String mapperLocations;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setPlugins(new Interceptor[]{mybatisPlusInterceptor});
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
GlobalConfig globalConfig=new GlobalConfig();
globalConfig.setSqlInjector(new MySqlInjector());
sqlSessionFactoryBean.setGlobalConfig(globalConfig);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
// SpringBoot项目集成mybatis打包为jar运行时setTypeAliasesPackage无效解决
VFS.addImplClass(SpringBootVFS.class);
return sqlSessionFactoryBean.getObject();
}
public DynamicTableNameInnerInterceptor getDynamicTableNameInnerInterceptor(){
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
HashMap map = new HashMap(2) {{
put("common",(sql, tableName) -> {
//common 代表可能向任何一张表进行操作
return ThreadLocalUtil.get("tableName");
});
}};
dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
return dynamicTableNameInnerInterceptor;
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//分表拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(getDynamicTableNameInnerInterceptor());
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
org.springframework.boot
spring-boot-starter-freemarker
包括对象entity、操作接口dao、service服务接口以及其实现类,entity对象将支持swagger、lombokModel注解生成、数据库表映射注解、以及父子类集成
entity.java.ftl文件如下
此文件对主键的处理,采用input方式,使之可传可不传id,并能够利用mysql的自增id
此文件将会从数据库表中读取信息,自动生成字段注释和字段默认值
package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
#list>
<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
#if>
/**
*
* ${table.comment!}
*
*
* @author ${author}
* @since ${date}
*/
<#if entityLombokModel>
@Data
<#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
#if>
@Accessors(chain = true)
#if>
<#if table.convert>
@TableName("${table.name}")
#if>
<#if swagger2>
@ApiModel(value="${table.comment!}", description="${table.comment!}")
#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}>#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#else>
public class ${entity} implements Serializable {
#if>
private static final long serialVersionUID = 1L;
<#-- ---------- BEGIN 字段循环遍历 ---------->
<#list table.fields as field>
<#if field.keyFlag>
<#assign keyPropertyName="${field.propertyName}"/>
#if>
<#if field.comment!?length gt 0>
<#if swagger2>
@ApiModelProperty(value = "${field.comment}")
<#else>
/**
* ${field.comment}
*/
#if>
#if>
<#if field.keyFlag>
<#-- 主键 -->
<#if field.keyIdentityFlag>
@TableId(value = "${field.name}", type = IdType.AUTO)
<#elseif idType??>
@TableId(value = "${field.name}", type = IdType.${idType})
<#elseif field.convert>
@TableId("${field.name}")
#if>
<#-- 普通字段 -->
<#elseif field.fill??>
<#-- ----- 存在字段填充设置 ----->
<#if field.convert>
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
<#else>
@TableField(fill = FieldFill.${field.fill})
#if>
<#elseif field.convert>
@TableField("${field.name}")
#if>
<#-- 乐观锁注解 -->
<#if (versionFieldName!"") == field.name>
@Version
#if>
<#-- 逻辑删除注解 -->
<#if (logicDeleteFieldName!"") == field.name>
@TableLogic
#if>
private ${field.propertyType} ${field.propertyName};
#list>
<#------------ END 字段循环遍历 ---------->
<#if !entityLombokModel>
<#list table.fields as field>
<#if field.propertyType == "boolean">
<#assign getprefix="is"/>
<#else>
<#assign getprefix="get"/>
#if>
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
<#if entityBuilderModel>
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
<#else>
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#if>
this.${field.propertyName} = ${field.propertyName};
<#if entityBuilderModel>
return this;
#if>
}
#list>
#if>
<#if entityColumnConstant>
<#list table.fields as field>
public static final String ${field.name?upper_case} = "${field.name}";
#list>
#if>
<#if activeRecord>
@Override
protected Serializable pkVal() {
<#if keyPropertyName??>
return this.${keyPropertyName};
<#else>
return null;
#if>
}
#if>
<#if !entityLombokModel>
@Override
public String toString() {
return "${entity}{" +
<#list table.fields as field>
<#if field_index==0>
"${field.propertyName}=" + ${field.propertyName} +
<#else>
", ${field.propertyName}=" + ${field.propertyName} +
#if>
#list>
"}";
}
#if>
}
mapper.java.ftl
此文件将会生成mapper类,这个类将会集成BaseMapper类(Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能)
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
/**
*
* ${table.comment!} Mapper 接口
*
*
* @author ${author}
* @since ${date}
*/
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
}
#if>
mapper.xml.ftl
此文件申明开启二级缓存,并生成一个mapper映射文件,这个映射文件只编码了映射关系,没有实际的crud操作
<#if enableCache>
#if>
<#if baseResultMap>
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
#if>
#list>
<#list table.commonFields as field><#--生成公共字段 -->
#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
#if>
#list>
#if>
<#if baseColumnList>
<#list table.commonFields as field>
${field.name},
#list>
${table.fieldNames}
#if>
服务接口
service.java.ftl
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
/**
*
* ${table.comment!} 服务类
*
*
* @author ${author}
* @since ${date}
*/
<#if kotlin>
interface ${table.serviceName} : ${superServiceClass}<${entity}>
<#else>
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
}
#if>
serviceImpl.java.ftl
服务dao实现类
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
/**
*
* ${table.comment!} 服务实现类
*
*
* @author ${author}
* @since ${date}
*/
@Slf4j
@Service
<#if kotlin>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
}
<#else>
@Transactional(rollbackFor = Exception.class)
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {
}
#if>
需要创建一个main方法MybatisPlusGenerator,其中“模块名”将会被拼接成com.xxx.user.db.dao....这样的文件路径
public static void main(String[] args) {
MybatisPlusGenerator.generate("username", "数据库名称", new String[]{"表名"},
"springcloud模块文件夹名","模块名", true, true);
}
public class MybatisPlusGenerator {
public static void generate(String author, String dataBase, String[] tables, String subProjectName,
String moduleName, boolean isExtendSuper, boolean isCreateDao ) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
String projectPath = System.getProperty("user.dir")+"/"+subProjectName;
mpg.setGlobalConfig(getGlobalConfig(projectPath, author));
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://192.168.1.1:3306/" + dataBase + "?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
mpg.setPackageInfo(getPackageConfig(moduleName));
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
}
};
List focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return projectPath + "/src/main/resources/mybatis/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
String daoTemplate = null;
String daoITemplate = null;
if(isCreateDao){
daoITemplate = "template/mybatis/service.java";
daoTemplate = "template/mybatis/serviceImpl.java";
}
mpg.setTemplate(new TemplateConfig()
.setEntity("template/mybatis/entity.java")
.setXml(null)
.setController(null)
.setService(daoITemplate)
.setServiceImpl(daoTemplate));
mpg.setStrategy(getStrategyConfig(tables,isExtendSuper));
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
public static GlobalConfig getGlobalConfig(String projectPath, String author){
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor(author);
gc.setOpen(false);
//是否开启swagger注释
gc.setSwagger2(true);
//是否覆盖文件
gc.setFileOverride(true);
//开启 BaseResultMap
gc.setBaseResultMap(true);
// baseColumnList
gc.setBaseColumnList(true);
//service改名为dao
gc.setServiceName("I%sDao");
gc.setServiceImplName("%sDaoImpl");
return gc;
}
public static PackageConfig getPackageConfig(String moduleName){
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.xxx");
pc.setModuleName(moduleName);
pc.setController("controller");
pc.setEntity("db.entity");
pc.setService("db.dao");
pc.setServiceImpl("db.dao.impl");
pc.setMapper("db.mapper");
return pc;
}
public static StrategyConfig getStrategyConfig(String[] tables, boolean isExtendSuper){
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
if(isExtendSuper){
strategy.setSuperEntityClass("com.xxx.common.generator.base.BaseEntity");
}
strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
strategy.setTablePrefix("ad");
strategy.setEntityLombokModel(true);
strategy.setInclude(tables);
if(isExtendSuper){
strategy.setSuperEntityColumns("id","creator","updator");
}
strategy.setSuperServiceClass(IDao.class);
strategy.setSuperServiceImplClass(DaoImpl.class);
strategy.setControllerMappingHyphenStyle(true);
return strategy;
}
以上生成entity文件由于需要继承BaseEntity类,此类主要是将表的通用字段进行抽象,至于创建时间和修改时间都不要传值,每次用mysql的默认值CURRENT_TIMESTAMP即可(前提是字段类型是timestamp)
@Data
public abstract class BaseEntity implements Serializable{
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.INPUT)
private Long id;
@TableField(value = "creator", fill = FieldFill.INSERT)
private String creator;
@TableField(value = "updator", fill = FieldFill.INSERT_UPDATE)
private String updator;
}
相关的自动生成crud的工具类到此已经完成,生成的路径如下