SpringBoot2.x学习-MyBatis-Plus使用

示例环境:JDK1.8+Spring Boot2+MP+Druid+Oracle

1.8
1.18.6
1.1.13
5.0.10
3.3.1.tmp

文章目录

  • 一、插入主键策略
    • 1.主键类型为NUMBER
    • 2.主键类型为VARCHAR2
  • 二、@TableField
  • 三、更新操作
  • 四、分页查询
    • 1.配置分页插件
    • 2.自定义分页查询
  • 五、常用配置
  • 六、条件构造器
    • 1.allEq
    • 2.likeLeft
    • 3.in
    • 4.or
    • 5.exists
    • 6.select
  • 七、代码生成器
  • 八、ActiveRecord模式
  • 九、自动填充
    • 1.指定要自动填充的字段
    • 2.自定义填充器实现类
    • 3.注入Spring BOOT
  • 十、MybatisX
    • 1.Java代码与XML之间跳转
    • 2.Mapper方法自动生成XML

一、插入主键策略

1.主键类型为NUMBER

当表的主键类型为NUMBER时,插入主键采用Oracle序列方式
创建序列如下:

create sequence emp_sequence
minvalue 1
maxvalue 999999999999999999999999
start with 1
increment by 1;

在实体类中采用@KeySequence注解,并且在注解中指定序列名称,主键生成策略必须使用INPUT,如下:

@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence(value = "EMP_SEQUENCE",clazz = Long.class)
public class Emp {
     
    /**
     * 员工编号
     */
    @TableId(type = IdType.INPUT)
    private Long empno;
    /**
     * 员工姓名
     */
    @TableField("ename")
    private String empname;
    ........

同时还要 配置Oracle序列生成器

@Configuration
public class MybatisPlusConfig {
     

    @Bean
    public IdentifierGenerator idGenerator() {
     
        //自定义ID生成器
        return new CustomIdGenerator();
    }

    @Bean
    public IKeyGenerator keyGenerator(){
     
    //
        return new OracleKeyGenerator();
    }
}

这样在保存的时候,会先执行查询序列:
SELECT EMP_SEQUENCE.NEXTVAL FROM DUAL
然后再执行插入:INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )

2.主键类型为VARCHAR2

(1)配置全局ID生成策略
mybatis-plus.global-config.db-config.id-type=assign_uuid
(2) 实现自定义的ID生成器

@Slf4j
public class CustomIdGenerator implements IdentifierGenerator {
     

    @Override
    public Number nextId(Object entity) {
     
        return null;
    }

    @Override
    public String nextUUID(Object entity) {
     
        log.info("调用了UUID 生成!");
        return PlatStringUtil.randomUUID();
    }

}

(3) MybatisPlusConfig中 注册UUID生成器

 /**
     * 注册UUID生成器
     * @return
     */
    @Bean
    public IdentifierGenerator idGenerator() {
     
        //自定义ID生成器
        return new CustomIdGenerator();
    }

二、@TableField

这个注解在MP中是字段注解(非主键) 主要用途如下

  • 对象中的属性名和表字段名称不一致的时候(非驼峰)使用
  • 对象中的属性名称在表中不存在的时候使用
  • 某个字段查询的时候不返回值

示例如下:假设表EMP中字段有:员工姓名-ENAME ;薪水-SAL等字段,对象Emp中有属性empAddress 不在表中,则可以使用该注解如下

@Setter
@Getter
@ToString
@TableName("EMP")
public class Emp {
     
    /**
     * 员工编号
     */
    @TableId(type = IdType.ASSIGN_ID)
    private Long empno;
    /**
     * 员工姓名
     */
    @TableField("ename")
    private String empname;
    /**
     * 工作岗位
     */
    private String job;
    /**
     * 领导者编号
     */
    private int mgr;
    /**
     * 入职时间
     * 自定义输出格式
     */
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Timestamp hiredate;
    /**
     * 薪水  查询的时候不返回这个字段值
     */
    @TableField(select = false)
    private double sal;
    /**
     * 奖金
     */
    private double comm;
    /**
     * 所在部门编号
     */
    private int deptno;
    /**
     * 地址 不是数据库表字段
     */
    @TableField(exist = false)
    private String empAddress;

}

三、更新操作

一种是根据ID更新,还有一种是根据条件更新

   @RequestMapping("/updateByid")
    public String updateById(HttpServletRequest request){
     
        Emp emp = new Emp();
        emp.setEmpno(1L);
        emp.setHiredate(new Timestamp(System.currentTimeMillis()));
        emp.setSal(100);
        boolean result = empService.updateById(emp);
        logger.info("the exec result is {}",result);
        return "success";
    }

根据ID更新的语句是: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE empno=?


    @RequestMapping("/update")
    public String update(HttpServletRequest request){
     
        Emp emp = new Emp();
        emp.setHiredate(new Timestamp(System.currentTimeMillis()));
        emp.setSal(100);

        /**
         * 根据条件更新需要使用 条件构造器  QueryWrapper或者 UpdateWrapper
         */
        QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ename","程序员");
        queryWrapper.ge("sal",200);

        boolean result = empService.update(emp, queryWrapper);

        logger.info("the exec result is {}",result+"");
        return "success";
    }

根据条件更新语句为: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE (ename = ? AND sal >= ?)
根据条件更新也可以写成这样

         /**
         * 或者
         */
        UpdateWrapper<Emp> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("sal", 9999).set("hiredate", new Timestamp(System.currentTimeMillis()))
                .eq("ename", "fire2").ge("sal", 200);

        boolean result = empService.update(updateWrapper);

四、分页查询

1.配置分页插件

/**
 * MybatisPlus配置
 *
 * @author David Lin
 * @version: 1.0
 * @date 2020-02-22 23:15
 */
@Configuration
public class MybatisPlusConfig {
     
    
    @Bean
    public PaginationInterceptor paginationInterceptor(){
     
        return new PaginationInterceptor();
    }

2.自定义分页查询

mapper接口定义如下:

/**
     * 分页查询
     * @param page
     * @param queryWrapper
     * @return
     */
    IPage<Emp> selectPageInfo(Page<Emp> page,  @Param(Constants.WRAPPER) QueryWrapper queryWrapper);

自定义SQL的同时也使用Wrapper,方法参数必须添加@Param(Constants.WRAPPER) ,这样在XML中的${ ew.customSqlSegment}才会生效,更多用法看官方DEMO
XML定义如下:

<select id="selectPageInfo" resultType="Emp">
          select  e.*,d.dname  from emp e
           left join dept d on e.deptno = d.deptno
           ${
     ew.customSqlSegment}
 </select>

service定义如下:

public IPage<Emp> selectPage() {
     
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("d.deptno","20");

        Page<Emp> page = new Page<>(1,5);
        IPage<Emp> resultPage = empMapper.selectPageInfo(page, queryWrapper);

        return resultPage;
    }

最终分页执行的Sql如下:

SELECT *
  FROM (SELECT TMP.*, ROWNUM ROW_ID
          FROM (select e.*, d.dname
                  from emp e
                  left join dept d
                    on e.deptno = d.deptno
                 WHERE (d.deptno = ?)) TMP
         WHERE ROWNUM <= ?)
 WHERE ROW_ID > ?

五、常用配置

application.properties 配置如下

#指定MyBatis-Plus配置文件  复杂配置可以单独写在 一个xml
#全局配置不能和  mybatis-plus.configuration.xx.xx 配置共同使用
mybatis-plus.config-location=classpath:mybatis/mybatis-config.xml
#如果接口方法对应的XML放在resources目录下 需要告诉MyBatis-Plus哪里扫描Mapper
mybatis-plus.mapper-locations=classpath:mapper/**/*.xml
#定义别名扫描的包
mybatis-plus.type-aliases-package=com.soft.fire.platform.*.model
#全局ID生成策略  设置后 可以省略实体对象中的  @TableId(type = IdType.INPUT) 配置
mybatis-plus.global-config.db-config.id-type=assign_uuid
#是否开启自动驼峰命名规则(camel case)映射
#此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中默认开启
#mybatis-plus.configuration.map-underscore-to-camel-case=true
#MP 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
#mybatis-plus.configuration.cache-enabled=true

六、条件构造器

1.allEq

全部eq(或个别isNull)

 @RequestMapping("list")
    public List<Emp> listEmp(HttpServletRequest request) {
     
        Map<String, Object> params = new HashMap<>(8);
        params.put("deptno", 20);
        params.put("comm", null);
        QueryWrapper<Emp> queryWrapper = new QueryWrapper();
        queryWrapper.allEq(params);

        return empService.list(queryWrapper);
    }

执行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (comm IS NULL AND deptno = ?)

2.likeLeft

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
     queryWrapper.likeLeft("job","MAN");

   return empService.list(queryWrapper);

执行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (job LIKE ?)
参数为: %MAN

3.in

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.in("deptno",10,20);

执行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (deptno IN (?, ?))

4.or

QueryWrapper<Emp> queryWrapper = new QueryWrapper();  
 queryWrapper.eq("ename", "smith").or().eq("deptno", 10);

执行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (ename = ? OR deptno = ?)

5.exists

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
  queryWrapper.exists("select 1  from dept d  where d.deptno =deptno ");

执行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (EXISTS (select 1 from dept d where d.deptno = deptno))

6.select

设置查询字段

 QueryWrapper<Emp> queryWrapper = new QueryWrapper();
 queryWrapper.select("empno,ename,job,deptno");

执行的Sql如下:
SELECT empno,ename,job,deptno FROM EMP

七、代码生成器

(1) 添加依赖

  <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${
     mybatis-plus.version}</version>
  </dependency>

(2)设置模板
这里底层采用Freemarke模板引擎生成代码,从GitHub下载这些模板如下:
SpringBoot2.x学习-MyBatis-Plus使用_第1张图片

(3)编写代码
这个代码来自MP官方的DEMO
稍加修改如下:

**
 * 代码生成器 示例
 * @since 202012/29
 */
public class CodeGeneratorWithTemplateTest {
     

    /**
     * 是否强制带上注解
     */
    private boolean enableTableFieldAnnotation = false;
    /**
     * 生成的注解带上IdType类型
     */
    private IdType tableIdType = null;
    /**
     * 是否去掉生成实体的属性名前缀
     */
    private String[] fieldPrefix = null;
    /**
     * 生成的Service 接口类名是否以I开头
     * 

默认是以I开头

*

user表 -> IUserService, UserServiceImpl

*/
private boolean serviceClassNameStartWithI = true; //运行这个方法 自动代码生成 @Test public void generateCode() { String packageName = "com.soft.fire.platform"; enableTableFieldAnnotation = true; tableIdType = null; generateByTables(packageName , "PLAT_SYSTEM_SYSUSER"); } private void generateByTables(String packageName, String... tableNames) { // 代码生成器 AutoGenerator autoGenerator = new AutoGenerator(); // 全局配置 GlobalConfig globalConfig = getGlobalConfig(); // 数据源配置 String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL"; DataSourceConfig dataSourceConfig = getDataSourceConfig(dbUrl); // 策略配置 StrategyConfig strategyConfig = getStrategyConfig(tableNames); //配置模板 TemplateConfig templateConfig = getTemplateConfig(); //包配置 PackageConfig packageConfig = getPackageConfig(packageName); // 自定义配置 InjectionConfig injectionConfig = getInjectionConfig(packageConfig); autoGenerator.setGlobalConfig(globalConfig); autoGenerator.setDataSource(dataSourceConfig); autoGenerator.setStrategy(strategyConfig); //配置自定义模板 autoGenerator.setTemplate(templateConfig); //配置自定义属性注入 autoGenerator.setCfg(injectionConfig); autoGenerator.setPackageInfo(packageConfig); //使用 Freemarker模版引擎 autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine()); autoGenerator.execute(); } /** * 包配置 * * @param packageName * @return */ private PackageConfig getPackageConfig(String packageName) { PackageConfig pc = new PackageConfig(); pc.setModuleName("system"); pc.setParent(packageName); pc.setController("controller"); pc.setEntity("model"); pc.setService("service"); pc.setMapper("mapper"); return pc; } /** * 自定义配置 * * @return */ private InjectionConfig getInjectionConfig(PackageConfig pc) { InjectionConfig injectionConfig = new InjectionConfig() { @Override public void initMap() { Map<String, Object> map = new HashMap<>(); map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp"); this.setMap(map); } }; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置优先于默认配置生效 focList.add(new FileOutConfig("/templates/code/mapper.xml.ftl") { @Override public String outputFile(TableInfo tableInfo) { // 自定义mapper xml输出目录 return System.getProperty("user.dir") + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getMapperName() + StringPool.DOT_XML; } }); injectionConfig.setFileOutConfigList(focList); return injectionConfig; } /** * 指定自定义模板路径 * * @return */ private TemplateConfig getTemplateConfig() { //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setEntity("templates/code/entity.java"); templateConfig.setController("templates/code/controller.java"); templateConfig.setMapper("templates/code/mapper.java"); //关闭默认的mapper xml生成 templateConfig.setXml(null); templateConfig.setService("templates/code/service.java"); templateConfig.setServiceImpl("templates/code/serviceImpl.java"); return templateConfig; } /** * 策略配置 * * @return */ private StrategyConfig getStrategyConfig(String... tableNames) { StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setCapitalMode(true); strategyConfig.setRestControllerStyle(true); //设置数据库表映射到实体的命名策略 strategyConfig.setNaming(NamingStrategy.underline_to_camel); //设置数据库表字段映射到实体的命名策略, 未指定按照 naming 执行 strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); //是否生成实体时,生成字段注解 strategyConfig.setEntityTableFieldAnnotationEnable(enableTableFieldAnnotation); //是否启用 Lombok strategyConfig.setEntityLombokModel(true); //表前缀,配置后 生成的的代码都会把前缀去掉 strategyConfig.setTablePrefix("PLAT_SYSTEM"); //test_id -> id, test_type -> type strategyConfig.setFieldPrefix(fieldPrefix); //修改替换成你需要的表名,多个表名传数组 strategyConfig.setInclude(tableNames); return strategyConfig; } /** * 数据源配置 * * @param dbUrl * @return */ private DataSourceConfig getDataSourceConfig(String dbUrl) { DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setDbType(DbType.ORACLE); dataSourceConfig.setUrl(dbUrl); dataSourceConfig.setUsername("scott"); dataSourceConfig.setPassword("tiger"); dataSourceConfig.setDriverName(Driver.class.getName()); return dataSourceConfig; } private GlobalConfig getGlobalConfig() { // 全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setActiveRecord(false); globalConfig.setAuthor("David"); //生成文件的输出目录 globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java"); //是否覆盖已有文件 globalConfig.setFileOverride(true); //开启 BaseResultMap globalConfig.setBaseResultMap(true); //开启 baseColumnList globalConfig.setBaseColumnList(true); //是否生成完成后打开资源管理器 globalConfig.setOpen(true); //日期类型的字段使用哪个类型,默认是 java8的 日期类型,此处改为 java.util.date globalConfig.setDateType(DateType.ONLY_DATE); //生成的Service 接口类名是否以I开头 if (!serviceClassNameStartWithI) { globalConfig.setServiceName("%sService"); } return globalConfig; }

八、ActiveRecord模式

实体对象继承Model就可以开启AR模式

@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence("EMP_SEQUENCE")
public class Emp extends Model<Emp> {
     
    /**
     * 员工编号
     */
    @TableId(type = IdType.INPUT)
    private Long empno;
 @RequestMapping("list")
    public List<Emp> listEmp(HttpServletRequest request) {
     

        QueryWrapper<Emp> queryWrapper = new QueryWrapper();
        queryWrapper.eq("deptno",20);
        queryWrapper.select("empno,ename,job,deptno");

        Emp emp = new Emp();

        return emp.selectList(queryWrapper);
    }

执行的SQL如下:
SELECT empno,ename,job,deptno FROM EMP WHERE (deptno = ?)
由于底层也是调用Mapper的方法,所以EmpMapper不能删除掉

九、自动填充

当插入的时候自动填充CREATE_TIME 字段值,当更新的时候 自动填充UPDATE_TIME 字段值

1.指定要自动填充的字段

/**
     * 创建时间
     */
    @TableField(value = "CREATE_TIME" ,fill = FieldFill.INSERT,jdbcType = JdbcType.TIMESTAMP)
    private Timestamp createTime;
    /**
     * 更新时间
     */
    @TableField(value = "UPDATE_TIME",fill = FieldFill.UPDATE,jdbcType = JdbcType.TIMESTAMP)
    private Timestamp updateTime;

如果日期类型使用LocalDateTime ,如会报:Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 无效的列类型: 1111, ,日期类型改成 Timestamp类型

2.自定义填充器实现类

**
 * 自定义填充器
 *
 * @author David Lin
 * @version: 1.0
 * @date 2020-03-15 17:28
 */
public class MyMetaObjectHandler implements MetaObjectHandler {
     

    /**
     * 插入时填充
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
     
        Object createTime = getFieldValByName("createTime", metaObject);
        //如果为空 才进行填充
        if (null == createTime) {
     
            this.strictInsertFill(metaObject, "createTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
        }
    }

    /**
     * 更新时填充
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
     
        Object createTime = getFieldValByName("updateTime", metaObject);
        //如果为空 才进行填充
        if (null == createTime) {
     
            this.strictUpdateFill(metaObject, "updateTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
        }
    }
}

3.注入Spring BOOT

 */
@Configuration
public class MybatisPlusConfig {
     

    /**
     * 注入 自定义填充器
     * @return
     */
    @Bean
    public MetaObjectHandler metaObjectHandler(){
     
        return new MyMetaObjectHandler();
    }

这样在插入操作/更新操作的时候 如果没有显示设置时间值, 则会自动填充,插入的sql如下:
INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno, CREATE_TIME ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
自动填充只对操作实体对象时才有效果

十、MybatisX

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

1.Java代码与XML之间跳转

2.Mapper方法自动生成XML

具体使用参考官网

你可能感兴趣的:(Spring,Boot,Mybatis,spring,boot,mybatis)