1 Star 0 Fork 0

yyjxpfz / basedao

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

BaseDao

介绍

这是一款基于MyBatis的SQL框架,该框架旨在提供一套高效、便捷、无侵入的数据库操作工具,帮助开发者快速实现数据库的增删改查操作。

核心特性:

  1. 标准Mapper方法:提供了一套标准的、通用的数据库操作方法,如insertupdateByIdselectByExampledeleteById等,极大地减少了手动编写SQL的工作量。

  2. 支持复杂查询:利用注解处理器(JSR269),框架可以在编译时自动生成Query类。这些Query类提供流式调用的方式,帮助开发者更直观方便地构建查询条件,以应对复杂查询SQL需求。

  3. 逻辑删除功能:支持逻辑删除。在逻辑删除模式下,删除操作自动切换为更新删除标记操作,新增操作自动初始化为未删除状态,更新和查询操作自动忽略已删除数据。此外,采用了不破坏唯一索引约束的逻辑删除方式,相同唯一键下允许存在多条已删除的数据,仅允许存在一条未删除的数据。

  4. 支持分库分表:内置分库分表功能,支持指定键分键和自定义分库分表算法,框架自动根据算法选择库表。同时也兼容第三方分库分表组件。

  5. 代码生成:提供工具通过数据库表结构自动生成Entity类,提高开发效率。

  6. 字节码增强:采用字节码增强技术提高框架性能和效率,相比传统的反射调用方式性能表现显著提升。

安装及使用

菜鸟级——开箱即用

  1. 引入jar包依赖。
<dependency>
    <groupId>priv.pfz</groupId>
    <artifactId>basedao-core</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  1. 将需要增强的Mapper类继承SimpleBaseMapper。
public interface UserMapper extends SimpleBaseMapper<UserEntity> {
    //手写mapper.xml的方法可与BaseMapper方法共存
   ...
}
  1. 继承SimpleBaseMapper后,UserMapper自动具备一组基础方法,开箱即用。
    @Resource
    private UserMapper userMapper;

    @Test
    public void test() {
        //simple
        userMapper.insert(new UserEntity());
        userMapper.updateById(new UserEntity());
        userMapper.updateByExample(new UserEntity());
        userMapper.updateByExamples(new ArrayList<>());
        userMapper.selectById(1);
        userMapper.listByIds(new ArrayList<>());
        userMapper.mapByIds(new ArrayList<>());
        userMapper.selectByExample(new UserEntity());
        userMapper.listByExample(new UserEntity());
        userMapper.listByExamples(new ArrayList<>());
        userMapper.countAll();
        userMapper.listAll();
        userMapper.deleteById(1);
        userMapper.deleteByIds(new ArrayList<>());
        userMapper.deleteByExample(new UserEntity());
        userMapper.deleteByExamples(new ArrayList<>());
    }

进阶级——注解增强

  1. 在Entity类的字段上进行注解标注,例如@NoInsert、@UniqueKey、@NoColumn等。
public class UserEntity {
    /**
     * 数据库自增id
     */
    @NoInsert //insert时无需保存此字段
    private Integer id;

    /**
     * 身份证号
     */
    @UniqueKey //身份证号是唯一键
    private String idCardNo;

    /**
     * 姓名
     */
    private String name;
    
    /**
     * 年龄
     */
    private Integer age;

    /**
     * 住址
     */
    @NoColumn //此字段保存在redis中,数据库并没有此字段
    private String address;
}
  1. 将UserMapper继承的接口更换为AnnotatedBaseMapper,即可新解锁若干方法。
public interface UserMapper extends AnnotatedBaseMapper<UserEntity> {
    ...
}
    @Resource
    private UserMapper userMapper;

    @Test
    public void test() {
        //annotated
        userMapper.batchInsert(new ArrayList<>());
        userMapper.updateByUniqueKey(new UserEntity());
    }

专家级——复杂查询

  1. 类似Lombok,在Entity类上标注@Entity注解,即可在编译时自动生成对应的Query类。
@Entity
public class UserEntity {
    ...
}

此类为框架自动生成,用于构造复杂查询的查询条件

/**
 * @author BaseDao
 * 2024/1/29 23:26
 */
public class UserQuery extends BaseQuery<UserQuery> {
 
    public final IntColumn<UserQuery> id = new IntColumn<>(this, "id", "id"); 
 
    public final StringColumn<UserQuery> idCardNo = new StringColumn<>(this, "idCardNo", "id_card_no"); 
 
    public final StringColumn<UserQuery> name = new StringColumn<>(this, "name", "name"); 
 
    public final IntColumn<UserQuery> age = new IntColumn<>(this, "age", "age");
}
  1. 将UserMapper继承的接口更换为DefaultBaseMapper,即可支持复杂查询。
public interface UserMapper extends DefaultBaseMapper<UserEntity, UserQuery> {
    ...
}
    @Resource
    private UserMapper userMapper;
    
    @Test
    public void test() {
        //default
        userMapper.update(new UserEntity(), new UserQuery());
        userMapper.count(new UserQuery());
        userMapper.list(new UserQuery());
        userMapper.delete(new UserQuery());
        userMapper.pageQuery(new UserQuery());
        
        //query example
        UserQuery query = new UserQuery()
                .idCardNo.eq("123456")
                .name.like("诸葛")
                .age.between(20, 40)
                .id.desc()
                .page(2, 10);
        userMapper.list(query);
    }

BaseMapper方法汇总

INSERT

方法名 方法签名 功能 说明 SQL伪代码
insert int insert(@Nonnull Entity entity); insert一条记录 entity中为空的字段不会保存 INSERT INTO table (a,b,c) VALUES (entity.a, entity.b, entity.c)
batchInsert int batchInsert(@Nonnull Iterable<Entity> entities); 批量insert多条记录 选择Entity类未标注@NoInsert的字段保存 INSERT INTO table (a,b,c) VALUES (entity1.a, entity1.b, entity1.c), (entity2.a, entity2.b, entity2.c), ...

UPDATE

方法名 方法签名 功能 说明 SQL伪代码
updateById int updateById(@Nonnull Entity entity); 按id更新一条记录 entity中为空的字段不会更新 UPDATE table SET a=entity.a, b=entity.b WHERE id=entity.id
updateByUniqueKey int updateByUniqueKey(@Nonnull Entity entity); 按Entity中标注了@UniqueKey的字段更新一条记录 entity中为空的字段不会更新 UPDATE table SET a=entity.a, b=entity.b WHERE uk=entity.uk
updateByExample int updateByExample(@Nonnull Entity entity, @Nonnull Entity example); 将example匹配的记录更新为entity example中为空的字段不会作为查询条件,entity中为空的字段不会更新 UPDATE table SET a=entity.a, b=entity.b WHERE b=example.b AND c=example.c
updateByExamples int updateByExamples(@Nonnull Entity entity, @Nonnull Iterable<Entity> examples); 将examples匹配的记录更新为entity example中为空的字段不会作为查询条件,entity中为空的字段不会更新 UPDATE table SET a=entity.a, b=entity.b WHERE (b=example1.b AND c=example1.c) OR (c=example2.c AND d=example2.d) OR ...
update int update(@Nonnull Entity entity, @Nonnull Query query); 按query条件批量更新多条记录 entity中为空的字段不会更新 UPDATE table SET a=entity.a, b=entity.b WHERE query

SELECT

方法名 方法签名 功能 说明 SQL伪代码
selectById Entity selectById(long id); 按id查询一条记录 SELECT a,b,c FROM table WHERE id=#{id}
listByIds List<Entity> listByIds(@Nonnull Iterable<Long> ids); 按ids批量查询多条记录 SELECT a,b,c FROM table WHERE id in ids
mapByIds Map<Long, Entity> mapByIds(@Nonnull Iterable<Long> ids); 按ids批量查询多条记录,返回以id为key的map SELECT a,b,c FROM table WHERE id in ids
selectByExample Entity selectByExample(@Nonnull Entity example); 按example中的条件查询第一条记录 example中为空的字段不会作为查询条件 SELECT a,b,c FROM table WHERE a=entity.a AND b=entity.b LIMIT 1
listByExample List<Entity> listByExample(@Nonnull Entity example); 按example中的条件查询多条记录 example中为空的字段不会作为查询条件 SELECT a,b,c FROM table WHERE a=entity.a AND b=entity.b
listByExamples List<Entity> listByExamples(@Nonnull Iterable<Entity> examples); 按examples中的条件查询多条记录 example中为空的字段不会作为查询条件 SELECT a,b,c FROM table WHERE (a=entity1.a AND b=entity1.b) OR (b=entity2.b AND c=entity2.c) OR ...
count int count(@Nonnull Query query); 按query条件查询数量 query中为空的字段不会作为查询条件 SELECT count(*) FROM table WHERE query
countAll int countAll(); 查询全表数量 SELECT count(*) FROM table
list List<Entity> list(@Nonnull Query query); 按query条件查询多条记录 query中为空的字段不会作为查询条件 SELECT a,b,c FROM table WHERE query
listAll List<Entity> listAll(); 查询全表所有记录 SELECT a,b,c FROM table
pageQuery Page<Entity> pageQuery(@Nonnull Query query); 分页查询 参考count和list方法

DELETE

方法名 方法签名 功能 说明 SQL伪代码
deleteById int deleteById(long id); 按id删除一条记录 DELETE FROM table WHERE id=#{id}
deleteByIds int deleteByIds(@Nonnull Iterable<Long> ids); 按ids批量删除多条记录 DELETE FROM table WHERE id in ids
deleteByExample int deleteByExample(@Nonnull Entity example); 按example中的条件删除多条记录 example中为空的字段不会作为删除条件 DELETE FROM table WHERE a=entity.a AND b=entity.b
deleteByExamples int deleteByExamples(@Nonnull Iterable<Entity> examples); 按examples中的条件删除多条记录 example中为空的字段不会作为删除条件 DELETE FROM table WHERE (a=entity1.a AND b=entity1.b) OR (b=entity2.b AND c=entity2.c) OR ...
delete int delete(@Nonnull Query query); 按query中的条件删除多条记录 query中为空的字段不会作为删除条件 DELETE FROM table WHERE query

注解汇总

注解名 标注位置 功能
@Table Mapper类 指定Mapper对应的数据库名、表名、是否开启逻辑删除、分库分表模式等
@Entity Entity类 使此Entity类开启自动生成相应的Query类
@NoColumn Entity字段 使框架全局忽略此字段
@NoSelect Entity字段 select/list系列方法不返回此字段,适用于text大字段,以提升性能(对于大字段建议单独手写SQL查询)
@NoInsert Entity字段 batchInsert方法不保存此字段,适用于数据库自增id、创建时间、更新时间等字段
@UniqueKey Entity字段 updateByUniqueKey方法以标注了此注解的一个或多个字段作为查询条件
@Column Entity字段 手工指定数据库列名,推荐Entity字段名与数据库列名保持一致,尽量避免使用此注解
@ShardingKey Entity字段 在分库分表模式下用于指定切分键和切分策略

Query查询能力汇总

  1. WHERE(字段级)
    UserQuery query = new UserQuery()
        .idCardNo.eq("123456");

除between和notBetween外,当value为空时,不会创建条件。非String类型“空”的含义为null,String类型“空”的含义为blank。

数据类型 操作 方法签名 SQL伪代码
所有 eq Query eq(@Nullable T value); xxx = 'abc'
所有 ne Query ne(@Nullable T value); xxx != 'abc'
所有 gt Query gt(@Nullable T value); xxx > 'abc'
所有 lt Query lt(@Nullable T value); xxx < 'abc'
所有 gte Query gte(@Nullable T value); xxx >= 'abc'
所有 lte Query lte(@Nullable T value); xxx <= 'abc'
所有 isNull Query isNull(); xxx IS NULL
所有 notNull Query notNull(); xxx IS NOT NULL
所有 in Query in(@Nullable Iterable<T> values)
Query in(@Nullable T... values)
xxx IN ('a', 'b', 'c')
所有 notIn Query notIn(@Nullable Iterable<T> values)
Query notIn(@Nullable T... values)
xxx NOT IN ('a', 'b', 'c')
所有 between Query between(@Nonnull T from, @Nonnull T to); xxx BETWEEN 'a' AND 'b'
所有 notBetween Query notBetween(@Nonnull T from, @Nonnull T to); xxx NOT BETWEEN 'a' AND 'b'
String isEmpty Query isEmpty(); xxx = ''
String notEmpty Query notEmpty(); xxx != ''
String like Query like(@Nullable String value); xxx LIKE CONCAT('%', 'abc', '%')
String notLike Query notLike(@Nullable String value); xxx NOT LIKE CONCAT('%', 'abc', '%')
String startWith Query startWith(@Nullable String value); xxx LIKE CONCAT('abc', '%')
String endWith Query endWith(@Nullable String value); xxx LIKE CONCAT('%', 'abc')
  1. ORDER BY(字段级)
    UserQuery query = new UserQuery()
        .id.desc();
操作 方法签名 SQL伪代码
asc Query asc(); ORDER BY xxx ASC
desc Query desc(); ORDER BY xxx DESC
  1. LIMIT(查询级)
    UserQuery query = new UserQuery()
        .page(2, 10);
操作 方法签名 SQL伪代码
page Query page(int pageNo, int pageSize); LIMIT (pageNo-1)*pageSize, pageSize
limit Query limit(int offset, int rows); LIMIT offset, rows
limit Query limit(int rows) LIMIT rows

逻辑删除功能

  1. 开启条件:数据库表中已存在delete_flag字段,类型为int,唯一索引需包含delete_flag字段。
  2. 开启方式:在Mapper的@Table注解中设置enableLogicDelete=true
@Table(enableLogicDelete = true)
public interface UserMapper extends DefaultBaseMapper<UserEntity, UserQuery> {
    ...
}
  1. 开启效果:所有字段由物理删除改为逻辑删除,delete_flag=0表示未删除,delete_flag>0表示已删除,已删除的记录无法查询或更新。

  2. SQL转换原理:

    SELECT默认添加"delete_flag=0"条件

    INSERT默认设置delete_flag为0

    UPDATE默认添加"delete_flag=0"条件

    DELETE转换为"UPDATE table SET delete_flag = id WHERE query AND delete_flag=0"

分库分表功能

  1. 开启方式:

1)在@Table中设置shardingMode为SHARDING_DB(仅分库)、SHARDING_TABLE(仅分表)、SHARDING_DB_TABLE(分库分表)之一。开启分库时,@Table的dbName为必填。

2)可选:将继承的BaseMapper替换为分库分表版本,即xxxShardingBaseMapper。ShardingBaseMapper可通过参数中的shardingInfo手工指定分库分表信息和获取分库分表结果。

@Table(dbName = "db", shardingMode = ShardingMode.SHARDING_DB_TABLE)
public interface UserMapper extends DefaultShardingBaseMapper<UserEntity, UserQuery> {
    ...
}

3)可选:在Entity的切分键字段上标注@ShardingKey,并设置切分策略类。设置@ShardingKey后,若SQL中有此字段的精确条件,则会根据切分策略自动生成分库分表信息。

    /**
     * 身份证号
     */
    @UniqueKey
    @ShardingKey(shardingStrategy = UserShardingStrategy.class)
    private String idCardNo;
/**
 * user表根据idCardNo分库分表的切分策略
 */
public class UserShardingStrategy implements ShardingStrategy<String> {
    @Override
    public ShardingInfo getShardingInfo(String idCardNo) {
        String dbSuffix = ...;
        String tableSuffix = ...;
        return new ShardingInfo(dbSuffix, tableSuffix);
    }
}
  1. 使用方式:

手工指定分库分表:调用ShardingBaseMapper中的方法,传入shardingInfo参数即可指定。

    UserEntity userEntity = new UserEntity();
    userEntity.setName("yyjx");
    userEntity.setAge(99);
    
    //手工指定02库05表
    ShardingInfo shardingInfo = ShardingInfo.dbTable("02", "05");
    
    userMapper.insert(userEntity, shardingInfo);
    //INSERT INTO db_02.`user_05` (`name`, `age`) VALUES (?, ?);

自动选择分库分表:在Entity或Query中填写切分键字段,若SQL中存在切分键的精确条件,则会自动生成分库分表信息。

    //包含切分键idCardNo,根据123456自动计算出分库分表
    UserEntity userEntity = new UserEntity();
    userEntity.setName("yyjx");
    userEntity.setAge(99);
    userEntity.setIdCardNo("123456");
        
    //shardingInfo可不设置值
    ShardingInfo shardingInfo = ShardingInfo.empty();
    
    userMapper.insert(userEntity, shardingInfo);
    //INSERT INTO db_02.`user_05` (`name`, `age`, `id_card_no`) VALUES (?, ?, ?);

获取分库分表结果:调用ShardingBaseMapper中的方法后,框架实际选择的分库分表结果会回填到shardingInfo参数中。

    UserEntity userEntity = new UserEntity();
    userEntity.setName("yyjx");
    userEntity.setAge(99);
    userEntity.setIdCardNo("123456");
        
    ShardingInfo shardingInfo = ShardingInfo.empty();
    
    userMapper.insert(userEntity, shardingInfo);
    //INSERT INTO db_02.`user_05` (`name`, `age`, `id_card_no`) VALUES (?, ?, ?);
    
    //调用方法后从shardingInfo中获取实际的分库分表信息
    System.out.println(shardingInfo);
    //ShardingInfo(dbSuffix=02, tableSuffix=05)

支持第三方分库分表组件

  1. 在@Table中设置shardingMode为THIRD_SHARDING_COMPONENT。

  2. 将继承的BaseMapper替换为分库分表版本,即xxxShardingBaseMapper。

@Table(shardingMode = ShardingMode.THIRD_SHARDING_COMPONENT)
public interface UserMapper extends DefaultShardingBaseMapper<UserEntity, UserQuery> {
    ...
}
  1. 调用ShardingBaseMapper中的方法,传入shardingInfo参数即可。
    UserEntity userEntity = new UserEntity();
    userEntity.setName("yyjx");
    userEntity.setAge(99);
    userEntity.setIdCardNo("123456");
        
    //shardingInfo中传入第三方所需的分库分表信息,此信息段追加到SQL末尾
    ShardingInfo shardingInfo = ShardingInfo.third("/@sdb=02;ts=05@/");
    
    userMapper.insert(userEntity, shardingInfo);
    //INSERT INTO `user` (`name`, `age`, `id_card_no`) VALUES (?, ?, ?) /@sdb=02;ts=05@/ 

Entity生成器

框架内置了一个Entity生成器,开发阶段可手工调用生成器生成Entity类,生成器会连接数据库查询DDL,根据DDL生成对应的Entity类。

调用示例如下:

public static void main(String[] args) {
   Generator.newInstance()
           .entitySuffix("Entity")
           .entityPath("basedao-lab/src/main/java/priv/pfz/basedao/lab/dao/entity/")
           .entityPackage("priv.pfz.basedao.lab.dao.entity")
           .dbName("world")
           .tableNames("tb_goods_00", "tb_trade_info_00")
           .tableNamePrefix("tb_")
           .tableNameSuffix("_00")
           .dbUrl("jdbc:mysql://localhost:3306/")
           .dbUser("root")
           .dbPassword("123456")
           .generate();
}

数据库DDL如下:

CREATE TABLE `tb_goods_00` (
    `id` int NOT NULL COMMENT '自增id',
    `goods_code` varchar(30) NOT NULL COMMENT '商品编码',
    `name` varchar(30) DEFAULT NULL COMMENT '商品名称',
    `price` int DEFAULT NULL COMMENT '价格',
    `delete_flag` int NOT NULL COMMENT '删除标识',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

自动生成的Entity类如下:

package priv.pfz.basedao.lab.dao.entity;

import lombok.Data;
import priv.pfz.basedao.annotations.Entity;
import java.util.Date;

/**
 * @author BaseDao
 * 2024/2/6 0:27
 */
@Data
@Entity
public class GoodsEntity  {

   /**
    * 自增id
    */
   private Long id;

   /**
    * 商品编码
    */
   private String goodsCode;

   /**
    * 商品名称
    */
   private String name;

   /**
    * 价格
    */
   private Long price;

   /**
    * 删除标识
    */
   private Long deleteFlag;

   /**
    * 创建时间
    */
   private Date createTime;

   /**
    * 更新时间
    */
   private Date updateTime;
}

空文件

简介

一款基于MyBatis的高效、便捷、无侵入的SQL框架 展开 收起
Java
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/yyjxpfz/basedao.git
git@gitee.com:yyjxpfz/basedao.git
yyjxpfz
basedao
basedao
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891