首页 > 其他分享 >全新一款基于mybatis的ORM框架,你没有见过的好用的框架,不信的前来看看!!!

全新一款基于mybatis的ORM框架,你没有见过的好用的框架,不信的前来看看!!!

时间:2024-04-10 21:01:29浏览次数:35  
标签:框架 getId id ORM SysUser mybatis class select

喜欢的朋友加入QQ群:917404304 ,群里不仅可以提mybatis-mp框架问题,还可以帮你解决后端的各种问题!
另外,喜欢的朋友,帮忙关注 和 star(点点小爱心)!

https://gitee.com/mybatis-mp

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

与众不同的 几大亮点:

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 群

群号: 917404304

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.4.7</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.4.7-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,默认是数据库自增

属性默认说明
valueIdAutoType.AUTO

IdAutoType.AUTO: 数据库自增

IdAutoType.NONE: 开发者自己set值

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

IdAutoType.NONE: 开发者自己set值

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

dbTypeDbType.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-11T10:10:10)]

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

SysRoleVo 也可以是 实体类 SysRole

@Fetch 属性说明

属性默认值说明
column""

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

column 和 property 二选一,column优先

sourceVoid.class

指定列的对应的实体

选择property属性时填写

property""

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

column 和 property 二选一,column优先

storey1

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

选择property属性时填写

target

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

targetProperty

指定加载实体类的属性

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

targetSelectProperty

指定返回实体类的属性

指定返回属性对象列

multiValueErrorIgnorefalse

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();

或者

List<SysUser> list=sysUserMapper.list(where->where.lt(SysUser::getId, 3));
        

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 其他函数

abs,
pow,
concat,
concatAs,
round,
if,
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.2.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

属性默认值说明
containerTypeSPRING

容器类型,默认SPRING

目前支持,SPRING、SOLON

swaggerVersion3swagger版本:2 代表2.x,3代表3.x
author""作者
ignoreViewfalse是否忽略视图
ignoreTablefalse是否忽略表
baseFilePathSystem.getProperty("user.dir") + "/demo-generate"根文件路径
basePackage""根包路径
templateRootPathtemplates模板根目录,默认即可
templateEnginenew 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);
});
属性默认值说明
swaggerfalse是否开启swagger
superClassNULL实体类的父类,例如:com.xx.test.BaseEntity
lomboktrue是否开启lombok,这样类上会生成@Data
schemafalse注解上是否加上schema信息
packageNameDO实体类包名
nameConvertNULL实体类名转换器,可以自定义规则,默认大驼峰规则
fieldNamingStrategyNamingStrategy.UNDERLINE_TO_CAMEL字段名策略,支持 NO_CHANGE ,UNDERLINE_TO_CAMEL
fieldNameConverterNULL字段名转换器,优先级大于 fieldNamingStrategy
remarksConverterNULL字段备注转换器,用于实现不一样的备注
defaultTableIdCodeNULL默认TableId代码,数据库非自增时生效,例如@TableId(...)
logicDeleteCodeNULL默认@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
mapperAnnotationtrue是否开启mybatis @Mapper注解,这样类上会生成@Mapper
packageNamemappermapper类的包名
suffixMappermapper类的后缀

配置 MapperXmlConfig(mapper xml配置)

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

配置 DaoConfig(dao接口配置)

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

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

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

配置 ServiceConfig(service接口配置)

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

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

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

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

new GeneratorConfig(...).actionConfig(actionConfig->{
    actionConfig.enable(true);
});
属性默认值说明
enabletrue是否生成控制器
swaggertrue是否开启swagger
injectServicetrue是否注入service
superClassNULLaction父类,例如:cn.xxx.BaseAction
generictrue是否启用泛型,启用后会在superclass后面加泛型>Entity,ID>
packageNameactionaction实现类的包名
suffixServiceImplaction实现类的后缀
returnClassObjectsave update 等返回的类型
savetrue是否生成save方法
updatetrue是否生成update方法
deleteByIdtrue是否生成deleteById方法
getByIdtrue是否生成getById方法
findtrue是否生成find方法

如何扩展 SQL指令

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

2.联系作者 帮忙 实现

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

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

标签:框架,getId,id,ORM,SysUser,mybatis,class,select
From: https://blog.csdn.net/2301_81633770/article/details/137610388

相关文章

  • 创建一个简单的区块链,并使用 Flask 框架提供一个简单的 Web 接口来与区块链交互。
    目录前言一、代码展示二、代码注释1.简单的区块链模拟实现,这段代码定义了一个简单的区块链类,可以创建一个新的区块链,并能够添加新的区块到链上,同时获取最新的区块信息。这个模拟的区块链可以用于教学或演示区块链的基本概念。2.这段代码是一个简单的工作量证明(ProofofWo......
  • mybatis-mp:这是一款你没见过的好用的ORM框架,值得你拥有!不信,可以试试看!
    https://gitee.com/mybatis-mp @Data@ResultEntity(SysRole.class)publicclassOneToManyVo{privateIntegerid;privateStringname;privateLocalDateTimecreateTime;@ResultField(value="otherName")privateStringasNam......
  • 扩展ueditor中公式插件kityformula的字符集
    https://blog.csdn.net/hshuaile/article/details/86079147 例如,我想在公式中使用"▱"符号,但是现有公式插件不支持输入,所以需要扩展,扩展步骤如下在网上找到"▱"符号,并起一个名字,例如叫parallelogram打开kity-formula-render.all.js文件,找到如下代码/*!*字体主文件*/_p[29]......
  • 掌握Ollama框架:一站式教程,从环境搭建到高效开发
    掌握Ollama框架:一站式教程,从环境搭建到高效开发安装ollamamacOSWindows预览版LinuxDockerollama的库和工具ollama-pythonollama-js快速开始运行模型访问模型库自定义模型从GGUF导入模型自定义提示CLI参考创建模型拉取模型删除模型复制模型多行输入多模态模型提示作为......
  • Mybatis入门
    1.Mybatis的入门1.1导包1.2准备核心配置文件MyBatis-Config.xml点击查看代码<configuration><!--引入配置文件信息,这里不能加classpath:。 resource:引入类路径下的资源,即classpath,所以不需要写classpath: url:引入网络路径或磁盘路径下的资源 --> <properti......
  • keycloak~对框架中提供的Provider总结
    提供者目录ProviderAuthenticatorBaseDirectGrantAuthenticatorAbstractFormAuthenticatorAbstractUsernameFormAuthenticatorRequiredActionProviderFormActionProtocolMapperAbstractOIDCProtocolMapperRealmResourceProvider具体provider的作用Provid......
  • 基于Golang的Nano游戏服务器框架
    在游戏开发过程中,一个高效的服务器框架是至关重要的。Nano正是这样一个框架,它以Golang为基础,提供了轻量级、高性能的服务器解决方案。下面,我们将深入探讨Nano的设计理念、核心特性以及如何在实战中使用它。Nano框架概述Nano是一个针对游戏服务器的框架,能够帮助开发者快速......
  • GitHub问题解决新突破,复旦大学MAGIS框架大幅超越GPT-4
    获取本文论文,请关注公众号【AI论文解读】回复:&nbsp;论文解读引言:GitHub问题解决的挑战与LLMs的潜力在软件开发的演进过程中,解决GitHub仓库中出现的问题是一个复杂的挑战。这不仅涉及到新代码的加入,还要维护现有功能的稳定运行。大型语言模型(LLMs)在代码生成和理解方......
  • 前端流程引擎有哪些框架推荐
    前端流程引擎通常是指用于设计、建模、执行和可视化业务流程、工作流或流程图的软件组件,它们通常包含图形化的设计器、流程模型解析与执行引擎、以及与用户交互的可视化界面。以下是一些推荐的前端流程引擎框架:bpmn-js:开源项目,基于BPMN2.0标准,提供流程图的绘制、解析......
  • 【大模型应用开发-FastAPI框架】(五)FastAPI 如何通过Poetry运行FastAPI应用程序
    一、概述FastAPI是一个现代、快速(高性能)的Web框架,用于构建API。Poetry是一个Python的依赖管理和打包工具,可以帮助我们更有效地管理项目的依赖和环境。在本文中,我们将介绍如何使用Poetry来运行FastAPI应用程序。二、安装FastAPI和Poetry在开始之前,我们需要先安装FastAPI和P......