Spring-boot + Mybatis-plus 3.0-gamma 配置记录

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

pom.xml


	4.0.0
	com.baomidou
	mybatisplus-spring-boot
	1.0

	
		org.springframework.boot
		spring-boot-starter-parent
		2.0.3.RELEASE
	

	
		UTF-8
		UTF-8
		1.8
		1.8
		3.0-gamma
	

	
		
			org.springframework.boot
			spring-boot-starter-web
		
		
			org.springframework.boot
			spring-boot-starter-jetty
		

		
		
			com.baomidou
			mybatis-plus-boot-starter
			${mybatis-plus-boot-starter.version}
		
		

		
			org.projectlombok
			lombok
		
		
			org.apache.commons
			commons-dbcp2
		
		
			mysql
			mysql-connector-java
		
		
			org.apache.commons
			commons-lang3
		
		
			com.google.code.gson
			gson
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	

这个工程里依赖 mysql, dbcp2, gson, lombok

工程的结构

Spring-boot + Mybatis-plus 3.0-gamma 配置记录_第1张图片

  • config.mybatis mybatis的配置
  • config.mybatis.scanner 扩展mybatis的mapper scanner
  • controller 接口控制器
  • db 访问数据库相关的对象
  • db.entity 实体
  • db.mapper mybatis mapper
  • exception 例外处理器,用来处理controller里的例外
  • repository 仓库
  • resources.mapper mybatis mapper的xml文件
  • resources.META-INF 向spring注册组件

application.yml 配置文件

#app
server:
    port: 8080

#spring
spring:
  devtools:
    restart:
      enabled: false

  # H2 DATABASE CONFIG
  datasource:
    type: org.apache.commons.dbcp2.BasicDataSource
    dbcp2:
      driver-class-name: com.mysql.jdbc.Driver
      username: xxxx
      password: xxxxx
      jmx-name: xxxxxx
      url: jdbc:mysql://xxxxxx:3306/xxxxxxx?zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&characterEncoding=UTF-8

mybatis:
  mapperScanner:
    basePackage: com.baomidou.springboot.db.mapper*


#mybatis
mybatis-plus:
  mapper-locations: classpath:/mapper/*Mapper.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.baomidou.springboot.db.entity
  typeEnumsPackage: com.baomidou.springboot.db.entity.enums
  global-config:
    #刷新mapper 调试神器
    db-config:
      #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
      id-type: id_worker
      #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
      field-strategy: not_empty
      #驼峰下划线转换
      column-underline: true
      #数据库大写下划线转换
      #capital-mode: true
      #逻辑删除配置
      logic-delete-value: Y
      logic-not-delete-value: N
      db-type: mysql
    refresh: true
    sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector
      #自定义填充策略接口实现
      #meta-object-handler: com.baomidou.springboot.xxx
      #自定义SQL注入器
      #sql-injector: com.baomidou.springboot.xxx
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false

mybatis-plus部分的配置中:

  • mapper-locations 存放mapper xml文件的位置
  • typeAliasesPackage 数据对象的目录
  • typeEnumsPackage 数据对象中使用的enum定义
  • id-type id的生成策略
  • field-strategy 字段处理策略
  • column-underline 对代码中的驼峰字段映射到数据库字段的策略
  • logic-delete-value 逻辑删除的标记值
  • logic-not-delete-value 逻辑未删除的标记值
  • sql-injector SQL注入器。 这里为了处理逻辑删除操作,使用了LogicSqlInjector注入器
  • db-type 数据库类型。支持主流的数据库
  • map-underscore-to-camel-case 数据库字段与数据对象字段的映射策略

类的说明

MybatisPlusConfig

@Configuration
public class MybatisPlusConfig {
	... 一些配置, 例如,配置 datasource, sqlSessionFactory, transaction manager 等等
}

MybatisPlusAutoConfiguration

@Configuration
public class MybatisPlusAutoConfiguration {

    /**
     * 将自定义的converter注册到ConversionService中
     *
     * @return
     */
    @Bean
    public ConversionService conversionService() {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
        Set converters = new HashSet<>();
        converters.add(new String2SqlInjectorConverter());
        bean.setConverters(converters);
        bean.afterPropertiesSet();
        return bean.getObject();
    }

    /**
     *  将String 转成 ISqlInjector 实例
     */
    public class String2SqlInjectorConverter implements Converter {
        @Override
        public ISqlInjector convert(String s) {
            if(StringUtils.isEmpty(s))
                return null;

            try {
                Class clazz = ClassUtils.getClass(s);
                ISqlInjector sqlInjector = (ISqlInjector)clazz.newInstance();
                return sqlInjector;
            } catch (ClassNotFoundException|IllegalAccessException|InstantiationException e) {
                ReflectionUtils.rethrowRuntimeException(e);
            }

            return null;
        }
    }
}

向spring注册自定义的 converter,否则,application.yml中mybatis-plus.sql-injector配置会出错,提示没有相应的转换器 这个文件需要在resources/META-INF/spring.factories中注册:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.springboot.config.mybatis.MybatisPlusAutoConfiguration

这个文件里的程序会先于spring组建bean之前执行

SuperEntity

@Data
public abstract class SuperEntity extends Model {

    /**
     * 主键ID , 这里故意演示注解可以无
     */
    @TableId
    private String  uuid;


    @Override
    protected Serializable pkVal() {
        return this.uuid;
    }
}

数据库对象的父类, 在这个类中可以定义一些公共的字段。 mybatis-plus只把id作为主键字段,非id的字段,需要用@TableId来标注。pkVal() 是用来支持组合主键的,现在版本的mybatis-plus不支持组合主键

WxTokenObj

@Data
@TableName("tb_wx_token")
public class WxTokenObj extends SuperEntity{
    private String token;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date createTime;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date modifyTime;

    private String accountUuid;
    private String name;
    private String avatarPath;

    @TableLogic
    private String isDelete;
    private String unionId;
}

所有的数据对象都需要继承SuperEntity, 如果表名与数据对象名称不对应,需要用@TableName标注数据表名。mybatis-plus可以配置自动解析驼峰命名的字段为下划线分割的数据库字段名。如果数据库字段名与数据字段名不一致,需要用@TableField来标注 @TableLogic标注的字段,是用来做逻辑删除的字段,在调用删除方法的时候,会将这个字段改成删除状态,而不是真正删除数据

SuperMapper

/**
 * 演示 mapper 父类,注意这个类不要让 mp 扫描到!!
 */
public interface SuperMapper extends BaseMapper {

    // 这里可以放一些公共的方法
}

所有mapper的父接口,在这个接口中可以放一些公共方法。这个接口需要放到mapper目录外,不能让mybatis scanner 扫描到。

WxTokenMapper

public interface WxTokenMapper extends SuperMapper {

    @Select("select * from tb_wx_token where uuid=#{uuid}")
    WxTokenObj selectOneBySQL(String uuid);

    WxTokenObj selectOneByWrapper(@Param("ew") Wrapper wrapper);

    List selectAllTokens();
}

一个具体的mapper接口。 接口可以使用@Select等标注原生的sql,也可以使用mapper.xml 放置灵活的数据库操作。 自定义的mapper需要继承SuperMapper,也就是需要从BaseMapper中继承其定义的方法。BaseMapper中定义了很多类似JPA的自定义函数,方便开发使用:

public interface BaseMapper {

    /**
     * 

* 插入一条记录 *

* * @param entity 实体对象 */ Integer insert(T entity); /** *

* 根据 ID 删除 *

* * @param id 主键ID */ Integer deleteById(Serializable id); /** *

* 根据 columnMap 条件,删除记录 *

* * @param columnMap 表字段 map 对象 */ Integer deleteByMap(@Param("cm") Map columnMap); /** *

* 根据 entity 条件,删除记录 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ Integer delete(@Param("ew") Wrapper queryWrapper); /** *

* 删除(根据ID 批量删除) *

* * @param idList 主键ID列表(不能为 null 以及 empty) */ Integer deleteBatchIds(@Param("coll") Collection idList); /** *

* 根据 ID 修改 *

* * @param entity 实体对象 */ Integer updateById(@Param(Constants.META_OBJ_PREFIX) T entity); /** *

* 根据 whereEntity 条件,更新记录 *

* * @param entity 实体对象 (set 条件值,不能为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ Integer update(@Param(Constants.META_OBJ_PREFIX) T entity, @Param("ew") Wrapper updateWrapper); /** *

* 根据 ID 查询 *

* * @param id 主键ID */ T selectById(Serializable id); /** *

* 查询(根据ID 批量查询) *

* * @param idList 主键ID列表(不能为 null 以及 empty) */ List selectBatchIds(@Param("coll") Collection idList); /** *

* 查询(根据 columnMap 条件) *

* * @param columnMap 表字段 map 对象 */ List selectByMap(@Param("cm") Map columnMap); /** *

* 根据 entity 条件,查询一条记录 *

* * @param queryWrapper 实体对象 */ T selectOne(@Param("ew") Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询总记录数 *

* * @param queryWrapper 实体对象 */ Integer selectCount(@Param("ew") Wrapper queryWrapper); /** *

* 根据 entity 条件,查询全部记录 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ List selectList(@Param("ew") Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询全部记录 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ List> selectMaps(@Param("ew") Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询全部记录 * 注意: 只返回第一个字段的值 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ List selectObjs(@Param("ew") Wrapper queryWrapper); /** *

* 根据 entity 条件,查询全部记录(并翻页) *

* * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ IPage selectPage(IPage page, @Param("ew") Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询全部记录(并翻页) *

* * @param page 分页查询条件 * @param queryWrapper 实体对象封装操作类 */ IPage> selectMapsPage(IPage page, @Param("ew") Wrapper queryWrapper); }

在定义mapper是,推荐使用idea ide + mybatisx 插件,支持从mapper 源代码 到 mapper.xml 的双向跳转、代码自动生成等。

Spring-boot + Mybatis-plus 3.0-gamma 配置记录_第2张图片Spring-boot + Mybatis-plus 3.0-gamma 配置记录_第3张图片

IWxTokenRepository

仓库接口。因为仓库是用JDK proxy做增强,所有的仓库都需要单独定义接口(如果换成用cglib做增强,可以不用单独定义接口)

public interface IWxTokenRepository extends IService{
    WxTokenObj getToken(String uuid);
    WxTokenObj getToken(Wrapper wrapper);
}

仓库接口需要继承IService接口。IService接口中定义了很多方便使用的函数(主要是与mapper对应的函数):

public interface IService {

    /**
     * 

* 插入一条记录(选择字段,策略插入) *

* * @param entity 实体对象 * @return boolean */ boolean save(T entity); /** *

* 插入(批量),该方法不适合 Oracle *

* * @param entityList 实体对象集合 * @return boolean */ boolean saveBatch(Collection entityList); /** *

* 插入(批量) *

* * @param entityList 实体对象集合 * @param batchSize 插入批次数量 * @return boolean */ boolean saveBatch(Collection entityList, int batchSize); /** *

* 批量修改插入 *

* * @param entityList 实体对象集合 * @return boolean */ boolean saveOrUpdateBatch(Collection entityList); /** *

* 批量修改插入 *

* * @param entityList 实体对象集合 * @param batchSize * @return boolean */ boolean saveOrUpdateBatch(Collection entityList, int batchSize); /** *

* 根据 ID 删除 *

* * @param id 主键ID * @return boolean */ boolean removeById(Serializable id); /** *

* 根据 columnMap 条件,删除记录 *

* * @param columnMap 表字段 map 对象 * @return boolean */ boolean removeByMap(Map columnMap); /** *

* 根据 entity 条件,删除记录 *

* * @param wrapper 实体包装类 {@link Wrapper} * @return boolean */ boolean remove(Wrapper wrapper); /** *

* 删除(根据ID 批量删除) *

* * @param idList 主键ID列表 * @return boolean */ boolean removeByIds(Collection idList); /** *

* 根据 ID 选择修改 *

* * @param entity 实体对象 * @return boolean */ boolean updateById(T entity); /** *

* 根据 whereEntity 条件,更新记录 *

* * @param entity 实体对象 * @param wrapper 实体包装类 {@link Wrapper} * @return boolean */ boolean update(T entity, Wrapper wrapper); /** *

* 根据ID 批量更新 *

* * @param entityList 实体对象集合 * @return boolean */ boolean updateBatchById(Collection entityList); /** *

* 根据ID 批量更新 *

* * @param entityList 实体对象集合 * @param batchSize 更新批次数量 * @return boolean */ boolean updateBatchById(Collection entityList, int batchSize); /** *

* TableId 注解存在更新记录,否插入一条记录 *

* * @param entity 实体对象 * @return boolean */ boolean saveOrUpdate(T entity); /** *

* 根据 ID 查询 *

* * @param id 主键ID * @return T */ T getById(Serializable id); /** *

* 查询(根据ID 批量查询) *

* * @param idList 主键ID列表 * @return Collection */ Collection listByIds(Collection idList); /** *

* 查询(根据 columnMap 条件) *

* * @param columnMap 表字段 map 对象 * @return Collection */ Collection listByMap(Map columnMap); /** *

* 根据 Wrapper,查询一条记录 *

* * @param wrapper 实体对象 * @return T */ T getOne(Wrapper wrapper); /** *

* 根据 Wrapper,查询一条记录 *

* * @param wrapper {@link Wrapper} * @return */ Map getMap(Wrapper wrapper); /** *

* 根据 Wrapper,查询一条记录 *

* * @param wrapper {@link Wrapper} * @return Object */ Object getObj(Wrapper wrapper); /** *

* 根据 Wrapper 条件,查询总记录数 *

* * @param wrapper 实体对象 * @return int */ int count(Wrapper wrapper); /** *

* 查询列表 *

* * @param wrapper 实体包装类 {@link Wrapper} * @return */ List list(Wrapper wrapper); /** *

* 翻页查询 *

* * @param page 翻页对象 * @param wrapper 实体包装类 {@link Wrapper} * @return */ IPage page(IPage page, Wrapper wrapper); /** *

* 查询列表 *

* * @param wrapper {@link Wrapper} * @return */ List> listMaps(Wrapper wrapper); /** *

* 根据 Wrapper 条件,查询全部记录 *

* * @param wrapper 实体对象封装操作类(可以为 null) * @return List */ List listObjs(Wrapper wrapper); /** *

* 翻页查询 *

* * @param page 翻页对象 * @param wrapper {@link Wrapper} * @return */ IPage> pageMaps(IPage page, Wrapper wrapper); }

对应的,仓库实现类也就要继承ServiceImpl,来对IService中的函数提供实现。

WxTokenRepository

仓库实现类,对自定义的仓库接口中的函数提供实现:

@Component
public class WxTokenRepository extends ServiceImpl implements IWxTokenRepository{
    public WxTokenObj getToken(String uuid) {
        return baseMapper.selectOneBySQL(uuid);
    }

    public WxTokenObj getToken(Wrapper wrapper) {
        return baseMapper.selectOneByWrapper(wrapper);
    }
}

在继承了ServiceImpl后,可通过baseMapper方法 接口对应的mapper。如果还需要访问其他的mapper,可以用个@Autowired 注入其他mapper。

Controller

用户接口。这里演示如何用仓库访问数据读取数据。

查询1

@GetMapping(value = "/id/{id}")
    public String getById(@PathVariable String id){
        WxTokenObj wxTokenObj = wxTokenRepository.getToken(id);

        return gson.toJson(wxTokenObj);
    }

这个接口中,访问的是mapper中使用@Select标注的数据库访问方法。可以在mapper中使用这种方式简单的定义数据库访问操作,这样的mapper函数不需要生成mapper.xml中的配置就可以实现数据操作。

查询2

@GetMapping("/token/{token}")
    public String getByToken(@PathVariable String token){
        Wrapper wrapper = new QueryWrapper().lambda().eq(WxTokenObj::getToken, token);

        WxTokenObj wxTokenObj = wxTokenRepository.getToken(wrapper);

        return gson.toJson(wxTokenObj);
    }

这个接口中,访问的是自定义的数据库访问方法。这样的mapper函数需要生成mapper.xml配置才能正确的操作数据。 为了提供查询条件,mybatis-plus提供了QueryWrapper来生成where片段。

查询3

@GetMapping("/token2/{token}")
    public String getByToken2(@PathVariable String token){
        Wrapper wrapper = new QueryWrapper().lambda().eq(WxTokenObj::getToken, token);

        WxTokenObj wxTokenObj = wxTokenRepository.selectOne(wrapper);

        return gson.toJson(wxTokenObj);
    }

这个接口中,使用的是BaseMapper中提供的数据库访问函数。这样的函数可以直接访问,不需要自己定义,也不需要在mapper.xml中配置。这种体验与JPA类似。

删除

    @DeleteMapping("/id/{id}")
    public String deleteById(@PathVariable String id) {
        boolean success = wxTokenRepository.deleteById(id);

        return gson.toJson(success);
    }

这个接口使用BaseMapper中提供的删除函数来根据ID删除数据。配置了逻辑删除后,这个删除操作只是修改了删除标记,并没有真正的删除数据。

mapper xml说明





	
	
		
		
		
		
		
		
		
		
		
		
	

	
	
	

这里面的select操作的where条件,使用的是传入的Wrapper参数中的sqlSegment来实现的。Wrapper可以通过流式函数风格,以类jpa的方式定义where 条件。 mybatis-plus提供了几种Wrapper实现:

Spring-boot + Mybatis-plus 3.0-gamma 配置记录_第4张图片

这其中有专用于查询的QueryWrapper, 专用于更新操作的UpdateWrapper。 如果需要按条件删除数据,可以用QueryWrapper传入条件。 当然,也可用根据 columnMap 条件进行查询、删除操作。 有关Wrapper的更多使用案例,可以参考mybatis-plus core里的WrapperTest.java:

private void logSqlSegment(String explain, ISqlSegment sqlSegment) {
        System.out.println(String.format(" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓", explain));
        System.out.println(sqlSegment.getSqlSegment());
    }

    private  void logParams(QueryWrapper wrapper) {
        wrapper.getParamNameValuePairs().forEach((k, v) ->
            System.out.println("key: '" + k + "'\t\tvalue: '" + v + "'"));
    }

    @Test
    public void test() {
        Wrapper wrapper = new QueryWrapper().lambda().eq(User::getName, 123)
            .or(c -> c.eq(User::getRoleId, 1).eq(User::getId, 2))
            .eq(User::getId, 1);
        log(wrapper.getSqlSegment());

    }

    @Test
    public void test1() {
        QueryWrapper ew = new QueryWrapper()
            .eq("xxx", 123)
            .and(i -> i.eq("andx", 65444).le("ande", 66666))
            .ne("xxx", 222);
        log(ew.getSqlSegment());
        ew.getParamNameValuePairs().forEach((k, v) -> System.out.println("key = " + k + " ; value = " + v));
    }
    
============================================
xxx = #{ew.paramNameValuePairs.MPGENVAL1} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL2} AND ande <= #{ew.paramNameValuePairs.MPGENVAL3} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL4}
key = MPGENVAL3 ; value = 66666
key = MPGENVAL2 ; value = 65444
key = MPGENVAL1 ; value = 123
key = MPGENVAL4 ; value = 222
============================================	


    @Test
    public void test2() {
        UpdateWrapper ew = new UpdateWrapper()
            .set("name", "三毛").set("id", 1)
            .eq("xxx", 123)
            .and(i -> i.eq("andx", 65444).le("ande", 66666))
            .ne("xxx", 222);
        log(ew.getSqlSet());
        log(ew.getSqlSegment());
    }
    
============================================
name=#{ew.paramNameValuePairs.MPGENVAL1},id=#{ew.paramNameValuePairs.MPGENVAL2}
xxx = #{ew.paramNameValuePairs.MPGENVAL3} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL4} AND ande <= #{ew.paramNameValuePairs.MPGENVAL5} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL6}
============================================

    @Test
    public void test3() {
        UpdateWrapper ew = new UpdateWrapper()
            .setSql("abc=1,def=2").eq("id", 1).ge("age", 3);
        log(ew.getSqlSet());
        log(ew.getSqlSegment());
    }
============================================
abc=1,def=2
id = #{ew.paramNameValuePairs.MPGENVAL1} AND age >= #{ew.paramNameValuePairs.MPGENVAL2}
============================================

    @Test
    public void testQueryWrapper() {
        logSqlSegment("去除第一个 or,以及自动拼接 and,以及手动拼接 or,以及去除最后的多个or", new QueryWrapper().or()
            .ge("age", 3).or().ge("age", 3).ge("age", 3).or().or().or().or());

        logSqlSegment("多个 or 相连接,去除多余的 or", new QueryWrapper()
            .ge("age", 3).or().or().or().ge("age", 3).or().or().ge("age", 3));

        logSqlSegment("嵌套,正常嵌套", new QueryWrapper()
            .nested(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,第一个套外的 and 自动消除", new QueryWrapper()
            .and(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,多层嵌套", new QueryWrapper()
            .and(i -> i.eq("id", 1).and(j -> j.eq("id", 1))));

        logSqlSegment("嵌套,第一个套外的 or 自动消除", new QueryWrapper()
            .or(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,套内外自动拼接 and", new QueryWrapper()
            .eq("id", 11).and(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,套内外手动拼接 or,去除套内第一个 or", new QueryWrapper()
            .eq("id", 11).or(i -> i.or().eq("id", 1)).or().eq("id", 1));

        logSqlSegment("多个 order by 和 group by 拼接,自动优化顺序,last方法拼接在最后", new QueryWrapper()
            .eq("id", 11)
            .last("limit 1")
            .orderByAsc("id", "name", "sex").orderByDesc("age", "txl")
            .groupBy("id", "name", "sex").groupBy("id", "name"));

        logSqlSegment("只存在 order by", new QueryWrapper()
            .orderByAsc("id", "name", "sex").orderByDesc("age", "txl"));

        logSqlSegment("只存在 group by", new QueryWrapper()
            .groupBy("id", "name", "sex").groupBy("id", "name"));
    }

============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(去除第一个 or,以及自动拼接 and,以及手动拼接 or,以及去除最后的多个or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} AND age >= #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(多个 or 相连接,去除多余的 or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} OR age >= #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,正常嵌套)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,第一个套外的 and 自动消除)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,多层嵌套)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) )
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,第一个套外的 or 自动消除)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,套内外自动拼接 and)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) AND id = #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,套内外手动拼接 or,去除套内第一个 or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} OR ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) OR id = #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(多个 order by 和 group by 拼接,自动优化顺序,last方法拼接在最后)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} GROUP BY id,name,sex,id,name ORDER BY id,name,sex ASC , age,txl DESC limit 1
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(只存在 order by)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 ORDER BY id,name,sex ASC , age,txl DESC
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(只存在 group by)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 GROUP BY id,name,sex,id,name
============================================

    @Test
    public void testCompare() {
        QueryWrapper queryWrapper = new QueryWrapper()
            .allEq(getMap()).allEq((k, v) -> true, getMap())
            .eq("id", 1).ne("id", 1)
            .or().gt("id", 1).ge("id", 1)
            .lt("id", 1).le("id", 1)
            .or().between("id", 1, 2).notBetween("id", 1, 3)
            .like("id", 1).notLike("id", 1)
            .or().likeLeft("id", 1).likeRight("id", 1);
        logSqlSegment("测试 Compare 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(测试 Compare 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
column1 = #{ew.paramNameValuePairs.MPGENVAL1} AND column0 = #{ew.paramNameValuePairs.MPGENVAL2} AND nullColumn IS NULL AND column1 = #{ew.paramNameValuePairs.MPGENVAL3} AND column0 = #{ew.paramNameValuePairs.MPGENVAL4} AND nullColumn IS NULL AND id = #{ew.paramNameValuePairs.MPGENVAL5} AND id <> #{ew.paramNameValuePairs.MPGENVAL6} OR id > #{ew.paramNameValuePairs.MPGENVAL7} AND id >= #{ew.paramNameValuePairs.MPGENVAL8} AND id < #{ew.paramNameValuePairs.MPGENVAL9} AND id <= #{ew.paramNameValuePairs.MPGENVAL10} OR id BETWEEN #{ew.paramNameValuePairs.MPGENVAL11} AND #{ew.paramNameValuePairs.MPGENVAL12} AND id NOT BETWEEN #{ew.paramNameValuePairs.MPGENVAL13} AND #{ew.paramNameValuePairs.MPGENVAL14} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL15} AND id NOT LIKE #{ew.paramNameValuePairs.MPGENVAL16} OR id LIKE #{ew.paramNameValuePairs.MPGENVAL17} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL18}
key: 'MPGENVAL3'		value: '1'
key: 'MPGENVAL2'		value: '0'
key: 'MPGENVAL1'		value: '1'
key: 'MPGENVAL18'		value: '1%'
key: 'MPGENVAL17'		value: '%1'
key: 'MPGENVAL16'		value: '%1%'
key: 'MPGENVAL15'		value: '%1%'
key: 'MPGENVAL14'		value: '3'
key: 'MPGENVAL13'		value: '1'
key: 'MPGENVAL9'		value: '1'
key: 'MPGENVAL12'		value: '2'
key: 'MPGENVAL8'		value: '1'
key: 'MPGENVAL11'		value: '1'
key: 'MPGENVAL7'		value: '1'
key: 'MPGENVAL10'		value: '1'
key: 'MPGENVAL6'		value: '1'
key: 'MPGENVAL5'		value: '1'
key: 'MPGENVAL4'		value: '0'
============================================

    @Test
    public void testFunc() {
        QueryWrapper queryWrapper = new QueryWrapper()//todo in 方法是不是各个加个后缀好点
            .isNull("nullColumn").or().isNotNull("notNullColumn")
            .orderByAsc("id").orderByDesc("name")
            .groupBy("id", "name").groupBy("id2", "name2")
            .in("inColl", getList()).or().notIn("notInColl", getList())
            .in("inArray").notIn("notInArray", 1, 2, 3)
            .inSql("inSql", "1,2,3,4,5").notInSql("inSql", "1,2,3,4,5")
            .having("sum(age) > {0}", 1).having("id is not null");
        logSqlSegment("测试 Func 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(测试 Func 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
nullColumn IS NULL OR notNullColumn IS NOT NULL AND inColl IN (#{ew.paramNameValuePairs.MPGENVAL1},#{ew.paramNameValuePairs.MPGENVAL2}) OR notInColl NOT IN (#{ew.paramNameValuePairs.MPGENVAL3},#{ew.paramNameValuePairs.MPGENVAL4}) AND notInArray NOT IN (#{ew.paramNameValuePairs.MPGENVAL5},#{ew.paramNameValuePairs.MPGENVAL6},#{ew.paramNameValuePairs.MPGENVAL7}) AND inSql IN (1,2,3,4,5) AND inSql NOT IN (1,2,3,4,5) GROUP BY id,name,id2,name2 HAVING sum(age) > #{ew.paramNameValuePairs.MPGENVAL8} AND id is not null ORDER BY id ASC , name DESC
key: 'MPGENVAL3'		value: '0'
key: 'MPGENVAL2'		value: '1'
key: 'MPGENVAL1'		value: '0'
key: 'MPGENVAL8'		value: '1'
key: 'MPGENVAL7'		value: '3'
key: 'MPGENVAL6'		value: '2'
key: 'MPGENVAL5'		value: '1'
key: 'MPGENVAL4'		value: '1'
============================================


    @Test
    public void testJoin() {
        QueryWrapper queryWrapper = new QueryWrapper()
            .last("limit 1").or()
            .apply("date_format(column,'%Y-%m-%d') = '2008-08-08'")
            .apply("date_format(column,'%Y-%m-%d') = {0}", LocalDate.now())
            .or().exists("select id from table where age = 1")
            .or().notExists("select id from table where age = 1");
        logSqlSegment("测试 Join 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(测试 Join 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
date_format(column,'%Y-%m-%d') = '2008-08-08' AND date_format(column,'%Y-%m-%d') = #{ew.paramNameValuePairs.MPGENVAL1} OR EXISTS (select id from table where age = 1) OR NOT EXISTS (select id from table where age = 1) limit 1
key: 'MPGENVAL1'		value: '2018-08-01'
============================================

    @Test
    public void testNested() {
        QueryWrapper queryWrapper = new QueryWrapper()
            .and(i -> i.eq("id", 1).nested(j -> j.ne("id", 2)))
            .or(i -> i.eq("id", 1).and(j -> j.ne("id", 2)))
            .nested(i -> i.eq("id", 1).or(j -> j.ne("id", 2)));
        logSqlSegment("测试 Nested 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
 
 ============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(测试 Nested 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL2} ) ) OR ( id = #{ew.paramNameValuePairs.MPGENVAL3} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL4} ) ) AND ( id = #{ew.paramNameValuePairs.MPGENVAL5} OR ( id <> #{ew.paramNameValuePairs.MPGENVAL6} ) )
key: 'MPGENVAL3'		value: '1'
key: 'MPGENVAL2'		value: '2'
key: 'MPGENVAL1'		value: '1'
key: 'MPGENVAL6'		value: '2'
key: 'MPGENVAL5'		value: '1'
key: 'MPGENVAL4'		value: '2'
============================================

    @Test
    public void testPluralLambda() {
        TableInfoHelper.initTableInfo(null, User.class);
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getName,"sss");
        queryWrapper.lambda().eq(User::getName,"sss2");
        logSqlSegment("测试 PluralLambda", queryWrapper);
        logParams(queryWrapper);
    }

    private List getList() {
        List list = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            list.add(i);
        }
        return list;
    }

    private Map getMap() {
        Map map = new HashMap<>();
        for (int i = 0; i < 2; i++) {
            map.put("column" + i, i);
        }
        map.put("nullColumn", null);
        return map;
    }

转载于:https://my.oschina.net/ez8life/blog/1922114

你可能感兴趣的:(java,数据库,python)