# 02-Mybatis Plus
# 一、Mybatis Plus
# 1、引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
# 2、配置
mybatis-plus:
mapper-locations: classpath*:/com/**/dao/mapper/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.*.entity
global-config:
banner: false
#自定义sql注入器,不在推荐使用此方式进行配置,请使用自定义bean注入
db-config:
table-underline: true
#逻辑删除配置(下面3个配置)
logic-delete-value: 1
logic-not-delete-value: 0
id-type: auto
enable-sql-runner: true
configuration:
#配置的缓存的全局开关
cache-enabled: false
#打印sql语句,调试用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置数据库 下划线命名和java驼峰命名自动映射。
map-underscore-to-camel-case: true
#延时加载的开关
lazyLoadingEnabled: true
#开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
multipleResultSetsEnabled: true
#字段值为空时,也返回字段
call-setters-on-nulls: false
type-enums-package: com.**.enums
3、优化配置:
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
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 com.google.common.base.CaseFormat;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.MapWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* Mybatis Plus 配置
*
* @Author qixiaodong
* @Date 2020/8/28 16:58
*/
@Configuration
@MapperScan("com.**.dao")
public class MybatisPlusConfig {
/**
* 配置 selectMaps 返回 Map 时,key转换为驼峰命名形式 step:3
*/
@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer() {
return configuration -> {
configuration.setObjectWrapperFactory(new MapWrapperFactory());
configuration.setUseDeprecatedExecutor(false);
};
}
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
interceptor.addInnerInterceptor(optimisticLockerInterceptor());
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 乐观锁mybatis插件
*/
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
@Bean
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
return new BlockAttackInnerInterceptor();
}
/**
* 配置 selectMaps 返回 Map 时,key转换为驼峰命名形式 step:2
*
* @Author qixiaodong
* @Date 2020/8/10 15:36
*/
public static class MapWrapperFactory implements ObjectWrapperFactory {
@Override
public boolean hasWrapperFor(Object object) {
return object instanceof Map;
}
@Override
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
return new CustomWrapper(metaObject, (Map) object);
}
}
/**
* 配置 selectMaps 返回 Map 时,key转换为驼峰命名形式 step:1
*
* @Author qixiaodong
* @Date 2020/8/10 15:35
*/
public static class CustomWrapper extends MapWrapper {
public CustomWrapper(MetaObject metaObject, Map<String, Object> map) {
super(metaObject, map);
}
@Override
public String findProperty(String name, boolean useCamelCaseMapping) {
if (useCamelCaseMapping) {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name);
}
return name;
}
}
}
自动填充配置
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.common.utils.UserInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* Mybatis Plus 自动填充配置
*
* @Author qixiaodong
* @Date 2020/8/28 16:58
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 新增-自动填充配置
*/
@Override
public void insertFill(MetaObject metaObject) {
try {
log.info("insertFill metaObject={}", JSON.toJSONString(metaObject));
String guideName = UserInfoUtil.getGuideName();
LocalDateTime now = LocalDateTime.now();
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, now);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, now);
this.strictInsertFill(metaObject, "createUser", String.class, guideName);
this.strictInsertFill(metaObject, "updateUser", String.class, guideName);
} catch (Exception e) {
log.error(e.getMessage(), 3);
}
}
/**
* 修改-自动填充配置
*/
@Override
public void updateFill(MetaObject metaObject) {
try {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class,
LocalDateTime.now(ZoneId.of("+08:00")));
String guideName = UserInfoUtil.getGuideName();
this.strictUpdateFill(metaObject, "updateUser", String.class, guideName);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
# 二、注解关联查询
School 实体类
@Data public class School { private Integer id; private String name; private List<Student> students; }
Student 实体类
@Data public class Student { private Integer id; private String name; private String NO; private School school; }
SchoolMapper
public interface SchoolMapper { /**单表查询*/ @Select("select * from t_school as sc where sc.sc_id=#{id}") @Results({ @Result(column="sc_id",property="id"), @Result(column="sc_name",property="name") }) public School selectSchoolById(Integer id); /**一对多 多表查询*/ @Select("select * from t_school as sc where sc.sc_id=#{id}") @Results({ @Result(column="sc_id",property="id"), @Result(column="sc_name",property="name"), @Result(column="sc_id",property="students",many=@Many(select="com.dw.dao.StudentMapper.selectStudentBySchoolId",fetchType=FetchType.LAZY)) }) public School selectSchoolAndStudentsById(Integer id); }
StudentMapper
public interface StudentMapper { /**单表查询*/ @Select("select * from t_student as s where s.s_id=#{id}") @Results({ @Result(column="s_id",property="id"), @Result(column="s_name",property="name"), @Result(column="s_NO",property="NO") }) public Student selectStudentById(Integer id); /**多对一 多表查询*/ @Select("select * from t_student as s where s.s_id=#{id}") @Results({ @Result(column="s_id",property="id"), @Result(column="s_name",property="name"), @Result(column="s_NO",property="NO"), @Result(column="sc_id",property="school",javaType=School.class, one=@One(select="com.dw.dao.SchoolMapper.selectSchoolById")) }) public Student selectStudentAndSchoolById(Integer id); /**通过学校id查询学生*/ @Select("select s.s_id,s.s_name,s.s_NO from t_student as s where s.sc_id=#{id}") @Results({ @Result(column="s_id",property="id"), @Result(column="s_name",property="name"), @Result(column="s_NO",property="NO") }) public List<Student> selectStudentBySchoolId(Integer id); }
# 三、Mybatis 注解
注解 | 目标 | 对应的xml | 描述 |
---|---|---|---|
@CacheNamespace | 类 | 为给定的命名空间 (比如类) 配置缓存。 属性:implemetation,eviction, flushInterval,size 和 readWrite。 | |
@CacheNamespaceRef | 类 | 参照另外一个命名空间的缓存来使用。 属性:value,应该是一个名空间的字 符串值(也就是类的完全限定名) 。 | |
@ConstructorArgs | 方法 | 收集一组结果传递给一个劫夺对象的 构造方法。属性:value,是形式参数 的数组。 | |
@Arg | 方法 | 单 独 的 构 造 方 法 参 数 , 是 ConstructorArgs 集合的一部分。属性: id,column,javaType,typeHandler。 id 属性是布尔值, 来标识用于比较的属 性,和XML 元素相似 | |
@TypeDiscriminator | 方法 | 一组实例值被用来决定结果映射的表 现。 属性: column, javaType, jdbcType, typeHandler,cases。cases 属性就是实 例的数组。 | |
@Case | 方法 | 单独实例的值和它对应的映射。属性: value,type,results。Results 属性是结 果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定 | |
@Results | 方法 | 结果映射的列表, 包含了一个特别结果 列如何被映射到属性或字段的详情。 属 性:value, id。value 属性是 Result 注解的数组。 The id attribute is the name of the result mapping. | |
@Result | 方法 | 在列和属性或字段之间的单独结果映 射。属 性:id,column, property, javaType ,jdbcType ,type Handler, one,many。id 属性是一个布尔值,表 示了应该被用于比较(和在 XML 映射 中的相似)的属性。one 属性是单 独 的 联 系, 和 <association> 相 似 , 而 many 属 性 是 对 集 合 而 言 的 , 和 <collection> 相似。 它们这样命名是为了 避免名称冲突 | |
@One | 方法 | 复杂类型的单独属性值映射。属性: select,已映射语句(也就是映射器方 法)的完全限定名,它可以加载合适类 型的实例。注意:联合映射在注解 API 中是不支持的。这是因为 Java 注解的 限制,不允许循环引用。 fetchType, which supersedes the global configuration parameterlazyLoadingEnabled for this mapping. | |
@Many | 方法 | 复杂类型集合属性的映射。Attributes:select,它是映射语句(即mapper方法)的完全限定名,可以加载适当类型的实例集合fetchType,它将取代全局配置参数lazyloadingenabled。注意:联合映射在 Java 注解中是不支持的。这是因为 Java 注 解的限制,不允许循环引用。 | |
@MapKey | 方法 | ||
@Options | 方法 | 映射语句的属性 | 这个注解提供访问交换和配置选项的 宽广范围, 它们通常在映射语句上作为 属性出现。 而不是将每条语句注解变复 杂,Options 注解提供连贯清晰的方式 来访问它们。属性:useCache=true , flushCache=FlushCachePolicy.DEFAULT , resultSetType=FORWARD_ONLY , statementType=PREPARED , fetchSize=-1 , , timeout=-1 useGeneratedKeys=false , keyProperty=”id” , keyColumn=”” , resultSets=””。 理解 Java 注解是很 重要的,因为没有办法来指定“null” 作为值。因此,一旦你使用了 Options 注解,语句就受所有默认值的支配。要 注意什么样的默认值来避免不期望的 行为 |
@Insert、@Update、@Delete、@Select | 方法 | ||
@InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider | 方法 | 这些可选的 SQL 注解允许你指定一个 类名和一个方法在执行时来返回运行 允许创建动态 的 SQL。 基于执行的映射语句, MyBatis 会实例化这个类,然后执行由 provider 指定的方法. 该方法可以有选择地接受参数对象.(In MyBatis 3.4 or later, it’s allow multiple parameters) 属性: type,method。type 属性是类。method 属性是方法名。 |
# 四、代码生成器
# 1、方式一:mybatis-plus-generator
# 1)、引入依赖
<!--mybatis plus代码生成器相关依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
# 2)、配置
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
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.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.apache.logging.log4j.util.Strings;
import java.util.ArrayList;
/**
* 代码生成工具
*
* @author qixiaodong
*/
public class MybatisGenerator {
public static void main(String[] args) {
try {
//腾讯云
String dbUrl = "jdbc:mysql://10.10.208.24:3306/wxapp";
String dbDriverName = "com.mysql.cj.jdbc.Driver";
String dbUsername = "wisdom-guide";
String dbPassword = "fh-wisdom-guide@ASD!#";
DbType dbType = DbType.MYSQL;
String schemaName = null;//不需要时设置为 null 即可
//父包名称
String parentPackage = "com.businesscenter";
//模块名称
String moduleName = "guide";
//注释作者姓名
String authorName = "qixiaodong";
//需要自动生成的表
String[] tables = new String[]{
"t_common_product_info"
};
//输出路径
String outPutDir = System.getProperty("user.dir") + "/src/main/java";
System.out.println("输出路径: " + outPutDir);
//配置-逻辑删除字段-没有时设置为空即可
String logicDeleteField = "is_deleted";
//配置-乐观锁字段-没有时设置为空即可
String versionField = "version";
//配置-自动填充字段-没有时设置为空即可
ArrayList<TableFill> tableFills = new ArrayList<TableFill>() {{
add(new TableFill("create_time", FieldFill.INSERT));
add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
}};
AutoGenerator generator = new AutoGenerator();
/*
全局配置
*/
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(outPutDir);
globalConfig.setAuthor(authorName);
//是否打开资源管理器
globalConfig.setOpen(false);
//是否覆盖
globalConfig.setFileOverride(true);
//去掉Service的I前缀
globalConfig.setServiceName("%sService");
//设置id类型
globalConfig.setIdType(IdType.AUTO);
//实体属性 Swagger2 注解
globalConfig.setSwagger2(true);
// globalConfig.setDateType(DateType.ONLY_DATE);
globalConfig.setBaseResultMap(true);// XML ResultMap
globalConfig.setBaseColumnList(true);// XML columnList
globalConfig.setEnableCache(false);// 关闭XML 二级缓存
generator.setGlobalConfig(globalConfig);
/*
数据源配置
*/
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl(dbUrl);
dataSourceConfig.setSchemaName(schemaName);
dataSourceConfig.setDriverName(dbDriverName);
dataSourceConfig.setUsername(dbUsername);
dataSourceConfig.setPassword(dbPassword);
dataSourceConfig.setDbType(dbType);
generator.setDataSource(dataSourceConfig);
/*
包配置
*/
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent(parentPackage);
packageConfig.setModuleName(moduleName);
packageConfig.setController("controller");
packageConfig.setService("service");
packageConfig.setMapper("dao");
packageConfig.setXml("dao.mapper");
packageConfig.setEntity("model.entity");
generator.setPackageInfo(packageConfig);
/*
策略配置
*/
StrategyConfig strategyConfig = new StrategyConfig();
//是否生成实体时,生成字段注解
strategyConfig.setEntityTableFieldAnnotationEnable(true);
//是否生成常量字段
strategyConfig.setEntityColumnConstant(true);
//大写命名、字段符合大写字母数字下划线命名
strategyConfig.setCapitalMode(true);
strategyConfig.setInclude(tables);
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
// strategyConfig.setSuperEntityClass("自己父类实体,没有就不用设置");
strategyConfig.setEntityLombokModel(true);
strategyConfig.setRestControllerStyle(true);
// //驼峰转连字符 @RequestMapping("/managerUserActionHistory") -> @RequestMapping
// ("/manager-user-action-history")
// strategyConfig.setControllerMappingHyphenStyle(false);
//逻辑删除字段
strategyConfig.setLogicDeleteFieldName(logicDeleteField);
//乐观锁字段
if (!Strings.isBlank(versionField)) {
strategyConfig.setVersionFieldName(versionField);
}
/*
自动填充配置
*/
if (null != tableFills && !tableFills.isEmpty()) {
strategyConfig.setTableFillList(tableFills);
}
generator.setStrategy(strategyConfig);
generator.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
# 方式二:idea插件:MybatisX
详见【开发工具】