{"id":34568573,"url":"https://github.com/dino-proj/dino-sql-builder","last_synced_at":"2026-05-08T03:16:12.489Z","repository":{"id":321557008,"uuid":"1086182704","full_name":"dino-proj/dino-sql-builder","owner":"dino-proj","description":"Dino-dev的sql构建库","archived":false,"fork":false,"pushed_at":"2025-10-30T09:27:01.000Z","size":43,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-30T09:37:14.897Z","etag":null,"topics":["jdbc","mysql","postgresql","sql"],"latest_commit_sha":null,"homepage":"https://dinodev.cn/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dino-proj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-30T04:09:00.000Z","updated_at":"2025-10-30T09:27:05.000Z","dependencies_parsed_at":"2025-10-30T09:37:16.886Z","dependency_job_id":null,"html_url":"https://github.com/dino-proj/dino-sql-builder","commit_stats":null,"previous_names":["dino-proj/dino-sql-builder"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/dino-proj/dino-sql-builder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dino-proj%2Fdino-sql-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dino-proj%2Fdino-sql-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dino-proj%2Fdino-sql-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dino-proj%2Fdino-sql-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dino-proj","download_url":"https://codeload.github.com/dino-proj/dino-sql-builder/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dino-proj%2Fdino-sql-builder/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27999539,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jdbc","mysql","postgresql","sql"],"created_at":"2025-12-24T09:13:31.244Z","updated_at":"2026-05-08T03:16:12.481Z","avatar_url":"https://github.com/dino-proj.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cdiv\u003e\u003cimg src=\"./.assert/intro.svg\" style=\"max-height:300px\"  /\u003e\u003c/div\u003e\n\u003c/div\u003e\n\n## 🦖 Dino Sql Builder\n\nDino Sql Builder 是一个轻量级、类型安全的 Java SQL 构建工具，专为提升开发效率而设计。通过流畅的链式 API 和智能的方言系统，让 SQL 构建变得优雅而简单。\n\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)\n[![Java](https://img.shields.io/badge/Java-17%2B-orange.svg)](https://www.oracle.com/java/)\n\n---\n\n## ✨ 核心特性\n\n- **🔒 类型安全** - 强类型 API 设计，编译期发现错误，避免 SQL 注入\n- **⛓️ 链式调用** - 流畅的 Fluent API，代码可读性强，易于维护\n- **🎯 功能完整** - 支持 SELECT、INSERT、UPDATE、DELETE 以及复杂子查询\n- **🌐 多数据库** - 内置 MySQL、PostgreSQL 方言，轻松扩展其他数据库\n- **📐 命名转换** - 自动处理驼峰/下划线命名转换，无需手动适配\n- **🪶 零依赖** - 纯 Java 实现，无第三方依赖，轻量集成\n- **🧪 高测试覆盖** - 完善的单元测试，代码质量有保障\n\n---\n\n## 📦 安装\n\n### Maven\n\n在你的 `pom.xml` 中添加依赖：\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecn.dinodev\u003c/groupId\u003e\n  \u003cartifactId\u003edino-sql-builder\u003c/artifactId\u003e\n  \u003cversion\u003e3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```gradle\nimplementation 'cn.dinodev:dino-sql-builder:3.0'\n```\n\n---\n\n## 🚀 快速开始\n\n### 1. 初始化方言\n\n方言（Dialect）是数据库适配层，负责处理不同数据库的语法差异和命名转换。\n\n```java\nimport cn.dinodev.sql.dialect.MysqlDialect;\nimport cn.dinodev.sql.naming.CamelNamingConversition;\n\n// MySQL 方言，使用驼峰命名\nDialect dialect = new MysqlDialect(null, new CamelNamingConversition());\n\n// PostgreSQL 方言，使用下划线命名\nDialect pgDialect = new PostgreSQLDialect(metaData, new SnakeNamingConversition());\n```\n\n### 2. 构建简单查询\n\n```java\nimport cn.dinodev.sql.builder.SelectSqlBuilder;\n\n// 基本 SELECT\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"users\")\n    .column(\"id\", \"name\", \"email\")\n    .eq(\"status\", 1)\n    .gt(\"age\", 18)\n    .orderByDesc(\"created_at\")\n    .limit(10);\n\nString sql = builder.getSql();\n// SELECT id, name, email FROM users WHERE status = ? AND (age \u003e ?) ORDER BY created_at DESC LIMIT 10\n\nObject[] params = builder.getParams();\n// [1, 18]\n```\n\n---\n\n## 📖 使用示例\n\n### SELECT 查询\n\n#### 基本查询\n\n```java\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"users\")\n    .column(\"id\", \"name\", \"email\")\n    .eq(\"status\", 1)\n    .like(\"name\", \"张\")\n    .orderByAsc(\"name\")\n    .limit(20);\n```\n\n#### JOIN 查询\n\n```java\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"users\", \"u\")\n    .column(\"u.id\", \"u.name\", \"o.order_no\", \"o.amount\")\n    .leftJoin(\"orders\", \"o\", \"u.id = o.user_id\")\n    .eq(\"u.status\", 1)\n    .gt(\"o.amount\", 100)\n    .orderByDesc(\"o.created_at\");\n```\n\n#### GROUP BY 和聚合\n\n```java\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"orders\")\n    .column(\"user_id\", \"COUNT(*) AS order_count\", \"SUM(amount) AS total\")\n    .eq(\"status\", \"completed\")\n    .groupBy(\"user_id\")\n    .havingCountGt(5)  // HAVING COUNT(*) \u003e 5\n    .orderByDesc(\"total\")\n    .limit(10);\n```\n\n#### 子查询\n\n```java\n// 创建子查询\nSelectSqlBuilder subQuery = SelectSqlBuilder.create(dialect, \"orders\")\n    .column(\"user_id\")\n    .eq(\"status\", \"completed\")\n    .groupBy(\"user_id\")\n    .havingCountGt(10);\n\n// 在主查询中使用\nSelectSqlBuilder mainQuery = SelectSqlBuilder.create(dialect, \"users\")\n    .column(\"id\", \"name\")\n    .in(\"id\", subQuery);  // WHERE id IN (子查询)\n```\n\n#### UNION 查询\n\n```java\nSelectSqlBuilder query1 = SelectSqlBuilder.create(dialect, \"users\")\n    .column(\"name\", \"email\")\n    .eq(\"type\", \"admin\");\n\nSelectSqlBuilder query2 = SelectSqlBuilder.create(dialect, \"customers\")\n    .column(\"name\", \"email\")\n    .eq(\"vip\", 1);\n\n// UNION（去重）\nSelectSqlBuilder unionQuery = query1.union(query2);\n\n// UNION ALL（保留重复）\nSelectSqlBuilder unionAllQuery = query1.unionAll(query2);\n```\n\n#### 复杂综合查询\n\n```java\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"products\", \"p\")\n    .distinct()\n    .column(\"p.id\", \"p.name\", \"p.price\", \"c.name AS category_name\")\n    .leftJoin(\"categories\", \"c\", \"p.category_id = c.id\")\n    .eqIfNotNull(\"p.status\", 1)          // 条件为真才添加\n    .gtIfNotNull(\"p.price\", 100)\n    .in(\"p.category_id\", Arrays.asList(1, 2, 3))\n    .likeIfNotBlank(\"p.name\", \"手机\")    // 参数非空才添加\n    .isNotNull(\"p.inventory\")\n    .groupBy(\"p.id\", \"c.name\")\n    .havingCountGt(0)\n    .orderByDesc(\"p.price\")\n    .orderByAsc(\"p.name\")\n    .limitPage(2, 20);  // 第2页，每页20条\n\nString sql = builder.getSql();\nObject[] params = builder.getParams();\n```\n\n### INSERT 语句\n\n#### 基本插入\n\n```java\nInsertSqlBuilder builder = InsertSqlBuilder.create(dialect, \"users\")\n    .set(\"name\", \"张三\")\n    .set(\"age\", 25)\n    .set(\"email\", \"zhangsan@example.com\")\n    .setExpression(\"created_at\", \"NOW()\");  // 数据库函数\n\nString sql = builder.getSql();\n// INSERT INTO users (name, age, email, created_at) VALUES (?, ?, ?, NOW())\n\nObject[] params = builder.getParams();\n// [\"张三\", 25, \"zhangsan@example.com\"]\n```\n\n#### 批量插入\n\n```java\nInsertSqlBuilder builder = InsertSqlBuilder.create(dialect, \"users\")\n    .columns(\"name\", \"age\", \"email\")\n    .values(\"张三\", 25, \"zhangsan@example.com\")\n    .values(\"李四\", 30, \"lisi@example.com\")\n    .values(\"王五\", 28, \"wangwu@example.com\");\n\nString sql = builder.getSql();\n// INSERT INTO users (name, age, email) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)\n```\n\n#### 使用表达式\n\n```java\nInsertSqlBuilder builder = InsertSqlBuilder.create(dialect, \"users\")\n    .set(\"name\", \"张三\")\n    .set(\"status\", \"UPPER(?)\", \"active\")  // 自定义表达式\n    .setExpression(\"id\", \"UUID()\")\n    .setExpression(\"created_at\", \"CURRENT_TIMESTAMP\");\n```\n\n#### 条件插入\n\n```java\nString nickname = null;\nInsertSqlBuilder builder = InsertSqlBuilder.create(dialect, \"users\")\n    .set(\"name\", \"张三\")\n    .setIfNotNull(\"nickname\", nickname)  // nickname 为 null，不会插入\n    .setIfNotBlank(\"email\", \"\");         // email 为空字符串，不会插入\n```\n\n### UPDATE 语句\n\n#### 基本更新\n\n```java\nUpdateSqlBuilder builder = UpdateSqlBuilder.create(dialect, \"users\")\n    .set(\"name\", \"张三\")\n    .set(\"age\", 26)\n    .setExpression(\"updated_at\", \"NOW()\")\n    .eq(\"id\", 1);\n\nString sql = builder.getSql();\n// UPDATE users SET name = ?, age = ?, updated_at = NOW() WHERE id = ?\n```\n\n#### 表达式更新\n\n```java\nUpdateSqlBuilder builder = UpdateSqlBuilder.create(dialect, \"products\")\n    .increment(\"view_count\")        // view_count = view_count + 1\n    .decrement(\"stock\", 5)          // stock = stock - 5\n    .eq(\"id\", 100);\n```\n\n#### 关联更新（MySQL）\n\n```java\nUpdateSqlBuilder builder = UpdateSqlBuilder.create(dialect, \"orders\", \"o\")\n    .innerJoin(\"users\", \"u\", \"o.user_id = u.id\")\n    .set(\"o.status\", \"vip_processed\")\n    .eq(\"u.level\", \"VIP\")\n    .eq(\"o.status\", \"pending\");\n```\n\n#### 关联更新（PostgreSQL）\n\n```java\nUpdateSqlBuilder builder = UpdateSqlBuilder.create(pgDialect, \"orders\", \"o\")\n    .from(\"users\", \"u\")\n    .set(\"o.status\", \"vip_processed\")\n    .where(\"o.user_id = u.id\")\n    .eq(\"u.level\", \"VIP\")\n    .eq(\"o.status\", \"pending\");\n```\n\n#### 条件更新\n\n```java\nInteger newAge = null;\nString newEmail = \"newemail@example.com\";\n\nUpdateSqlBuilder builder = UpdateSqlBuilder.create(dialect, \"users\")\n    .setIfNotNull(\"age\", newAge)        // newAge 为 null，不会更新\n    .setIfNotBlank(\"email\", newEmail)   // newEmail 不为空，会更新\n    .eq(\"id\", 1);\n```\n\n### DELETE 语句\n\n#### 基本删除\n\n```java\nDeleteSqlBuilder builder = DeleteSqlBuilder.create(dialect, \"users\")\n    .eq(\"id\", 1);\n\nString sql = builder.getSql();\n// DELETE FROM users WHERE id = ?\n```\n\n#### 条件删除\n\n```java\nDeleteSqlBuilder builder = DeleteSqlBuilder.create(dialect, \"users\")\n    .eq(\"status\", 0)\n    .lt(\"last_login\", \"2023-01-01\")\n    .isNull(\"email\");\n\nString sql = builder.getSql();\n// DELETE FROM users WHERE status = ? AND (last_login \u003c ?) AND (email IS NULL)\n```\n\n#### 批量删除\n\n```java\nDeleteSqlBuilder builder = DeleteSqlBuilder.create(dialect, \"logs\")\n    .in(\"id\", Arrays.asList(1, 2, 3, 4, 5));\n```\n\n### WHERE 条件构建\n\nDino Sql Builder 提供了丰富的条件构建方法，支持各种比较操作符和逻辑组合。\n\n#### 基本条件\n\n```java\nbuilder\n    .eq(\"status\", 1)                    // status = ?\n    .ne(\"deleted\", 1)                   // deleted != ?\n    .gt(\"age\", 18)                      // age \u003e ?\n    .gte(\"score\", 60)                   // score \u003e= ?\n    .lt(\"price\", 100)                   // price \u003c ?\n    .lte(\"stock\", 10)                   // stock \u003c= ?\n    .like(\"name\", \"张\")                 // name LIKE ?  (自动添加 %)\n    .isNull(\"deleted_at\")               // deleted_at IS NULL\n    .isNotNull(\"email\")                 // email IS NOT NULL\n    .in(\"category_id\", Arrays.asList(1, 2, 3))  // category_id IN (?, ?, ?)\n    .notIn(\"status\", Arrays.asList(0, -1));     // status NOT IN (?, ?)\n```\n\n#### 条件方法（If 系列）\n\n只有在条件满足时才添加到 WHERE 子句：\n\n```java\nString name = \"张三\";\nInteger age = null;\nString email = \"\";\n\nbuilder\n    .eqIfNotNull(\"name\", name)      // name 不为 null，添加条件\n    .gtIfNotNull(\"age\", age)        // age 为 null，不添加\n    .likeIfNotBlank(\"email\", email) // email 为空串，不添加\n    .eqIf(status == 1, \"type\", \"VIP\");  // 自定义条件判断\n```\n\n#### BETWEEN 条件\n\n```java\nbuilder\n    .between(\"age\", 18, 65)         // age BETWEEN ? AND ?\n    .notBetween(\"price\", 0, 10);    // price NOT BETWEEN ? AND ?\n```\n\n#### 自定义条件\n\n```java\nbuilder\n    .where(\"age \u003e ? AND age \u003c ?\", 18, 65)\n    .where(\"DATE(created_at) = ?\", \"2024-01-01\")\n    .whereIf(needFilter, \"status = ?\", 1);  // 条件为真才添加\n```\n\n#### 逻辑组合（AND/OR）\n\n```java\n// 默认是 AND 连接\nbuilder\n    .eq(\"status\", 1)\n    .gt(\"age\", 18)\n    .like(\"name\", \"张\");\n// WHERE status = ? AND (age \u003e ?) AND (name LIKE ?)\n\n// 使用 OR 连接\nbuilder\n    .eq(\"status\", 1)\n    .or()  // 切换到 OR 模式\n    .eq(\"level\", \"VIP\")\n    .like(\"email\", \"@vip.com\");\n// WHERE status = ? OR (level = ?) OR (email LIKE ?)\n\n// 混合使用（需要注意逻辑）\nbuilder\n    .eq(\"status\", 1)\n    .and()  // 显式指定 AND\n    .gt(\"age\", 18)\n    .or()   // 切换到 OR\n    .eq(\"level\", \"VIP\");\n```\n\n### 高级特性\n\n#### GROUP BY ALL（PostgreSQL 17+）\n\nPostgreSQL 17 引入的便捷语法，自动将 SELECT 中的非聚合列添加到 GROUP BY：\n\n```java\n// 需要 PostgreSQL 17+ 方言\nPostgreSQLDialect pg17 = new PostgreSQLDialect(\n    DatabaseMetaDataMocks.createPostgreSQL(17),\n    new CamelNamingConversition()\n);\n\nSelectSqlBuilder builder = SelectSqlBuilder.create(pg17, \"sales\")\n    .column(\"region\", \"product\", \"COUNT(*) AS count\", \"SUM(amount) AS total\")\n    .gt(\"amount\", 100)\n    .groupByAll()  // 自动 GROUP BY region, product\n    .orderByDesc(\"total\");\n```\n\n#### WITH 子句（CTE）\n\n```java\n// 创建 CTE\nSelectSqlBuilder cte = SelectSqlBuilder.create(dialect, \"orders\")\n    .column(\"user_id\", \"COUNT(*) AS order_count\")\n    .eq(\"status\", \"completed\")\n    .groupBy(\"user_id\");\n\n// 使用 CTE\nSelectSqlBuilder mainQuery = SelectSqlBuilder.create(dialect, \"users\", \"u\")\n    .with(\"user_orders\", cte)\n    .column(\"u.name\", \"uo.order_count\")\n    .innerJoin(\"user_orders\", \"uo\", \"u.id = uo.user_id\")\n    .gt(\"uo.order_count\", 10);\n```\n\n#### 分页查询\n\n```java\n// LIMIT + OFFSET\nbuilder.limit(20).offset(40);  // 跳过40条，取20条\n\n// 直接使用页码\nbuilder.limitPage(3, 20);  // 第3页，每页20条（等同于 LIMIT 20 OFFSET 40）\n```\n\n#### COUNT 查询\n\n```java\nSelectSqlBuilder builder = SelectSqlBuilder.create(dialect, \"users\")\n    .column(\"id\", \"name\")\n    .eq(\"status\", 1)\n    .gt(\"age\", 18);\n\n// 获取 COUNT SQL（自动去除 ORDER BY 和 LIMIT）\nString countSql = builder.getCountSql();\n// SELECT count(1) AS cnt FROM users WHERE status = ? AND (age \u003e ?)\n```\n\n#### 类型转换（Type Cast）\n\n不同数据库有不同的类型转换语法，方言会自动处理：\n\n```java\n// MySQL: CAST(column AS type)\nbuilder.column(\"CAST(price AS DECIMAL(10,2))\");\n\n// PostgreSQL: column::type\nbuilder.column(\"price::NUMERIC(10,2)\");\n\n// 使用方言方法（推荐）\nString castExpr = dialect.typeCast(\"price\", \"DECIMAL(10,2)\");\nbuilder.column(castExpr + \" AS formatted_price\");\n```\n\n---\n\n## 🎨 方言系统\n\n方言（Dialect）是适配不同数据库的核心机制，负责处理：\n\n1. **SQL 语法差异** - 如类型转换、LIMIT/OFFSET 语法等\n2. **命名转换** - 驼峰与下划线命名自动转换\n3. **特性支持** - 如 PostgreSQL 的 RETURNING、MySQL 的 ON DUPLICATE KEY 等\n\n### 内置方言\n\n#### MySQL 方言\n\n```java\nimport cn.dinodev.sql.dialect.MysqlDialect;\nimport cn.dinodev.sql.naming.CamelNamingConversition;\n\nDialect mysql = new MysqlDialect(null, new CamelNamingConversition());\n```\n\n**特性：**\n- 支持 `LIMIT offset, count` 语法\n- 支持 JOIN 更新\n- 类型转换使用 `CAST(expr AS type)`\n\n#### PostgreSQL 方言\n\n```java\nimport cn.dinodev.sql.dialect.PostgreSQLDialect;\nimport cn.dinodev.sql.naming.SnakeNamingConversition;\n\nDialect postgres = new PostgreSQLDialect(metaData, new SnakeNamingConversition());\n```\n\n**特性：**\n- 支持 `LIMIT count OFFSET offset` 语法\n- 支持 FROM 子句更新\n- 类型转换使用 `expr::type`\n- PostgreSQL 17+ 支持 `GROUP BY ALL`\n\n### 命名转换策略\n\n#### 驼峰命名（CamelNamingConversition）\n\nJava 代码中的字段名保持驼峰风格，不做转换：\n\n```java\nNamingConversition camel = new CamelNamingConversition();\ncamel.columnName(\"userName\");  // userName\ncamel.tableName(\"userOrders\"); // userOrders\n```\n\n#### 下划线命名（SnakeNamingConversition）\n\n自动将驼峰转换为下划线风格：\n\n```java\nNamingConversition snake = new SnakeNamingConversition();\nsnake.columnName(\"userName\");  // user_name\nsnake.tableName(\"userOrders\"); // user_orders\n```\n\n---\n\n## 🔧 配置与扩展\n\n### 自定义方言\n\n如需支持其他数据库，实现 `Dialect` 接口：\n\n```java\npublic class OracleDialect implements Dialect {\n    private final NamingConversition naming;\n    \n    public OracleDialect(DatabaseMetaData metaData, NamingConversition naming) {\n        this.naming = naming;\n    }\n    \n    @Override\n    public String columnName(String name) {\n        return naming.columnName(name);\n    }\n    \n    @Override\n    public String tableName(String name) {\n        return naming.tableName(name);\n    }\n    \n    @Override\n    public String typeCast(String column, String type) {\n        return \"CAST(\" + column + \" AS \" + type + \")\";\n    }\n    \n    // 实现其他接口方法...\n}\n```\n\n### 自定义命名策略\n\n实现 `NamingConversition` 接口：\n\n```java\npublic class CustomNamingConversition implements NamingConversition {\n    @Override\n    public String columnName(String name) {\n        // 自定义列名转换逻辑\n        return name.toUpperCase();\n    }\n    \n    @Override\n    public String tableName(String name) {\n        // 自定义表名转换逻辑\n        return \"tbl_\" + name;\n    }\n}\n```\n\n---\n\n## 🧪 测试\n\n项目使用 JUnit 5 + JaCoCo 进行测试和覆盖率统计。\n\n### 运行测试\n\n```bash\n# 运行全部测试\nmvn test\n\n# 运行指定测试类\nmvn test -Dtest=SelectSqlBuilderTest\n\n# 生成覆盖率报告\nmvn clean test jacoco:report\n```\n\n覆盖率报告生成在 `target/site/jacoco/index.html`。\n\n### 测试工具类\n\n项目提供了测试辅助工具：\n\n```java\nimport static cn.dinodev.sql.testutil.SqlTestHelper.*;\n\n// 断言 SQL 语句\nassertSql(builder, \"测试描述\", \"期望的SQL\");\n\n// 断言 SQL 和参数\nassertSqlWithParams(builder, \"测试描述\", \"期望的SQL\", new Object[]{参数1, 参数2});\n```\n\n---\n\n## 📚 API 文档\n\n生成 Javadoc：\n\n```bash\nmvn javadoc:javadoc\n```\n\n文档生成在 `target/reports/apidocs/`。\n\n---\n\n## 🤝 贡献指南\n\n欢迎各种形式的贡献！\n\n### 贡献方式\n\n1. **报告问题** - 在 [Issues](https://github.com/dino-proj/dino-sql-builder/issues) 中提交 Bug 或功能建议\n2. **提交代码** - Fork 项目，提交 Pull Request\n3. **完善文档** - 改进 README、Javadoc 或示例代码\n4. **分享经验** - 在博客或社区分享使用心得\n\n### 开发流程\n\n1. Fork 本仓库\n2. 创建特性分支：`git checkout -b feature/amazing-feature`\n3. 提交代码：`git commit -m 'Add amazing feature'`\n4. 推送分支：`git push origin feature/amazing-feature`\n5. 提交 Pull Request\n\n### 代码规范\n\n- 遵循 Java 17+ 标准\n- 保持代码简洁，方法不超过 50 行\n- 添加完整的 Javadoc 注释\n- 编写单元测试，覆盖率不低于 80%\n- 所有测试必须通过\n\n---\n\n## 📝 更新日志\n\n### v2.1 (2024-01-04)\n\n- ✨ 新增 PostgreSQL 17+ `GROUP BY ALL` 支持\n- 🔧 优化 WHERE 条件构建逻辑\n- 📖 完善文档和示例\n- 🐛 修复已知问题\n\n### v2.0 (2024-12-01)\n\n- 🎉 全新重构的 API 设计\n- ✨ 增强的子句支持（WITH、UNION、HAVING 等）\n- 🌐 改进的方言系统\n- 🧪 完善的测试覆盖\n\n---\n\n## 🔗 相关链接\n\n- [官方文档](https://dinodev.cn/dino-sql-builder/)\n- [GitHub 仓库](https://github.com/dino-proj/dino-sql-builder)\n- [问题反馈](https://github.com/dino-proj/dino-sql-builder/issues)\n\n---\n\n## 📄 许可证\n\n本项目采用 [Apache-2.0](./LICENSE) 开源许可证。\n\n---\n\n## 🫶 引用\n\n如果本项目对您的研究或工作有帮助，请考虑引用：\n\n```bibtex\n@misc{Dino-Sql-Builder,\n  author = {Cody Lu},\n  title = {Dino-Sql-Builder: A Type-Safe SQL Builder for Java},\n  year = {2024},\n  publisher = {GitHub},\n  journal = {GitHub Repository},\n  howpublished = {\\url{https://github.com/dino-proj/dino-sql-builder}}\n}","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdino-proj%2Fdino-sql-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdino-proj%2Fdino-sql-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdino-proj%2Fdino-sql-builder/lists"}