Skip to content

dingkui/dlz-db

Repository files navigation

DLZ-DB

一个不到 7000 行代码的 Java 数据库框架,让你写 SQL 像写本地代码一样直接。

License JDK Build Status Maven Central

List<User> users = DB.Pojo.select(User.class)
        .eq(User::getStatus, 1)
        .like(User::getName, "张")
        .orderByDesc(User::getCreateTime)
        .queryBeanList();

没有 Mapper 接口,没有 Service 层,没有 XML。


版本说明

当前版本 v7.0.0(开源初始版 v6.6.4)。

这个项目不是从零开始的。它大约在 2009 年开始积累,2014 年左右成型,作为公司内部的数据库操作工具包投入使用。此后十年间,累计被数十个内部项目采用,适配过各种老旧系统、各种开源框架组合、各种奇奇怪怪的版本混搭。

2024 年,我们决定将它开源。为此做了大量重构和删减——剥离内部依赖、清理历史包袱、提炼通用能力,最终发布了第一个公开版本 6.6.4

所以版本号不是从 1.0 开始的——因为你看到的是一个已经跑了十几年的工具,被数十个项目验证过,而不是一个刚起步的新项目。


为什么又一个数据库框架?

如果你写过一段时间 Java,大概率经历过这些:

  • 为了一次简单的 CRUD,创建 6 个文件、200 行代码。
  • 线上日志一条 SQL,全局搜半小时找不到是谁执行的。
  • @DS("slave") 写在注解里,新租户动态接入?对不起,字符串硬编码。
  • MyBatis 出异常,栈里 15 层代理,看不出错在哪一行业务代码。
  • 查询返回一个 JSON 字段,还得自己 JSON.parseObject(...) 一层层剥。

DLZ-DB 想解决的不是"没有框架",而是**"框架长在了用户不想要它长的地方"**。


四个你会立刻感受到的不同

1. SQL 日志能直接跳到写它的那一行

caller:(UserController.java:42) getList 15ms sql:SELECT * FROM user WHERE id = 1
        ↑                                    ↑                 ↑
   IDE 点这里直接跳转              真实耗时           参数已填充,复制即可执行

MyBatis 日志告诉你"有一条 SQL 跑了",DLZ-DB 告诉你**"是你的 UserController 第 42 行跑的"**。生产排查时,这一条就值回票价。


2. 多数据源:运行时完全动态

@DS("slave") 在编译期就把数据源 key 写死了。想按租户、按 Header、按灰度规则路由?SpEL 补丁、手动 push/pop、AOP 顺序……每一步都在和框架较劲。

DLZ-DB 把两端都做成字符串:

// 运行时注册一个新数据源
DB.Dynamic.setDataSource(prop);

// 运行时用任意逻辑决定走哪个库
String dsName = routeByTenant(tenantId);
User user = DB.Dynamic.use(dsName, () ->
    DB.Pojo.select(User.class).eq(User::getId, id).queryBean()
);

SaaS 多租户、动态数据源管理、ETL 工具、灰度迁移——这些场景里注解派要绕一大圈,这里两行解决。


3. 核心代码不到 7000 行,2天能通读

这不是"功能少",是**"不做你不需要的事"**:

  • 不做 Mapper 接口和 XML 双向映射 → 省掉解析引擎。
  • 不做 SqlSession / Executor 分层 → 调用栈直通 JDBC。
  • 不做一二级缓存 → 交给 Redis / Caffeine,各司其职。

你因此得到的实际好处:

  • 可通读:整个框架没有黑盒,出 bug 能自己跟进源码。
  • 可定制:想改一个行为?fork 下来一眼能看到改哪里。
  • 异常栈短:查询异常直接告诉你 SQL 在哪,不需要穿越 10 层代理。
  • 部署轻:jar 体积小、启动快、常驻内存低,适合微服务和工具类项目。

运行时单次查询性能与 MyBatis 相近——数据库才是瓶颈,框架层差距可以忽略。我们不在这个维度卷。


4. 查询结果自带深度取值

ResultMap result = DB.Table.select("user").eq("id", 1).queryOne();

result.getInt("age", 0);
result.getStr("profile.address.city", "未知");  // profile 是 JSON 字段
result.getList("orders", Order.class);          // orders 是 JSON 数组

ResultMap 继承自 JSONMapa.b.c 路径取值是原生能力,不用自己 JSON.parseObject 再一层层 .get


API 风格:显式 > 魔法

DLZ-DB 整个框架的审美是一致的:用显式的 lambda 和链式,对抗隐式的注解和代理。

// 条件判断:三参形式,不用写 if
.eq(name != null, "name", name)

// 嵌套逻辑:lambda 就地表达
.or(o -> o.like(User::getName, "关键词").like(User::getAddress, "关键词"))

// 数据源作用域:lambda 包起来
DB.Dynamic.use("other_db", () -> { ... });

// 空值自动忽略:SQL 里用方括号
[AND status = #{status}]

代码里能看见的控制流,才是真正可靠的控制流。


30 秒上手

DLZ-DB v7 采用多模块架构,可根据运行环境选择依赖:

模块 说明 适用场景
dlz-db-core 核心模块,零 Spring 依赖 手动集成、非 Spring 项目
dlz-db-spring-boot-starter Spring Boot 自动配置 Spring Boot 项目(推荐)
dlz-db-solon-plugin Solon 插件 Solon 项目

Spring Boot 快速开始

1. 引入依赖

<dependency>
    <groupId>top.dlzio</groupId>
    <artifactId>dlz-db-spring-boot-starter</artifactId>
    <version>7.0.0</version>
</dependency>

2. 配置数据源

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

# DLZ-DB 配置(可选)
dlz:
  db:
    logic-delete-field: is_deleted
    log:
      show-run-sql: true
      show-caller: true

3. 启用 DLZ-DB

@Configuration
@EnableConfigurationProperties(SpringDlzDbProperties.class)
public class DlzDbConfigs extends SpringDlzDbConfig {}

包路径:com.dlz.db.spring.config.SpringDlzDbConfigcom.dlz.db.spring.config.SpringDlzDbProperties

4. 开始使用

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private Integer isDeleted;      // 可选:存在即启用逻辑删除
    private Date createTime;
}

@RestController
public class UserController {
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return DB.Pojo.select(User.class).eq(User::getId, id).queryBean();
    }
}

没了。 不需要 Mapper,不需要 Service,不需要 XML。


Solon 快速开始

1. 引入依赖

<dependency>
    <groupId>top.dlzio</groupId>
    <artifactId>dlz-db-solon-plugin</artifactId>
    <version>7.0.0</version>
</dependency>

2. 配置

dlz:
  db:
    logic-delete-field: is_deleted
    log:
      show-run-sql: true
      show-caller: true

Solon 数据源配置(以 HikariCP 为例):

datasource:
  default:
    jdbcUrl: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
    driverClassName: com.mysql.cj.jdbc.Driver

3. 使用

Solon 下 API 完全一致,DB.Pojo/DB.Table/DB.Jdbc/DB.Sql 接口不变:

@Component
public class UserService {
    public User getUser(Long id) {
        return DB.Pojo.select(User.class).eq(User::getId, id).queryBean();
    }
}

Solon 事务使用 @Tran 注解:

@Tran
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    DB.Pojo.update(Account.class)
        .setSql("balance = balance - #{amount}", Params.of("amount", amount))
        .eq(Account::getId, fromId)
        .execute();
    DB.Pojo.update(Account.class)
        .setSql("balance = balance + #{amount}", Params.of("amount", amount))
        .eq(Account::getId, toId)
        .execute();
}

常见操作速览

// 查询
User u     = DB.Pojo.select(User.class).eq(User::getId, 1).queryBean();
List<User> list = DB.Pojo.select(User.class).eq(User::getStatus, 1).queryBeanList();
Page<User> page = DB.Pojo.select(User.class)
        .setPage(Page.build(1, 10, Order.desc("create_time")))
        .queryBeanPage();

// 插入
DB.Pojo.insert(user);
DB.Batch.insert(users, 100);

// 更新
DB.Pojo.update(user).eq(User::getId, id).execute();
DB.Pojo.update(User.class).set(User::getName, "新名字").eq(User::getId, id).execute();

// 删除(有 isDeleted 字段自动走逻辑删除)
DB.Pojo.delete(User.class).eq(User::getId, id).execute();

// 预设 SQL(xml / db 中定义,key 以 "key." 开头)
List<User> users = DB.Sql.select("key.user.find")
        .addPara("status", 1)
        .queryList(User.class);

六个入口,职责分工清晰

主操作入口(按 SQL 风格选一个)
├─ DB.Pojo   ← 有 Bean 时首选,链式 + Lambda,类型安全
├─ DB.Table  ← 动态表名场景,不需要 Bean
├─ DB.Jdbc   ← 一行搞定的简单 SQL,? 占位符,秒迁 JdbcTemplate
└─ DB.Sql    ← 复杂 / 动态 / 可复用 SQL,#{} 占位符 + 预设 SQL

正交能力(任何时候可叠加)
├─ DB.Batch    ← 批量写入
└─ DB.Dynamic  ← 数据源切换作用域

对 AI 也友好

  • 入口收敛到 DB.,决策树很浅。
  • 条件方法统一 (condition, field, value) 三参形式,特例少。
  • 返回值有机械规则:Bean → Bean,不带 → Map,带 (Class) → 指定类型
  • 整个使用规范可以压进 1000 token 以内塞给 AI(见 docs/第05章-AI辅助/5.1-AI速读.md)。

常见问题

Q:复杂 SQL 怎么写?

// 原生 SQL
DB.Jdbc.select("复杂的SQL语句 where id=?", id).queryList();

// 预设 SQL
DB.Sql.select("key.复杂查询").addPara("x", 1).queryList();

// 条件构造器 + 自定义片段
DB.Pojo.select(User.class)
        .eq(User::getStatus, 1)
        .sql("EXISTS (SELECT 1 FROM vip WHERE user_id=t.id AND level>=#{lv})",
             new JSONMap("lv", 3))
        .queryBeanList();

Q:如何调试 SQL?

打开 dlz.db.log.show-run-sql=true,日志会直接显示:

  1. 完整的可执行 SQL(参数已填充,可直接复制执行)
  2. 执行耗时
  3. 调用代码位置(IDE 中可点击跳转)

Q:性能如何?

底层直通 JDBC,无额外反射/代理损耗。与 MyBatis 运行时接近,不是我们的主要卖点——我们卖的是简单和可控,不是性能。

Q:能和现有 MyBatis / MP 项目共存吗?

可以。DLZ-DB 不依赖 MyBatis 体系,两者走各自的数据源和连接即可。迁移可以渐进——新模块用 DLZ-DB,老模块保持不动。

Q:v7 和 v6 的 API 兼容吗?

DB.Pojo/DB.Table/DB.Jdbc/DB.Sql 等核心 API 完全兼容。但 Maven 坐标、配置类包路径有变更,详见 6.2-v6升级到v7


文档导航

快速上手

基础操作

高级特性

框架集成

迁移与升级

其他

English Documentation


License

Apache License 2.0 © DLZ KIT


**简单的事情简单做,复杂的事情也能简单做。** 如果觉得有帮助,请点个 ⭐ Star 支持一下!

About

DLZ-DB 一个不到 7000 行代码的 Java 数据库框架,让你写 SQL 像写本地代码一样直接。

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors