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

    }
}

# 二、注解关联查询

  1. School 实体类

    @Data
    public class School {
    	private Integer id;
    	private String name;
    	private List<Student> students;
    }
    
  2. Student 实体类

    @Data
    public class Student {
    	private Integer id;
    	private String name;
    	private String NO;
    	private School school;
    }
    
  3. 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);
    }
    
  4. 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

详见【开发工具】

Last Updated: 12/15/2023, 8:18:50 AM