这是一款基于MyBatis的SQL框架,该框架旨在提供一套高效、便捷、无侵入的数据库操作工具,帮助开发者快速实现数据库的增删改查操作。
核心特性:
标准Mapper方法:提供了一套标准的、通用的数据库操作方法,如insert
、updateById
、selectByExample
、deleteById
等,极大地减少了手动编写SQL的工作量。
支持复杂查询:利用注解处理器(JSR269),框架可以在编译时自动生成Query类。这些Query类提供流式调用的方式,帮助开发者更直观方便地构建查询条件,以应对复杂查询SQL需求。
逻辑删除功能:支持逻辑删除。在逻辑删除模式下,删除操作自动切换为更新删除标记操作,新增操作自动初始化为未删除状态,更新和查询操作自动忽略已删除数据。此外,采用了不破坏唯一索引约束的逻辑删除方式,相同唯一键下允许存在多条已删除的数据,仅允许存在一条未删除的数据。
支持分库分表:内置分库分表功能,支持指定键分键和自定义分库分表算法,框架自动根据算法选择库表。同时也兼容第三方分库分表组件。
代码生成:提供工具通过数据库表结构自动生成Entity类,提高开发效率。
字节码增强:采用字节码增强技术提高框架性能和效率,相比传统的反射调用方式性能表现显著提升。
<dependency>
<groupId>priv.pfz</groupId>
<artifactId>basedao-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
public interface UserMapper extends SimpleBaseMapper<UserEntity> {
//手写mapper.xml的方法可与BaseMapper方法共存
...
}
@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<>());
}
public class UserEntity {
/**
* 数据库自增id
*/
@NoInsert //insert时无需保存此字段
private Integer id;
/**
* 身份证号
*/
@UniqueKey //身份证号是唯一键
private String idCardNo;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 住址
*/
@NoColumn //此字段保存在redis中,数据库并没有此字段
private String address;
}
public interface UserMapper extends AnnotatedBaseMapper<UserEntity> {
...
}
@Resource
private UserMapper userMapper;
@Test
public void test() {
//annotated
userMapper.batchInsert(new ArrayList<>());
userMapper.updateByUniqueKey(new UserEntity());
}
@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");
}
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);
}
方法名 | 方法签名 | 功能 | 说明 | 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), ... |
方法名 | 方法签名 | 功能 | 说明 | 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 |
方法名 | 方法签名 | 功能 | 说明 | 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方法 |
方法名 | 方法签名 | 功能 | 说明 | 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字段 | 在分库分表模式下用于指定切分键和切分策略 |
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') |
UserQuery query = new UserQuery()
.id.desc();
操作 | 方法签名 | SQL伪代码 |
---|---|---|
asc | Query asc(); | ORDER BY xxx ASC |
desc | Query desc(); | ORDER BY xxx DESC |
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 |
@Table(enableLogicDelete = true)
public interface UserMapper extends DefaultBaseMapper<UserEntity, UserQuery> {
...
}
开启效果:所有字段由物理删除改为逻辑删除,delete_flag=0表示未删除,delete_flag>0表示已删除,已删除的记录无法查询或更新。
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)在@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);
}
}
手工指定分库分表:调用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)
在@Table中设置shardingMode为THIRD_SHARDING_COMPONENT。
将继承的BaseMapper替换为分库分表版本,即xxxShardingBaseMapper。
@Table(shardingMode = ShardingMode.THIRD_SHARDING_COMPONENT)
public interface UserMapper extends DefaultShardingBaseMapper<UserEntity, UserQuery> {
...
}
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类,生成器会连接数据库查询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;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型