Code Monkey home page Code Monkey logo

mybatis-mp's Introduction

喜欢的朋友加入QQ群:121908790 ,群里不仅可以提mybatis-mp框架问题,还可以帮你解决后端的各种问题!

另外,喜欢的朋友,帮忙关注 和 star(点点小爱心)!官网文档:http://mybatis-mp.cn !!!

特别申明:禁止在非法项目中使用,否则后果自负!

Maven Apache 2 Gitee star

与众不同的 几大亮点:

1:mybatis-mp - 亮点一:可自定义动态默认值

2:mybatis-mp - 亮点二:支持不同数据库ID自增配置

3:mybatis-mp - 亮点三:逻辑删除,可自动填充删除时间

4:mybatis-mp - 亮点四:可自定义sql(sql模板)

5:mybatis-mp - 亮点五:mapWithKey(把查询转成一个 map)

6:mybatis-mp - 亮点 六:部分字段 新增 和 修改

7:mybatis-mp - 亮点七:枚举的良好支持

8:mybatis-mp - 亮点八:mybatis-xml returnType 的 ORM 映射

9:mybatis-mp - 亮点九:优雅的 XML和 @Select查询 自动分页

10:mybatis-mp - 亮点十:支持多层嵌套VO,自动映射以及自动select 所需列

特征

1、很轻量,非常轻量

轻量级封装mybatis。 其他框架都比较深度修改了mybatis源码。

2、高性能

对比其他mybatis框架,性能不差,接近最优。

3、灵活方便

中高度实现ORM,查询API零学习成本。

4、高可用

可应付90%的SQL需求。

5、可靠,安全

没有过于复杂的设计,但是api却很丰富,足够使用! 其他框架或多或少设计的过于复杂,反而容易出现各种问题。

5、优秀的分页和SQL优化能力

自动过滤多余的left join count查询 自动去除order by ,无效的left join,以及select部分替换成 select count() 或 select 1 后 在select count() 内置分页功能,超级牛逼!

QQ 群

群号: 121908790 ,邀请各位大神参与补充,绝对开源,大家都可以进行代码提交,审核通过会进行master分支。

springboot接入示例:

https://gitee.com/mybatis-mp/mybatis-mp-spring-boot-demo

快速开始

1. 基于spring-boot开发 (已引入spring、springboot 基本依赖,创建SpringApplication.run即可启动)

1.1 springboot2 maven 集成

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.mybatis-mp</groupId>
            <artifactId>mybatis-mp-spring-boot-parent</artifactId>
            <version>1.5.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>cn.mybatis-mp</groupId>
        <artifactId>mybatis-mp-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

1.2 springboot3 maven 集成

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.mybatis-mp</groupId>
            <artifactId>mybatis-mp-spring-boot-parent</artifactId>
            <version>1.5.0-spring-boot3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>cn.mybatis-mp</groupId>
        <artifactId>mybatis-mp-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

1.3 数据源 配置

配置spring boot配置文件

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass

或者 自己实例一个 DataSource 也可以

@Configuration(proxyBeanMethods = false)
public class DatasourceConfig {

    @Bean
    public DataSource getDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setName("test_db")
                .setType(EmbeddedDatabaseType.H2)
                .addScript("schema.sql")
                .build();
    }
}

其他配置(可不配置)

数据库命名规则配置(可不配置,在项目启动时配置)

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        MybatisMpConfig.setTableUnderline(true); //数据库表是否下划线规则 默认 true
        MybatisMpConfig.setColumnUnderline(true); ///数据库列是否下划线规则 默认 true
        SpringApplication.run(DemoApplication.class, args);
    }
}

数据库类型配置(可不配置,自动识别)

mybatis:
  configuration:
    databaseId: MYSQL

更多数据库支持,请查看类:db.sql.api.DbType

启动springboot即可,非常简单

一切都就绪,启动即可

1.添加此依赖,无需再添加mybatis依赖

2.包含 mybatis、mybatis-spring、 mybatis-spring-boot-starter 所有功能(支持原有mybatis的所有功能)

3.更多mybatis 配置参数,参考 https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/zh/index.html

4.参考示例:https://gitee.com/mybatis-mp/mybatis-mp/tree/master/mybatis-mp-spring-boot-demo

5.更多 mybatis 用法,参考: mybatis:https://mybatis.org/mybatis-3/zh/index.html

mybatis spring: https://mybatis.org/spring/zh/index.html

6.更多mybatis-mp 用法,参考作者编写的test case:(包含各种简单,复杂的CRUD操作案例)

https://gitee.com/mybatis-mp/mybatis-mp/tree/master/mybatis-mp-core/src/test/java/com/mybatis/mp/core/test/testCase

枚举类支持

1.要求

mybatis-mp 支持枚举作为参数进行进行CRUD;要求一下:

1.1.普通枚举,以名字存储(继承 Serializable)

1.2.非普通枚举,自定义属性存储(继承 EnumSupport)

public enum TestEnum implements EnumSupport<String> {

    X1("a1"),X2("a2");

    TestEnum(String code){
        this.code=code;
    }

    private final String code;

    @Override
    public String getCode() {
        return this.code;
    }

2.如何使用

2.1:新增

@Table
@Data
public class DefaultValueTest {

    @TableId
    private Integer id;

    @TableField(defaultValue = "{BLANK}")
    private String value1;

    @TableField(defaultValue = "1", updateDefaultValue = "2")
    private Integer value2;

    @TableField(defaultValue = "{NOW}")
    private LocalDateTime createTime;

    private TestEnum value3;
}
DefaultValueTestMapper mapper = session.getMapper(DefaultValueTestMapper.class);
DefaultValueTest defaultValueTest = new DefaultValueTest();
defaultValueTest.setValue3(TestEnum.X1);
mapper.save(defaultValueTest);

2.2:查询

QueryChain.of(mapper)
.in(DefaultValueTest::getValue1,Arrays.asList(TestEnum.X1,TestEnum.X2))
.list();

把它看成普通的参数即可,用户和 int string 都是一样的

注解

1. @Table

@Table
public class Student {

}

2. @TableId

@Table
public class Student {

    @TableId
    private Integer id;
}

@TableId 支持不同数据库自增或序列 以及 自定义SQL,默认是数据库自增

属性 默认 说明
value IdAutoType.AUTO

IdAutoType.AUTO: 数据库自增

IdAutoType.NONE: 开发者自己set值

IdAutoType.SQL: 结合@TableId.sql属性使用

IdAutoType.NONE: 开发者自己set值

IdAutoType.GENERATOR: 结合@TableId.generatorName属性,实现自定义自增;

dbType DbType.MYSQL 设置数据库的类型,@TableId 可配置多个,以支持多数据库切换
sql "" IdAutoType.SQL生效,实现数据库层自增;例如:SELECT LAST_INSERT_ID() 或 序列
generatorName ""

IdAutoType.GENERATOR生效;可取值:

IdentifierGeneratorType.DEFAULT(推荐,可替换成自己的实现):基于雪花算法

IdentifierGeneratorType.UUID: 基于UUID

IdentifierGeneratorType.mpNextId:基于雪花算法

自定义:只需要实现 IdentifierGenerator,并注册(项目启动时)ID生成器:IdentifierGeneratorFactory.register("名字",生成器的实例)

3. @TableField(可不配置) 数据库字段注解

@Table
public class Student {

    @TableField
    private LocalDateTime createTime;
}

@TableField 可以设置列名、typeHandler、jdbcType、是否查询、是否更新,保存时默认值,修改时默认值 等

默认值 可以是常量,动态值(结合 动态参数配置),例如{BLANK} 空值,支持string,list,map,set,数组,更多查看 动态参数配置 一项

4. @Ignore 可忽略字段(可用的实体类 VO 等字段上)

5. @ForeignKey 外键,配置后 JOIN时无需要再加 ON条件(自动添加)

@Data
@Table
public class Achievement {

    @TableId
    private Integer id;

    @ForeignKey(Student.class)
    private Integer studentId;

    private BigDecimal score;

    private Date createTime;
}

6. 非实体类结果映射

非实体类映射,可以select(非实体类.class),除@ResultField注解,其他均可自动帮你select;

QueryChain queryChain = QueryChain.of(sysUserMapper)
    .select(SysUserRoleAutoSelectVo.class)
    .from(SysUser.class)
    .join(SysUser.class, SysRole.class)
    .eq(SysUser::getId, 2)
    .returnType(SysUserRoleAutoSelectVo.class);
@Data
@ToString(callSuper = true)
@ResultEntity(Student.class)
public class StudentVo {

    private Integer id;

    private String name;

    //private LocalDateTime stCreateTime;

    @ResultEntityField(target = Student.class, property = "createTime")
    private LocalDateTime st2CreateTime;
}

@Data
@ToString(callSuper = true)
@ResultEntity(Student.class)
public class StudentAchievementVo extends StudentVo {

    @ResultEntityField(target = Achievement.class, property = "id")
    private Integer achievementId;

    @NestedResultEntity(target = Achievement.class)
    private Achievement achievement;

    @ResultField(value = "xx_score")
    private Integer score;

}

@ResultEntity

用于返回类 和 实体类关联 (只能配置一个,哪个返回列多,配哪个实体类)

@ResultEntityField

用于解决字段名字和实体类名字不一致的问题

@NestedResultEntity

用于内嵌类的映射 和 @ResultEntity 类似,可以映射一对一,一对多(List 属性)

@Data
@ResultEntity(SysRole.class)
public class OneToManyVo {

    private Integer id;

    private String name;

    private LocalDateTime createTime;

    @NestedResultEntity(target = SysUser.class)
    private List<SysUser> sysUserList;
}

@NestedResultEntityField

用于解决内嵌字段名字和实体类名字不一致的问题

@ResultField

返回列的映射(用于非实体类字段,可以设置列名、typeHandler、jdbcType) 返回可以平级 或者 1级 2级 多层映射

@Data
@ResultEntity(SysUser.class)
public class SysUserVo {

    private Integer id;
    
    private String userName;
    
    @ResultField("asNameOther")
    private String asName;

    @ResultField
    private String asName2;

}

2种用法:

    SysUserVo sysUser = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getUserName)
        .selectWithFun(SysUser::getUserName, c -> c.as("asNameOther"))
        .selectWithFun(SysUser::getUserName, c -> c.as(SysUserVo::getAsName2))
        .from(SysUser.class)
        .eq(SysUser::getId, 1)
        .returnType(SysUserVo.class)
        .get();

asName 字段 也可以 用 selectWithFun(SysUser::getUserName, c -> c.as(SysUserVo::getAsName))映射

7. @Version 乐观锁

乐观锁,只能注解在Integer类型字段上

在save(实体类)、save(Model)、update(实体类)、update(Model);

其他update地方操作 需要自己维护,进行version+1.

8. @TenantId 多租户ID

多租户是围绕实体进行自动设置租户ID,通常在 from(实体类),join(实体类),delete(实体类),update(实体类),

另外设置租户ID的获取方法,示例是返回租户ID为2:

TenantContext.registerTenantGetter(() -> {
    return 2;
});

如果想临时关闭

TenantContext.registerTenantGetter(() -> {
     if(临时关闭){
         //返回null 即可
        return null;    
     }
     return 2;
});

9. @LogicDelete 逻辑删除

支持字段类型:string,数字,布尔类型,时间类型(Date,LocalDateTime,Long,Integer)

逻辑删除 在deleteById,delete(实体类),delete(Where) 生效

查询时,将自动添加删除过滤条件(通常在 from(实体类),join(实体类),update(实体类)时,自动添加,delete 除上面3个方法, 其他不附加)

@Data
@Table
public class LogicDeleteTest {

    @TableId
    private Long id;

    private String name;

    private LocalDateTime deleteTime;

    @LogicDelete(beforeValue = "0", afterValue = "1", deleteTimeField = "deleteTime")
    private Byte deleted;
}

9.1 @LogicDelete 属性 beforeValue

未删除前的值,只能是固定值;时间类型的逻辑,可不填

9.2 @LogicDelete 属性 afterValue

删除后的值,可固定值或者动态值 例如 afterValue = "{NOW}",目前支持LocalDateTime,Date,Long,Integer,框架自动给值

9.3 @LogicDelete 属性 deleteTimeField

逻辑删除的时间字段,可不填,填了系统自动update 时 设置删除时间,deleteTimeField对应的字段类型 支持:LocalDateTime,Date,Long,Integer类型

9.4 逻辑删除全局开关(默认开)

MybatisMpConfig.setLogicDeleteSwitch(true);

9.5 逻辑删除-局部开关

try (LogicDeleteSwitch ignored = LogicDeleteSwitch.with(false)) {
    logicDeleteTestMapper.getById(1);
}

上面代码必须try (LogicDeleteSwitch ignored = LogicDeleteSwitch.with(false))

下面的更简单:

LogicDeleteTest logicDeleteTest = LogicDeleteUtil.execute(false, () -> {
    return logicDeleteTestMapper.getById(1);
});

10. @Fetch 加载额外数据,实现1对1,1对多

用于非实体类中,内嵌(非实体)类中 配置代码

@Data
@ResultEntity(SysUser.class)
public class FetchSysUserVo {

    private Integer id;

    private String userName;

    private String password;

    @Fetch(source = SysUser.class, property = "role_id", target = SysRole.class, targetProperty = "id")
    private SysRoleVo sysRole;

    private LocalDateTime create_time;

}
List<FetchSysUserVo> list = QueryChain.of(sysUserMapper)
                    .select(FetchSysUserVo.class)
                    .from(SysUser.class)
                    .eq(SysUser::getId,2)
                    .returnType(FetchSysUserVo.class)
                    .list();

[FetchSysUserVo(id=2, userName=test1, password=123456, sysRole=SysRoleVo(id=1, name=测试), create_time=2023-10-11T15:16:17)]

以上为1对1,如果需要实现 1对多,则 private SysRoleVo sysRole 改为 private List sysRoles 即可

SysRoleVo 也可以是 实体类 SysRole

@Fetch 属性说明

属性 默认值 说明
column ""

指定结果列,用于获取查询子表的值

column 和 property 二选一,column优先

source Void.class

指定列的对应的实体

选择property属性时填写

property ""

指定列的对应的实体的属性

column 和 property 二选一,column优先

storey 1

指定列的存储层级,默认为1

选择property属性时填写

target

指定加载实体类(也就是表的意思)

targetProperty

指定加载实体类的属性

指定属性对象列进行匹配查询

targetSelectProperty

指定返回实体类的属性

指定返回属性对象列

multiValueErrorIgnore false

1对1 多条时,发现多条是否忽略错误

orderBy

排序,格式为 属性 desc/asc,多个逗号分割

例如:id asc,userName desc

11. ModelEntityField 注解

Model类字段实体类注解 用于解决字段命名不一样问题

Model是类似实体类子集类,用于新增、修改中,可以减少类型转换的损失;save(model)/update(model) 再也不用转换了

@Data
@Table
public class IdTest {


    @TableId
    @TableId(dbType = DbType.SQL_SERVER, value = IdAutoType.AUTO)
    @TableId(dbType = DbType.PGSQL, value = IdAutoType.SQL, sql = "select nextval('seq1')")
    private Long id;

    private LocalDateTime createTime;

}
@Data
public class IdTestModel implements Model<IdTest> {

    @ModelEntityField("id")
    private Long xxid;

    private LocalDateTime createTime;
}

Model类会继承实体类的相关信息,例如上面 IdTestModel 自动匹配 IdTest的相关信息;

12.TypeHandler 注解

丰富Vo类 ,实现类似脱敏的功能;注意:实体类里不会生效

package com.mybatis.mp.core.test.typeHandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.*;

public class PhoneTypeHandler extends BaseTypeHandler<String> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        throw new RuntimeException("not support");
    }

    private String format(String value) throws SQLException {
        if (value != null) {
            //此处实现 手机号码脱敏
            return value.substring(0,3)+"****"+value.substring(7);
        }
        return "";
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return format(rs.getString(columnName));
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return format(rs.getString(columnIndex));
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return format(cs.getString(columnIndex));
    }
}
@Data
@ResultEntity(SysRole.class)
public class OneToManyTypeHandlerVo {

    private Integer id;

    @TypeHandler(PhoneTypeHandler.class)
    private String name;

    private LocalDateTime createTime;

    @TypeHandler(PhoneTypeHandler.class)
    @ResultField("kk2")
    private String kkName;

    @NestedResultEntity(target = SysUser.class)
    private List<SysUserTypeHandlerVo> sysUserList;
}

内嵌的类 也是可以的

@Data
public class SysUserTypeHandlerVo {

    private Integer id;

    @TypeHandler(PhoneTypeHandler.class)
    private String userName;

    @TypeHandler(PhoneTypeHandler.class)
    @NestedResultEntityField("password")
    private String pwd;

    @TypeHandler(PhoneTypeHandler.class)
    @ResultField("kk")
    private String kkName;

}

查询代码:关键在 returnType(OneToManyTypeHandlerVo.class) ;其他都是普通的查询代码

List<OneToManyTypeHandlerVo> list = QueryChain.of(sysRoleMapper)
                    .select(SysUser.class)
                    .selectWithFun(SysUser::getUserName, c -> c.as("kk"))
                    .selectWithFun(SysUser::getUserName, c -> c.as("kk2"))
                    .select(SysRole.class)
                    .from(SysRole.class)
                    .join(SysRole.class, SysUser.class, on -> on.eq(SysUser::getRole_id, SysRole::getId))
                    .returnType(OneToManyTypeHandlerVo.class)
                    .list();

XML 或 @Select注解的自动分页

1.1 要求:

  1. 方法返回是 Pager T不能省略
  1. 方法第一个参数 是 Pager 类型
  1. 方法上加上注解 @Paging

1.2 方法示例:

    @Paging
    Pager<SysRole> xmlPaging(Pager<SysRole> pager, @Param("id") Integer id, @Param("id2") Integer id2);

    @Paging
    Pager<SysRole> xmlPaging2(Pager<SysRole> pager);

    @Paging
    Pager<SysRole> xmlDynamicPaging(Pager<SysRole> pager, @Param("id") Integer id, @Param("id2") Integer id2, @Param("id3") Integer id3);

    @Paging
    @Select("select * from sys_role where id >=#{id} and id <=#{id2}")
    Pager<SysRole> annotationPaging(Pager<SysRole> pager, @Param("id") Integer id, @Param("id2") Integer id2);

1.3 xml 代码示例:

    <resultMap id="sysRole" type="com.mybatis.mp.core.test.DO.SysRole">
        <id column="id" property="id"/>
    </resultMap>

    <select id="xmlPaging" resultType="com.mybatis.mp.core.test.DO.SysRole">
        select *
        from sys_role
        where id >= #{id}
          and id &lt;= #{id2}
    </select>

    <select id="xmlPaging2" resultMap="sysRole">
        select *
        from sys_role
    </select>

    <select id="xmlDynamicPaging" resultType="com.mybatis.mp.core.test.DO.SysRole">
        select * from sys_role where id >=#{id} and id &lt;=#{id2}
        <if test="id3 != null">
            and id &lt;=#{id3}
        </if>
    </select>

1.4 (各种场景) 代码使用:

 Pager<SysRole> pager = sysRoleMapper.xmlPaging(Pager.of(1), 1, 1);

 Pager<SysRole> pager = sysRoleMapper.xmlPaging2(Pager.of(2));

 Pager<SysRole> pager = sysRoleMapper.xmlDynamicPaging(Pager.of(2),1,2,1);

 Pager<SysRole> pager  = sysRoleMapper.annotationPaging(Pager.of(2), 1, 2);
 

mybatis-mp mvc 架构理念

mybatis-mp 只设计到1层持久层,不过 mybatis-mp的理念,把持久层分2层,mapper层,dao层

mapper层、dao层的区别

区别在于 dao层是对mapper的扩展和限制

扩展:增加CRUD链式操作 增加 queryChain()、updateChain()、deleteChain()、insertChain()等方法,无需操作mapper接口;

限制:dao 只暴露 save(实体类|Model类)、update(实体类|Model类)、getById(ID)、deleteById(ID)、delete(实体类)等

mapper层一般不增加代码,如果有无法的实现的数据库操作,则需要在 mapper类上添加方法

如果不需要dao 可以直接使用mapper接口,进行CRUD;代码生成器有对应的关闭dao生成 和 service 注入mapper的开关

mybatis-mp service层的理解

service 不应该支持操作mapper,因为mapper包含丰富的api,这样不利于代码的维护性

service 不应该使用Query等作为参数,dao层也不应该;让service更专注于业务

链路式 CRUD 说明

为了增加CRUD一体式操作,增加了链路式类,分别为是:

QueryChain 包含 get(),list(),count(),exists(),map(),paging()等查询方法;

UpdateChain 包含 execute();

InsertChain 包含 execute();

DeleteChain 包含 execute();

如何简单创建 CRUD Chain类

Chain类都包含一个of(MybatisMapper mapper)静态方法,直接调用即可

例如:

List<SysUser> list=QueryChain.of(sysUserMapper).select(...).list();
int updateCnt=UpdateChain.of(sysUserMapper)
        .update(SysUser.class)
        .set(SysUser::getName,"hi")
        .eq(SysUser::getName,"haha")
        .execute();

dao 里自带 queryChain()、updateChain()等方法,可以直接获取链路式CRUD类

Dao 层 CRUD

继承 cn.mybatis.mp.core.mvc.Dao 接口 和 cn.mybatis.mp.core.mvc.impl.DaoImpl

save

update

getById

deleteById

deleteByIds

map 查询结果转map方法

链式类的获取方法

queryChain()

updateChain()

insertChain()

deleteChain() 使用方式

queryChain()
    .select(SysUser.class)
    .select(SysRole.class)
    .selectWithFun(SysUser::getName,c->c.concat("").as("copy_name"))
    .from(SysUser.class)
    .join(SysUser.class,SysRole.class)
    .eq(SysUser::getId,id)
    .returnType(SysUserVo.class)
    .get();

updateChain()
    .update(SysRole.class)
    .set(SysRole::getName,"test")
    .eq(SysRole::getId,1)
    .execute();

Service层 链路式 CRUD

使用代码生成器时,可在 ServiceImplConfig injectMapper 一项设置为true,即可生成 queryChain()等方法 和 Dao 层 链路式 CRUD 一样

建议在dao里操作,如果还是想操作,参考 其他层 链路式 CRUD

其他层 链路式 CRUD

QueryChain.of(sysUserMapper)
    .select(SysUser.class)
    .select(SysRole.class)
    .selectWithFun(SysUser::getName,c->c.concat("").as("copy_name"))
    .from(SysUser.class)
    .join(SysUser.class,SysRole.class)
    .eq(SysUser::getId,id)
    .returnType(SysUserVo.class)
    .get();

UpdateChain.of(sysUserMapper)
    .update(SysRole.class)
    .set(SysRole::getName,"test")
    .eq(SysRole::getId,1)
    .execute();     

使用前,请先查看此项(重要)

其他框架一般都是默认忽略null值参数的

但是这样可能比较容易导致bug出现,例如 删除 修改时,因为null 导致数据紊乱了

因此mybatis-mp,默认会检测 null值,并设有开关,让有需要忽略null,或者 空字符串等需要操作;如下使用:

Query重 select 可以select 实体类,vo类,属性等,推荐select vo类(也就是配置@ResultEntity的vo类)

SysUser sysUser = QueryChain.of(sysUserMapper)
    // forSearch包含忽略null 、空字符串、对字符串进行trim去空格    
    .forSearch()
    .select(SysUser::getId)
    .from(SysUser.class)
    .eq(SysUser::getUserName, null )
    .eq(SysUser::getUserName, "" )
    .eq(SysUser::getUserName," admin ")
    .returnType(SysUser.class)
    .get();

或者

SysUser sysUser = QueryChain.of(sysUserMapper)
    // 忽略 null 条件参数    
    .ignoreNullValueInCondition(true)
    // 忽略 空字符串 条件参数    
    .ignoreEmptyInCondition(true)
    //  对字符串进行trim 去空格操作    
    .trimStringInCondition(true)
    .select(SysUser::getId)
    .from(SysUser.class)
    .eq(SysUser::getUserName, null )
    .eq(SysUser::getUserName, "" )
    .eq(SysUser::getUserName," admin ")
    .returnType(SysUser.class)
    .get();

目前 CRUD类、Where类都支持以上方法

开始CRUD(CRUD教程)

Mapper 需要继承 MybatisMapper

public interface StudentMapper extends MybatisMapper<Student> {

}

1.Mapper方法

单个查询

getById(Serializable id) 根据ID查询实体

R get(Query query) 单个动态查询(可自定返回类型)

T get(Where where) 单个动态查询(只返回实体类)

列表查询

List list(Query query) 列表动态查询(可自定返回类型)

List list(Where where) 列表动态查询(只返回实体类)

count查询

count(Query query) 动态count查询

count(Where where) 动态count查询

exists查询

exists(Query query) 动态检测是否存在

exists(Where where) 动态检测是否存在

删除

deleteById(Serializable id) 根据ID删除

delete(T entity) 根据实体类删除

delete(Where where) 动态删除

delete(Delete delete) 动态删除

deleteByIds(Serializable... ids) 根据多个ID删除

deleteByIds(List ids) 根据多个ID删除

保存

save(T entity) 实体类保存

save(Model entity) 实体类部分保存

save(Insert insert) 动态插入(无法返回ID)

save(List list) 插入多个

saveBatch(List list, Getter... saveFields) 原生sql批量插入,需要指定列

 * 使用数据库原生方式批量插入
 * 一次最好在100条内
 * 
 * 会自动加入 主键 租户ID 逻辑删除列 乐观锁
 * 自动设置 默认值,不会忽略NULL值字段
List<DefaultValueTest> list= Arrays.asList(new DefaultValueTest(),new DefaultValueTest());
mapper.saveBatch(list, DefaultValueTest::getValue1, DefaultValueTest::getValue2, DefaultValueTest::getCreateTime);

修改

update(T entity) 实体类更新

update(T entity,F... fields) 实体类更新 + 强制字段更新

update(Model entity) 实体类部分更新

update(Model entity,F... fields) 实体类部分更新 + 强制字段更新

update(T entity,Where where) 实体类批量更新

update(Update update) 动态更新

mapWithKey 查询结果转成一个map的方法

Map<K, V> mapWithKey(Getter mapKey, Serializable... ids) key为mapKey,value当前实体类

Map<K, V> mapWithKey(Getter mapKey, BaseQuery query) key为mapKey,value为query指定的类型

Map<K, V> mapWithKey(Getter mapKey, List ids) key为mapKey,value当前实体类

Map<K, T> mapWithKey(Getter mapKey, Consumer consumer) key为mapKey,value当前实体类

Map<K, T> mapWithKey ... 还有其他的方法

分页查询

Pager paging(Query query, Pager pager) 分页查询(可自定返回类型)

Pager paging(Where where, Pager pager) 分页查询(只返回实体类类型)

牛逼的SQL优化

查询优化

去除无用left join

count优化

去除无用left join (自动判断是否有效)

去除无用的order by (自动判断是否有效)

优化union 查询 (优化 left join 和 order by,自动判断是否有效)

CRUD 操作

文档为演示 所以是调用 QueryChain.of(mapper),实际使用 queryChain()方法,在dao层

1.1 单个查询

Student stu=studentMapper.getById(1);

或

Student stu2=QueryChain.of(sysUserMapper)
    .eq(Student::getId,1)
    .get();

或

Student stu3=studentMapper.get(where->where.eq(Student::getId,1));

能用前者优先前者,后者为单个动态查询

1.2 选择部分列

Student stu3=QueryChain.of(sysUserMapper)
    .select(Student::getName,Student::getCreateTime)
    .from(Student.class)
    .eq(Student::getId,1)
    .get();

1.2 忽略部分列

Student stu3=QueryChain.of(sysUserMapper)
    .select(Student.class)
    .selectIgnore(Student::getCreateTime)
    .from(Student.class)
    .eq(Student::getId,1)
    .get();

注意:需要先select 再 selectIgnore

1.3 join 连表查询

1.3.1 基础用法

List<Achievement> achievementList=QueryChain.of(achievementMapper)
    .select(Achievement.class)
    .select(Student::getName)
    .from(Achievement.class)
    .join(Achievement.class,Student.class,on->on.eq(Achievement::getStudentId,Student::getId))
    .eq(Achievement::getId,1)
    .list();

支持各种连接:INNER JOIN ,LEFT JOIN 等等

1.3.2 省力用法

join 可结合 @ForeignKey使用 这样无需加 ON 条件

List<Achievement> achievementList2=QueryChain.of(achievementMapper)
    .select(Achievement.class)
    .select(Student::getName)
    .from(Achievement.class)
    .join(Achievement.class,Student.class)
    .eq(Achievement::getId,1)
    .list();

注意,假如自己拼接上了 on 条件 ,则 @ForeignKey 不生效,需要自己把条件书写完成!

1.3.3 相同表 join

List<SysUser> list=QueryChain.of(sysUserMapper)
    .select(SysUser.class)
    .from(SysUser.class)
    .join(JoinMode.INNER,SysUser.class,1,SysUser.class,2,on->on.eq(SysUser::getId,1,SysUser::getRole_id,2))
    .list();

注意 select(SysUser.class) 只是返回前面那个表的数据,如需返回后面那个表,则需要 结合注解@ResultField(别名)

然后 new Query().select(SysUser::getUserName,2,c->c.as("sub_name")),其中2 表示实体类SysUser对应的表实例的缓存层级

1.3.4 不同表join

List<SysUserAndRole> list=QueryChain.of(sysUserMapper)
    .select(SysUser.class)
    .select(SysRole.class)
    .from(SysUser.class)
    .join(SysUser.class,SysRole.class,on->on.eq(SysUser::getRole_id,SysRole::getId))
    .returnType(SysUserAndRole.class)
    .list();

SysUserAndRole 如何映射,请查看注解说明。

1.3.5 join 子表

SubQuery subQuery=SubQuery.create("sub")
    .select(SysRole.class)
    .from(SysRole.class)
    .eq(SysRole::getId,1);

List<SysUser> list=QueryChain.of(sysUserMapper)
    .select(SysUser.class)
    .from(SysUser.class)
    .join(JoinMode.INNER,SysUser.class,subQuery,on->on.eq(SysUser::getRole_id,subQuery.$(subQuery,SysRole::getId)))
    .list();

1.3.6 connect 更好的内联

这个方法的意义在于 拿到本身实例,从而更好的链式操作,例如下面的query.$(SysUser::getId):

这样可以使用query里方法,query.$(SysUser::getId) 就是从query里取得SysUser的id列,从而被 exists子查询引用。

List<SysUser> list=QueryChain.of(sysUserMapper)
    .select(SysUser::getId,SysUser::getUserName,SysUser::getRole_id)
    .from(SysUser.class)
    .connect(query->{
        query.exists(SubQuery.create()
            .select1()
            .from(SysUser.class)
            .eq(SysUser::getId,query.$(SysUser::getId))
            .isNotNull(SysUser::getPassword)
            .limit(1)
            );
    })
    .list();

1.4 删除

studentMapper.deleteById(1);

    或
        
DeleteChain.of(studentMapper).eq(Student::getId,1).execute();

    或

studentMapper.delete(where->where.eq(Student::getId,1))

能用前者优先前者,后者为单个动态删除

1.5 新增

Student student=new Student();
//student.setIdMethod(11);
student.setName("哈哈");
student.setExcellent(true);
student.setCreateTime(LocalDateTime.now());
studentMapper.save(student);

    或者

@Data
public class StudentDTO implements cn.mybatis.mp.db.Model<Student> {

    private Integer id;

    private String name;

    private LocalDateTime createTime;
}

StudentDTO studentDTO = new StudentDTO();
studentDTO.setName("DTO Insert");
studentDTO.setCreateTime(LocalDateTime.now());
studentMapper.save(studentDTO);

        或者
        
InsertChain.of(studentMapper)
    .insert(Student.class)
    .field(
        Student::getName,
        Student::getExcellent,
        Student::getCreateTime
    )
    .values(Arrays.asList("哈哈",true,LocalDateTime.now()))
    .execute();

能用前者优先前者,后者为动态插入(可多个)

1.6 更新

Student student=new Student();
student.setName("哈哈");
student.setExcellent(true);
student.setCreateTime(LocalDateTime.now());
studentMapper.update(student);

    或者

@Data
public class StudentDTO implements cn.mybatis.mp.db.Model<Student> {

    private Integer id;
    
    private String name;
    
    private LocalDateTime createTime;
}

StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(1);
studentDTO.setName("DTO Insert");
studentMapper.update(studentDTO);

或者

UpdateChain.of(studentMapper)
    .update(Student.class)
    .set(Student::getName,"嘿嘿")
    .eq(Student::getId,1)
    .execute();

或者

SysUser updateSysUser = new SysUser();
updateSysUser.setUserName("adminxx");
sysUserMapper.update(updateSysUser, where->where.eq(SysUser::getId,1));

能用前者优先前者,后者为动态更新

1.7 group by

List<Integer> counts=QueryChain.of(sysUserMapper)
        .select(SysUser::getId,c->c.count())
        .from(SysUser.class)
        .groupBy(SysUser::getRole_id)
        .returnType(Integer.TYPE)
        .list();

1.8 order by

SysUser sysUser=QueryChain.of(sysUserMapper)
        .select(SysUser::getId,SysUser::getUserName,SysUser::getRole_id)
        .from(SysUser.class)
        .orderBy(false,SysUser::getRole_id,SysUser::getId)
        .limit(1)
        .get();

1.9 group by having

Integer count=QueryChain.of(sysUserMapper)
        .selectWithFun(SysUser::getRole_id,FunctionInterface::count)
        .from(SysUser.class)
        .groupBy(SysUser::getRole_id)
        .having(SysUser::getRole_id,c->c.gt(0))
        .returnType(Integer.TYPE)
        .get();

2.0 条件

2.1 and or 相互切换

QueryChain.of(sysUserMapper)
    .select(SysUser::getId)
    .from(SysUser.class)
    .eq(SysUser::getId,2).or().eq(SysUser::getId,1)
    .returnType(Integer.TYPE)
    .get();

调用 and(),则后续操作默认都and操作

调用 or(),则后续操作默认都or操作

2.2 大于( gt() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .gt(SysUser::getId,2)
        .returnType(Integer.TYPE)
        .get();

2.3 大于等于( gte() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .gte(SysUser::getId,2)
        .limit(1)
        .returnType(Integer.TYPE)
        .get();

2.4 小于( lt() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .lt(SysUser::getId,2)
        .limit(1)
        .returnType(Integer.TYPE)
        .get();

2.5 小于等于( lte() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .lte(SysUser::getId,1)
        .limit(1)
        .returnType(Integer.TYPE)
        .get();

2.6 等于( eq() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .eq(SysUser::getId,2)
        .returnType(Integer.TYPE)
        .get();

2.7 不等于( ne() )

Integer id=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .ne(SysUser::getId,2)
        .returnType(Integer.TYPE)
        .count();

2.8 is NULL

SysUserMapper sysUserMapper=session.getMapper(SysUserMapper.class);
        SysUser sysUser=QueryChain.of(sysUserMapper)
        .select(SysUser::getId,SysUser::getPassword,SysUser::getRole_id)
        .from(SysUser.class)
        .isNull(SysUser::getPassword)
        .get();

2.9 is NOT NULL

SysUser sysUser=QueryChain.of(sysUserMapper)
        .select(SysUser::getId,SysUser::getPassword)
        .from(SysUser.class)
        .isNotNull(SysUser::getPassword)
        .eq(SysUser::getId,3)
        .returnType(Integer.TYPE)
        .get();

3.0 in

List<Integer> list=QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .in(SysUser::getId,1,2)
        .returnType(Integer.TYPE)
        .list();

3.1 like

SysUser sysUser=QueryChain.of(sysUserMapper)
        .select(SysUser::getId,SysUser::getPassword,SysUser::getRole_id)
        .from(SysUser.class)
        .like(SysUser::getUserName,"test1")
        .get();

3.2 left like

SysUser sysUser = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getPassword, SysUser::getRole_id)
        .from(SysUser.class)
        .like(SysUser::getUserName, "test1", LikeMode.LEFT)
        .get();

3.3 right like

Integer count = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, c -> c.count())
        .from(SysUser.class)
        .like(SysUser::getUserName, "test", LikeMode.RIGHT)
        .returnType(Integer.TYPE)
        .count();

3.4 not like

SysUser sysUser = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getPassword, SysUser::getRole_id)
        .from(SysUser.class)
        .notLike(SysUser::getUserName, "test1")
        .like(SysUser::getUserName, "test")
        .get();

3.5 not left like

SysUser sysUser = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getPassword, SysUser::getRole_id)
        .from(SysUser.class)
        .notLike(SysUser::getUserName, "est1", LikeMode.LEFT)
        .like(SysUser::getUserName, "test")
        .get();

3.6 not right like

SysUser sysUser = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getPassword, SysUser::getRole_id)
        .from(SysUser.class)
        .notLike(SysUser::getUserName, "test2", LikeMode.RIGHT)
        .like(SysUser::getUserName, "test")
        .get();

3.7 between

List<Integer> list = QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .between(SysUser::getId, 1, 2)
        .returnType(Integer.TYPE)
        .list();

3.8 not between

List<Integer> list = QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .between(SysUser::getId, 1, 3)
        .notBetween(SysUser::getId, 1, 2)
        .returnType(Integer.TYPE)
        .list();

3.9 exists

boolean exists = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getUserName, SysUser::getRole_id)
        .from(SysUser.class)
        .join(SysUser.class, SysRole.class)
        .like(SysUser::getUserName, "test")
        .exists();
或者

sysUserMapper.exists(where->where.like(SysUser::getUserName, "test"));

3.0 select 去重

List<Integer> roleIds = QueryChain.of(sysUserMapper)
        .selectDistinct()
        .select(SysUser::getRole_id)
        .from(SysUser.class)
        .returnType(Integer.TYPE)
        .list();

4.0 union 和 union all 查询

List<SysUser> list = QueryChain.of(sysUserMapper)
        .select(SysUser::getRole_id, SysUser::getId)
        .from(SysUser.class)
        .eq(SysUser::getId, 1)
        .union(Query.create()
        .select(SysUser::getRole_id, SysUser::getId)
        .from(SysUser.class)
        .lt(SysUser::getId, 3)
        .list();
List<SysUser> list = QueryChain.of(sysUserMapper)
        .select(SysUser::getRole_id, SysUser::getId)
        .from(SysUser.class)
        .eq(SysUser::getId, 1)
        .unionAll(Query.create()
        .select(SysUser::getRole_id, SysUser::getId)
        .from(SysUser.class)
        .lt(SysUser::getId, 3)
        .list();
        

4.1 为空

Integer id = QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .eq(SysUser::getId, 2)
        .empty(SysUser::getUserName)
        .returnType(Integer.TYPE)
        .get();

4.2 不为空

Integer id = QueryChain.of(sysUserMapper)
        .select(SysUser::getId)
        .from(SysUser.class)
        .eq(SysUser::getId, 2)
        .notEmpty(SysUser::getUserName)
        .returnType(Integer.TYPE)
        .get();

4.3 with 操作

SubQuery subQuery = SubQuery.create("sub")
    .select(SysRole.class)
    .from(SysRole.class)
    .eq(SysRole::getId, 1);

List<SysUser> list = QueryChain.of(sysUserMapper)
    .with(subQuery)
    .selectWithFun(subQuery, SysRole::getId, c -> c.as("xx"))
    .select(subQuery, "id")
    .select(SysUser.class)
    .from(SysUser.class)
    .from(subQuery)
    .eq(SysUser::getRole_id, subQuery.$(subQuery, SysRole::getId))
    .orderBy(subQuery, SysRole::getId)
    .list();

上面是一个利用with 构建的复杂SQL,核心在 .with(subQuery)

.from(subQuery)

把它看成一个子查询

函数操作

1.1 聚合函数(min,count,max,avg)

Integer count = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, c -> c.count())
        .from(SysUser.class)
        .like(SysUser::getUserName, "test", LikeMode.RIGHT)
        .returnType(Integer.TYPE)
        .count();
    selectWithFun(SysUser::getId,c->c.min())
    selectWithFun(SysUser::getId,c->c.max())
    selectWithFun(SysUser::getId,c->c.avg())

1.2 运算(加,减,乘,除)

selectWithFun(SysUser::getId,c->c.plus(1))
selectWithFun(SysUser::getId,c->c.subtract(1))
selectWithFun(SysUser::getId,c->c.multiply(1))
selectWithFun(SysUser::getId,c->c.divide(1))

1.3 其他函数

abspowconcatconcatAsroundif,
case when
比较函数
gte,gt,lt,lte 等等还很多

如何结合函数进行条件查询

Integer id = QueryChain.of(sysUserMapper)
    .select(SysUser::getId)
    .from(SysUser.class)
    .and(SysUser::getId, c -> c.concat("x1").eq("2x1"))
    .returnType(Integer.TYPE)
    .get();

SysUser sysUser = sysUserMapper.get(where -> where.and(SysUser::getId, c -> c.concat("x1").eq("2x1")));

复杂SQL示例

exists

boolean exists = QueryChain.of(sysUserMapper)
        .select(SysUser::getId, SysUser::getUserName, SysUser::getRole_id)
        .from(SysUser.class)
        .join(SysUser.class, SysRole.class)
        .like(SysUser::getUserName, "test")
        .exists();

in 一张表的数据

List<SysUser> list = QueryChain.of(sysUserMapper)
    .select(SysUser::getId, SysUser::getUserName, SysUser::getRole_id)
    .from(SysUser.class)
    .connect(queryChain -> {
        queryChain.in(SysUser::getId, new SubQuery()
                .select(SysUser::getId)
                .from(SysUser.class)
                .connect(subQuery -> {
                    subQuery.eq(SysUser::getId, queryChain.$().field(SysUser::getId));
                })
                .isNotNull(SysUser::getPassword)
                .limit(1)
        );
    })
    .list();

sql 如下:

SELECT 
    t.id, t.user_name, t.role_id
FROM
    t_sys_user t
WHERE
    t.id IN (SELECT 
            st.id
        FROM
            t_sys_user st
        WHERE
            st.id = t.id AND st.password IS NOT NULL
        LIMIT 1 OFFSET 0)

join 自己

Integer count = QueryChain.of(sysUserMapper)
        .selectWithFun(SysUser::getId, FunctionInterface::count)
        .from(SysUser.class)
        .join(JoinMode.INNER, SysUser.class, 1, SysUser.class, 2, on -> on.eq(SysUser::getId, 1, SysUser::getRole_id, 2))
        .returnType(Integer.TYPE)
        .get();

sql 如下:

SELECT 
    COUNT(t.id)
FROM
    t_sys_user t
        INNER JOIN
    t_sys_user t2 ON t.id = t2.role_id
LIMIT 1 OFFSET 0

join 子表

SubQuery subQuery = SubQuery.create("sub")
    .select(SysRole.class)
    .from(SysRole.class)
    .eq(SysRole::getId, 1);

List<SysUser> list = QueryChain.of(sysUserMapper)
    .selectWithFun(subQuery, SysRole::getId, c -> c.as("xx"))
    .select(SysUser.class)
    .from(SysUser.class)
    .join(JoinMode.INNER, SysUser.class, subQuery, on -> on.eq(SysUser::getRole_id, subQuery.$(subQuery, SysRole::getId)))
    .orderBy(subQuery, SysRole::getId)
    .list();

selectWithFun(subQuery, SysRole::getId, c -> c.as("xx")) 返回子表的角色ID并设置别名 sql 如下:

SELECT sub.id AS xx , sub.id , t.id , t.user_name , t.password , t.role_id , t.create_time 

FROM t_sys_user t 

INNER JOIN ( 
    SELECT st.id , st.name , st.create_time FROM sys_role st WHERE st.id = 1
) AS sub ON t.role_id = sub.id 

ORDER BY sub.id ASC

select 1 or select *

new Query().select1();
new Query().selectAll();

select count(1) or select count(*)

new Query().selectCount1();
new Query().selectCountAll();

批量操作

MybatisBatchUtil 内置了批量保存,批量修改,批量其他操作

batchSave 批量保存

/**
 * 批量插入(batchSize!=1时,无法获取主键)
 *
 * @param sqlSessionFactory mybatis SqlSessionFactory 通过spring 注解注入获取
 * @param mapperType        MybatisMapper 的 class
 * @param list              数据列表
 * @param batchSize         一次批量处理的条数(如需获取主键,请设置为1)
 * @param <M>               MybatisMapper
 * @param <T>               数据的类型
 * @return 影响的条数
 */
public static <M extends MybatisMapper, T> int batchSave(SqlSessionFactory sqlSessionFactory, Class<M> mapperType, List<T> list, int batchSize) {
    
}

batchUpdate 批量更新

/**
 * 批量更新
 *
 * @param sqlSessionFactory mybatis SqlSessionFactory 通过spring 注解注入获取
 * @param mapperType        MybatisMapper 的 class
 * @param list              数据列表
 * @param batchSize         一次批量处理的条数
 * @param <M>               MybatisMapper
 * @param <T>               数据的类型
 * @return 影响的条数
 */
public static <M extends MybatisMapper, T> int batchUpdate(SqlSessionFactory sqlSessionFactory, Class<M> mapperType, List<T> list, int batchSize) {
    
}

batch(核心)批量

/**
 * 批量操作
 *
 * @param sqlSessionFactory mybatis SqlSessionFactory 通过spring 注解注入获取
 * @param mapperType        MybatisMapper 的 class
 * @param list              数据列表
 * @param batchSize         一次批量处理的条数
 * @param batchFunction     操作方法
 * @param <M>               MybatisMapper
 * @param <T>               数据的类型
 * @return 影响的条数
 */
public static <M extends MybatisMapper, T> int batch(SqlSessionFactory sqlSessionFactory, Class<M> mapperType, List<T> list, int batchSize, MybatisBatchBiConsumer<SqlSession, M, T> batchFunction) {
    
}

如何使用批量操作

List<IdTest> list = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
    IdTest idTest = new IdTest();
    idTest.setCreateTime(LocalDateTime.now());
    list.add(idTest);
}

MybatisBatchUtil.batchSave(sqlSessionFactory, IdTestMapper.class, list);

sqlSessionFactory 是mybatis SqlSessionFactory 可通过spring 依赖注入获得

IdTestMapper 是mybatis Mapper 接口

如需获取主键,可设置batchSize=1,虽然性能可能下降,也比一般的循环save,update快!

动态参数配置(用于默认值,逻辑删除)

项目启动时设置

MybatisMpConfig.setDefaultValue("{NOW}", (type) -> {
    if (type == LocalDateTime.class) {
        return LocalDateTime.now();
    } else if (type == LocalDate.class) {
        return LocalDate.now();
    } else if (type == Date.class) {
        return new Date();
    } else if (type == Long.class) {
        return System.currentTimeMillis();
    } else if (type == Integer.class) {
        return (int) (System.currentTimeMillis() / 1000);
    }
    throw new RuntimeException("Inconsistent types");
});

内置动态参数配置

1.{BLANK} 空

可应用在 字符串,集合,数组上

2.{NOW} 当前时间

可应用在 LocalDateTime,LocalDate,Date,Long,Integer 字段上

如何创建条件,列,表等

1.使用命令工厂创建

Query,QueryChain等中有个方法,专门提供创建sql的工厂类:$()

可创建 条件,列,表,函数等等,例如

new Query().$().table(...) //创建表
new Query().$().field(...) //创建表的列
new Query().$().columnName(...) //获取数据库列名
new Query().$().gt(...) //创建大于的条件
结合实际使用,例如:
new Query(){{
    select(SysUser::getRole_id)
    .from(SysUser.class)
    .eq($().field(SysUser::getId),1)
    .gt($().table(SysUser.class).$("role_id"),2);
}};

甚至 Query 也包含了部分创建 列 表的功能

new Query(){{
    select($(SysUser::getId))
    .from($(SysUser.class))
    .eq($("id"),1);
}};

2.通过命令模板创建 (完美结合框架)

3个模板类:普通模板 CmdTemplate ,函数模板 FunTemplate(后续可继续调用框架的函数),条件模板 ConditionTemplate(用于 where 中)

QueryChain queryChain = QueryChain.of(sysUserMapper);
queryChain.selectWithFun(SysUser::getRole_id, c -> CmdTemplate.create("count({0})+{1}", c, "1"));
queryChain.from(SysUser.class);
queryChain.and(cs -> ConditionTemplate.create("{0}+{1}={2}", cs[0], cs[1], 2), SysUser::getId, SysUser::getId);
queryChain.returnType(String.class);
String str = queryChain.get();
QueryChain queryChain = QueryChain.of(sysUserMapper);
queryChain.selectWithFun(SysUser::getRole_id, c -> FunTemplate.create("count({0})",c).plus(1).concat(1,"2",3).length());
queryChain.from(SysUser.class);
queryChain.and(cs -> ConditionTemplate.create("{0}+{1}={2}", cs[0], cs[1], 2), SysUser::getId, SysUser::getId);
queryChain.returnType(String.class);
String str = queryChain.get();

支持那些数据库函数方法

数据库函数使用方法

Integer id = QueryChain.of(sysUserMapper)
    //方法 1    
    .selectWithFun(SysUser::getId, c -> c.sin().as("x_sin"))
    .from(SysUser.class)
    .eq(SysUser::getId, 2)
    //方法 2
    .and(queryChain -> {
        return queryChain.$(SysUser::getCreate_time, c -> c.date().eq("2023-12-10"));
    })
    //方法 3
    .and(queryChain -> {
        return Methods.date(queryChain.$(SysUser::getCreate_time)).eq("2023-12-10");
    })
    .empty(SysUser::getUserName)
    //方法 1     
    .orderBy(SysUser::getId, c -> c.plus(1))
    .returnType(Integer.TYPE)
    .get();

数据库函数说明

支持的函数 函数说明
count 查询数据总量
sum 返回指定字段值的和
avg 返回指定列的平均值
min 返回指定列的最小值
max 返回指定列的最大值
abs 返回绝对值
ceil 返回大于或等于 x 的最小整数(向上取整)
floor 返回小于或等于 x 的最大整数(向下取整)
rand 返回 0~1 的随机数
sign 返回 x 的符号,x 是负数、0、正数分别返回 -1、0、1
pi 返回圆周率
truncate 返回数值 x 保留到小数点后 y 位的值
round 返回离 x 最近的整数(四舍五入)
pow 返回 x 的 y 次方
power 返回 x 的 y 次方
sqrt 返回 x 的平方根
exp 返回 e 的 x 次方
mod 取模
log 返回自然对数(以 e 为底的对数)
log2 返回以 2 为底的对数
log10 返回以 10 为底的对数
radians 将角度转换为弧度
degrees 将弧度转换为角度
sin 求正弦值
asin 求反正弦值
cos 求余弦值
acos 求反余弦值
tan 求正切值
atan 求反正切值
cot 求余切值
charLength 返回字符串 s 的字符数
length 返回字符串 s 的长度
concat 字符串拼接
concatWs 字符串拼接,需要指定分割符
upper 转大写
lower 转小写
left 左截取
right 截取
lpad 从左字符串 补全,需要补全的字符和长度
rpad 从右字符串 补全,需要补全的字符和长度
trim 去除两边的空格
ltrim 去除左边的空格
rtrim 去除右边的空格
repeat 将字符串 s 重复 n 次
replace 用字符串 s2 代替字符串 s 中的字符串 s1
strcmp 比较字符串 s1 和 s2
substring 获取从字符串 s 中的第 n 个位置开始长度为 len 的字符串
instr 从字符串 s 中获取 s1 的开始位置
reverse 将字符串 s 的顺序反过来
field 返回第一个与字符串 s 匹配的字符串的位置
findInSet 返回在字符串 s2 中与 s1 匹配的字符串的位置
currentDate 返回当前日期
currentTime 返回当前时间
currentDateTime 返回当前日期和时间
unixTimestamp 以 UNIX 时间戳的形式返回当前时间
fromUnixTime 把 UNIX 时间戳的时间转换为普通格式的时间
month 日期 d 中的月份值,范围是 1~12
weekday 日期星期几,0 表示星期一,1 表示星期二
year 返回年
day 返回日期的天数值
hour 返回时间 t 中的小时值
dateDiff 计算日期 d1 到 d2 之间相隔的天数
dateAdd 日期加操作
dateSub 日期减操作
md5 字符串md5 加密
inetAton 将 IP 地址转换为数字表示,IP 值需要加上引号
inetNtoa 将数字 n 转换成 IP 的形式

多数据源支持

目前仅支持spring项目

点击前往多数据源文档

如何支持不同数据库

如何配置不同类型的数据库

mybatis:
  configuration:
    databaseId: MYSQL

目前数据库支持的类型

已测试通过的有:

H2,MYSQL,MARIA_DB ,PGSQL,ORACLE(12c及以上),SQL SERVER(2012及以上),DM达梦,DB2

其实也支持其他数据库(只要是和以上数据库规范接近的),只是作者没有放开,如有需要,可联系作者放开!

代码生成器

maven引入

<dependency>
    <groupId>cn.mybatis-mp</groupId>
    <artifactId>mybatis-mp-generator</artifactId>
    <version>1.5.0</version>
    <scope>provided</scope>
</dependency>

添加数据库驱动 例如:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.32</version>
</dependency>

然后,编写一个任意带有 main 方法的类,如下所示

// 根据数据库链接生成
new FastGenerator(new GeneratorConfig(
        "jdbc:mysql://xxx.xx.x:3306/数据库名字",
        "用户名",
        "密码")
        .basePackage("com.test")//根包路径
        ).create();

        or
        
//根据数据源生成
new FastGenerator(new GeneratorConfig(
        DbType.H2,//数据库类型
        dataSource)
        .basePackage("com.test")//根包路径
        ).create();

配置 GeneratorConfig

属性 默认值 说明
containerType SPRING

容器类型,默认SPRING

目前支持,SPRING、SOLON

swaggerVersion 3 swagger版本:2 代表2.x,3代表3.x
author "" 作者
ignoreView false 是否忽略视图
ignoreTable false 是否忽略表
baseFilePath System.getProperty("user.dir") + "/demo-generate" 根文件路径
basePackage "" 根包路径
templateRootPath templates 模板根目录,默认即可
templateEngine new FreemarkerTemplateEngine() 模板引擎,默认Freemarker引擎,其他引擎需要自己实现
templateBuilders 包含 实体类,mapper,mapper xml,dao,service,serviceImpl,action等模板生成构建器 模板生成构建器,继承AbstractTemplateBuilder,即可实现自己的生成器(生成自己的页面或其他类等)

配置 TableConfig(表配置)

new GeneratorConfig(...).tableConfig(tableConfig->{
    tableConfig.includeTables("table1","table2");
});
属性 默认值 说明
tablePrefixs 表、视图的前缀,用于生成类名时忽略前缀
includeTables 默认包含所有表、视图
excludeTables 排除表,默认不排除

配置 ColumnConfig(列配置)

new GeneratorConfig(...).columnConfig(columnConfig->{
    columnConfig.excludeColumns("create_time","creater_id");
});
属性 默认值 说明
versionColumn 指定乐观锁列名
tenantIdColumn 指定租户ID列名
logicDeleteColumn 逻辑删除列名,配置实体类配置:logicDeleteCode 一起使用
excludeColumns 排除列,默认不排除(在有公共实体类的时候很实用)
disableUpdateColumns 禁止更新的列,这样字段上会生成@TableField(update=false)
disableSelectColumns 禁止Select的列,这样字段上会生成@TableField(select=false)
defaultValueConvert 默认实现 可动态转换数据库的默认值(由静态值转成动态值)

配置 EntityConfig(实体类配置)

new GeneratorConfig(...).entityConfig(entityConfig->{
    entityConfig.lombok(true);
});
属性 默认值 说明
swagger false 是否开启swagger
superClass NULL 实体类的父类,例如:com.xx.test.BaseEntity
lombok true 是否开启lombok,这样类上会生成@Data
schema false 注解上是否加上schema信息
packageName DO 实体类包名
nameConvert NULL 实体类名转换器,可以自定义规则,默认大驼峰规则
fieldNamingStrategy NamingStrategy.UNDERLINE_TO_CAMEL 字段名策略,支持 NO_CHANGE ,UNDERLINE_TO_CAMEL
fieldNameConverter NULL 字段名转换器,优先级大于 fieldNamingStrategy
remarksConverter NULL 字段备注转换器,用于实现不一样的备注
defaultTableIdCode NULL 默认TableId代码,数据库非自增时生效,例如@TableId(...)
logicDeleteCode NULL 默认@LogicDelete代码,数据库非自增时生效,例如@LogicDelete(beforeValue="0",afterValue="1",deleteTimeField="create_time
typeMapping 内置包含各种列类型的java映射 数据库列类型映射,用于定制

配置 MapperConfig(mapper类配置)

new GeneratorConfig(...).mapperConfig(entityConfig->{
    mapperConfig.mapperAnnotation(true);
});
属性 默认值 说明
superClass 默认继承 MybatisMapper 接口 Mapper接口的父接口,例如:cn.mybatis.mp.core.mybatis.mapper.MybatisMapper
mapperAnnotation true 是否开启mybatis @Mapper注解,这样类上会生成@Mapper
packageName mapper mapper类的包名
suffix Mapper mapper类的后缀

配置 MapperXmlConfig(mapper xml配置)

new GeneratorConfig(...).mapperXmlConfig(mapperXmlConfig->{
    mapperXmlConfig.enable(true);
});
属性 默认值 说明
enable false 是否生成mapper xml
resultMap true 是否生成resultMap
columnList true 是否生成列信息,用于select 列
packageName mappers mapper xml的目录名字
suffix "" mapper xml文件的后缀

配置 DaoConfig(dao接口配置)

new GeneratorConfig(...).daoConfig(daoConfig->{
    daoConfig.enable(true);
});
属性 默认值 说明
enable false 是否生成 dao 接口
superClass 默认继承 Dao 接口 dao接口的父接口,例如:cn.mybatis.mp.core.mvc.Dao
generic true 是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageName dao dao接口的包名
suffix Dao dao接口的后缀

配置 DaoImplConfig(dao接口实现类的配置)

new GeneratorConfig(...).daoImplConfig(daoImplConfig->{
    daoImplConfig.enable(true);
});
属性 默认值 说明
superClass 默认继承 DaoImpl 实现类 dao接口的父接口,例如:cn.mybatis.mp.core.mvc.impl.DaoImpl
generic true 是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageName dao.impl dao实现类的包名
suffix DaoImpl dao实现类的后缀

配置 ServiceConfig(service接口配置)

new GeneratorConfig(...).serviceConfig(serviceConfig->{
    serviceConfig.enable(true);
});
属性 默认值 说明
enable false 是否生成 Service 接口
superClass 默认继承 Service 接口 Service接口的父接口,例如:cn.mybatis.mp.core.mvc.Service
generic true 是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageName service Service接口的包名
suffix Service Service接口的后缀

配置 ServiceImplConfig(service接口实现类的配置)

new GeneratorConfig(...).serviceImplConfig(serviceImplConfig->{
    serviceImplConfig.injectDao(true);
});
属性 默认值 说明
injectDao true 是否注入dao
injectMapper true 是否注入mapper
superClass 默认继承 ServiceImpl 实现类 dao接口的父接口,例如:cn.mybatis.mp.core.mvc.impl.ServiceImpl
generic true 是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageName service.impl service实现类的包名
suffix ServiceImpl service实现类的后缀

配置 ActionConfig(action实现类的配置)

new GeneratorConfig(...).actionConfig(actionConfig->{
    actionConfig.enable(true);
});
属性 默认值 说明
enable true 是否生成控制器
swagger true 是否开启swagger
injectService true 是否注入service
superClass NULL action父类,例如:cn.xxx.BaseAction
generic true 是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageName action action实现类的包名
suffix ServiceImpl action实现类的后缀
returnClass Object save update 等返回的类型
save true 是否生成save方法
update true 是否生成update方法
deleteById true 是否生成deleteById方法
getById true 是否生成getById方法
find true 是否生成find方法

如何扩展 SQL指令

1.继承 Cmd 实现 sql 方法即可

2.联系作者 帮忙 实现

扩展起来,非常方便,不过最好是联系作者,这样可以让更多开发者使用!

支持作者,赏作者一盒盒饭(^o^)

mybatis-mp's People

Contributors

ai-010 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.