{"id":25569610,"url":"https://github.com/dromara/easy-query","last_synced_at":"2026-05-16T03:23:42.283Z","repository":{"id":94656754,"uuid":"541873766","full_name":"dromara/easy-query","owner":"dromara","description":"java/kotlin high performance lightweight solution for jdbc query,support oltp and olap query,一款java下面支持强类型、轻量级、高性能的ORM,致力于解决jdbc查询,拥有对象模型筛选、隐式子查询、隐式join","archived":false,"fork":false,"pushed_at":"2026-01-31T02:58:27.000Z","size":22863,"stargazers_count":740,"open_issues_count":62,"forks_count":70,"subscribers_count":15,"default_branch":"main","last_synced_at":"2026-01-31T04:58:37.939Z","etag":null,"topics":["java","jdbc","join","kotlin","mysql","orm","sharding"],"latest_commit_sha":null,"homepage":"http://www.easy-query.com","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dromara.png","metadata":{"files":{"readme":"README-zh.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":"2022-09-27T02:42:07.000Z","updated_at":"2026-01-31T02:58:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"310ccaa1-8404-4948-9063-99c0e219cf9a","html_url":"https://github.com/dromara/easy-query","commit_stats":null,"previous_names":["dromara/easy-query","xuejmnet/easy-query"],"tags_count":290,"template":false,"template_full_name":null,"purl":"pkg:github/dromara/easy-query","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Feasy-query","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Feasy-query/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Feasy-query/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Feasy-query/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dromara","download_url":"https://codeload.github.com/dromara/easy-query/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Feasy-query/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28931043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T04:05:25.756Z","status":"ssl_error","status_checked_at":"2026-01-31T04:02:35.005Z","response_time":128,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["java","jdbc","join","kotlin","mysql","orm","sharding"],"created_at":"2025-02-21T00:02:31.958Z","updated_at":"2026-01-31T06:01:18.354Z","avatar_url":"https://github.com/dromara.png","language":"Java","funding_links":[],"categories":["数据库开发"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg height=\"340\" src=\"./imgs/logo.png\"\u003e\n\u003c/p\u003e\n\n# [English](https://github.com/dromara/easy-query/blob/main/README.md) | 中文\n\n\u003cp align=\"center\"\u003e\n    \u003ca target=\"_blank\" href=\"https://central.sonatype.com/search?q=easy-query\"\u003e\n        \u003cimg src=\"https://img.shields.io/maven-central/v/com.easy-query/easy-query-all?label=Maven%20Central\" alt=\"Maven\" /\u003e\n    \u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.apache.org/licenses/LICENSE-2.0.txt\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/:license-Apache2-blue.svg\" alt=\"Apache 2\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-8-green.svg\" alt=\"jdk-8\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-11-green.svg\" alt=\"jdk-11\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-17-green.svg\" alt=\"jdk-17\" /\u003e\n\t\u003c/a\u003e\n    \u003cbr /\u003e\n        \u003cimg src=\"https://img.shields.io/badge/SpringBoot-v2.x-blue\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/SpringBoot-v3.x-blue\"\u003e\n        \u003ca target=\"_blank\" href='https://gitee.com/noear/solon'\u003e\u003cimg src=\"https://img.shields.io/badge/Solon-v2.x-blue\"\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003ca target=\"_blank\" href='https://gitee.com/xuejm/easy-query'\u003e\n\t\t\u003cimg src='https://gitee.com/xuejm/easy-query/badge/star.svg' alt='Gitee star'/\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href='https://github.com/xuejmnet/easy-query'\u003e\n\t\t\u003cimg src=\"https://img.shields.io/github/stars/xuejmnet/easy-query.svg?logo=github\" alt=\"Github star\"/\u003e\n\t\u003c/a\u003e\n      \u003ca href='https://gitcode.com/dromara/easy-query'\u003e\n        \u003cimg src='https://gitcode.com/dromara/easy-query/star/badge.svg' alt='fork'\u003e\n      \u003c/a\u003e\n\u003c/p\u003e\n\n\n- [GITHUB](https://github.com/dromara/easy-query)  GITHUB镜像地址\n- [GITEE](https://gitee.com/dromara/easy-query)  GITEE镜像地址\n\n## 📚 文档\n\u003cdiv align=\"center\"\u003e\n\n[GITHUB文档地址](https://xuejmnet.github.io/easy-query-doc/) | [官方 文档地址](https://www.easy-query.com/easy-query-doc/)\n\n\u003c/div\u003e\n\n## 🐧 QQ群:170029046\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"./imgs/qrcode.jpg\" title=\"JetBrains\" width=122 /\u003e\n\n\u003c/div\u003e\n\n## 五大隐式 🔥🔥🔥\n- [x] 隐式join `OneToOne`、`ManyToOne` 自动实现join查询筛选、排序和结果获取\n- [x] 隐式子查询 `OneToMany`、`ManyToMany` 自动实现子查询查询筛选、排序和聚合函数结果获取\n- [x] 隐式分组 `OneToMany`、`ManyToMany` 自动实现子查询优化合并将多个子查询合并成一个分组查询支持筛选、排序和聚合函数结果获取\n- [x] 隐式分区分组 `OneToMany`、`ManyToMany` 自动实现第一个、第N个数据的筛选、排序和聚合函数结果获取\n- [x] 隐式CASE WHEN表达式 `属性.聚合函数.筛选`，`o.age().sum().filter(()-\u003eo.name().like(\"123\"))`\n\n`Company` OneToMany `SysUser`\n\n### 隐式join\n查询用户条件是用户所属企业是xx公司\n```java\n\nList\u003cSysUser\u003e userInXXCompany = entityQuery.queryable(SysUser.class)\n        .where(user -\u003e {\n            user.company().name().like(\"xx公司\");\n        })\n        .orderBy(user -\u003e {\n            user.company().registerMoney().desc();\n            user.birthday().asc();\n        }).toList();\n```\n\n\n### 隐式子查询\n查询企业条件是企业必须有小明这个用户并且小明这个用户是2000年1月1日之后出生\n```java\n\nList\u003cCompany\u003e companies = entityQuery.queryable(Company.class)\n        .where(company -\u003e {\n          company.users().any(u -\u003e u.name().like(\"小明\"));\n          company.users().where(u -\u003e u.name().like(\"小明\")).max(u -\u003e u.birthday()).gt(LocalDateTime.of(2000,1,1,0,0,0));\n        }).toList();\n```\n### 隐式分组\n查询企业条件是企业必须有小明这个用户并且小明这个用户是2000年1月1日之后出生\n\n合并两个子查询使用join group合并\n```java\n\nList\u003cCompany\u003e companies = entityQuery.queryable(Company.class)\n        //在where中的两个子查询会进行合并\n        .subQueryToGroupJoin(company -\u003e company.users())\n        .where(company -\u003e {\n          company.users().any(u -\u003e u.name().like(\"小明\"));\n          company.users().where(u -\u003e u.name().like(\"小明\")).max(u -\u003e u.birthday()).gt(LocalDateTime.of(2000,1,1,0,0,0));\n        }).toList();\n```\n\n### 隐式分区分组\n查询企业要求企业的所有员工年龄最小的那个是小明并且生日是2000年1月1日之前\n\n```java\n\nList\u003cCompany\u003e companies = entityQuery.queryable(Company.class)\n        .where(company -\u003e {\n          company.users().orderBy(u-\u003eu.birthday().desc()).first().name().eq(\"小明\");\n          company.users().orderBy(u-\u003eu.birthday().desc()).element(0).birthday().lt(LocalDateTime.of(2000,1,1,0,0,0));\n        }).toList();\n```\n\n### 隐式CASE WHEN表达式\n```java\n\nList\u003cDraft2\u003cLocalDateTime, Long\u003e\u003e customVO = entityQuery.queryable(SysUser.class)\n        .where(user -\u003e {\n            user.birthday().lt(LocalDateTime.now());\n        }).groupBy(user -\u003e GroupKeys.of(user.companyId()))\n        .select(group -\u003e Select.DRAFT.of(\n                group.groupTable().birthday().max().filter(() -\u003e {\n                    group.groupTable().name().like(\"小明\");\n                }),\n                group.groupTable().id().count().filter(() -\u003e {\n                    group.groupTable().birthday().ge(LocalDateTime.of(2024, 1, 1, 0, 0));\n                })\n        )).toList();\n\nList\u003cDraft3\u003cLong, Long, BigDecimal\u003e\u003e result = entityQuery.queryable(SysUser.class)\n        .where(user -\u003e {\n          user.birthday().lt(LocalDateTime.now());\n        })\n        .select(user -\u003e Select.DRAFT.of(\n                user.id().count().filter(() -\u003e {\n                  user.address().eq(\"Hangzhou\");\n                }),\n                user.id().count().filter(() -\u003e {\n                  user.address().eq(\"Beijing\");\n                }),\n                user.age().avg().filter(() -\u003e {\n                  user.address().eq(\"Beijing\");\n                })\n        )).toList();\n```\n\n\n## 单表完整案例\n首先我们来看一下完整版本的单表查询,涉及到筛选、聚合、聚合筛选、映射查询、排序\n```java\n\nList\u003cDraft3\u003cString, Integer, LocalDateTime\u003e\u003e myBlog = easyEntityQuery.queryable(BlogEntity.class)\n        .where(b -\u003e {\n            b.content().like(\"my blog\");\n        })\n        .groupBy(b -\u003e GroupKeys.of(b.title()))\n        .having(group -\u003e {\n            group.groupTable().star().sum().lt(10);\n        })\n        //select那么会将select和之前的表达式作为一个内嵌视图(t1表)进行包裹如果后续没有链式配置则会展开否则以内嵌视图(t1表)表示\n        .select(group -\u003e Select.DRAFT.of(\n                group.key1(),//value1\n                group.groupTable().star().sum().asAnyType(Integer.class),//value2\n                group.groupTable().createTime().max()//value3\n        ))\n        //如果不添加orderBy则不会生成内嵌视图(t1表)sql\n        //因为orderBy是对前面的select结果进行orderBy\n        .orderBy(group -\u003e group.value3().desc())\n        limit(2,2)//对结果进行限制返回\n        .toList();\n\n\n\n-- 第1条sql数据\nSELECT\n    t1.`value1` AS `value1`,\n    t1.`value2` AS `value2`,\n    t1.`value3` AS `value3` \nFROM\n    (SELECT\n        t.`title` AS `value1`,\n        SUM(t.`star`) AS `value2`,\n        MAX(t.`create_time`) AS `value3` \n    FROM\n        `t_blog` t \n    WHERE\n        t.`deleted` = false \n        AND t.`content` LIKE '%my blog%' \n    GROUP BY\n        t.`title` \n    HAVING\n        SUM(t.`star`) \u003c 10) t1 \nORDER BY\n    t1.`value3` DESC LIMIT 2,2\n```\n\n\n## 数据库支持\n\n| 数据库名称          | 包名            | springboot配置   | solon配置        |\n| ------------------- | --------------- | ---------------- | ---------------- |\n| MySQL               | sql-mysql       | mysql            | mysql            |\n| Oracle              | sql-oracle      | oracle           | oracle           |\n| PostgreSQL         | sql-pgsql       | pgsql            | pgsql            |\n| SqlServer           | sql-mssql       | mssql            | mssql            |\n| SqlServer RowNumber | sql-mssql       | mssql_row_number | mssql_row_number |\n| H2                  | sql-h2          | h2               | h2               |\n| SQLite              | sql-sqlite      | sqlite           | sqlite           |\n| ClickHouse          | sql-clickhouse  | clickhouse       | clickhouse       |\n| 达梦dameng          | sql-dameng      | dameng           | dameng           |\n| 人大金仓KingbaseES  | sql-kingbase-es | kingbase_es      | kingbase_es      |\n| 高斯  | sql-gauss-db | gauss-db      | gauss-db      |\n| duckdb  | sql-duckdb | duckdb      | duckdb      |\n| DB2  | sql-db2 | db2     | db2     |\n\n\n## 依赖\n### 使用代理\nentity对象添加注解 `@EntityProxy` 然后build project apt 将会自动生成代理对象的java代码\n```xml\n\n\u003cproperties\u003e\n  \u003ceasy-query.version\u003elast-version\u003c/easy-query.version\u003e\n\u003c/properties\u003e\n\u003cdependency\u003e\n\u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n\u003cartifactId\u003esql-api-proxy\u003c/artifactId\u003e\n\u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n\u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n\u003cartifactId\u003esql-mysql\u003c/artifactId\u003e\n\u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n## 🚀 介绍\n\n- [使用介绍](#使用介绍)\n    - [简介](#简介)\n    - [如何获取最新版本](#如何获取最新版本)\n    - [安装](#安装)\n- [开始](#开始)\n    - [单表查询](#单表查询)\n    - [多表查询](#多表查询)\n    - [复杂查询](#复杂查询)\n    - [动态表名](#动态表名)\n    - [新增](#新增)\n    - [修改](#修改)\n    - [删除](#删除)\n    - [联合查询](#联合查询)\n    - [子查询](#子查询)\n- [分片](#分片)\n    - [分表](#分表)\n    - [分库](#分库)\n- [捐赠](#捐赠)\n\n\n# 使用介绍\n`easy-query` 🚀 一款高性能、轻量级、多功能的Java/Kotlin对象查询ORM框架支持分库分表读写分离\n\n## 简介\n\n`easy-query`是一款无任何依赖的JAVA/Kotlin ORM\n框架，十分轻量，拥有非常高的性能，支持单表查询、多表查询、union、子查询、分页、动态表名、VO对象查询返回、逻辑删、全局拦截、数据库列加密(\n支持高性能like查询)、数据追踪差异更新、乐观锁、多租户、自动分库、自动分表、读写分离，支持框架全功能外部扩展定制，拥有强类型表达式。\n\n\n## 如何获取最新版本\n\n[https://central.sonatype.com/](https://central.sonatype.com/) 搜索`com.easy-query`获取最新安装包\n\n## 安装\n以下是`spring-boot`环境和控制台模式的安装\n### spring-boot\n\n```xml\n\n\u003cproperties\u003e\n  \u003ceasy-query.version\u003elast-version\u003c/easy-query.version\u003e\n\u003c/properties\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n    \u003cartifactId\u003esql-springboot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n### console\n以mysql为例\n```xml\n\n\u003cproperties\u003e\n  \u003ceasy-query.version\u003elast-version\u003c/easy-query.version\u003e\n\u003c/properties\u003e\n        \u003c!--  not required support proxy 非必须  提供了代理模式支持apt模式以非lambda形式的强类型sql语法 --\u003e\n\u003cdependency\u003e\n\u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n\u003cartifactId\u003esql-api-proxy\u003c/artifactId\u003e\n\u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n        \u003c!--  not required support labda  提供了以java语法强类型,如果不引用也可以使用只是无法使用lambda表达式来表示属性只能用字符串 --\u003e\n\u003cdependency\u003e\n\u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n\u003cartifactId\u003esql-api4j\u003c/artifactId\u003e\n\u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n        \u003c!-- required databaase sql  这边以mysql为例 其实不需要添加下面的包也可以运行,指示默认的个别数据库行为语句没办法生成 --\u003e\n\u003cdependency\u003e\n\u003cgroupId\u003ecom.easy-query\u003c/groupId\u003e\n\u003cartifactId\u003esql-mysql\u003c/artifactId\u003e\n\u003cversion\u003e${easy-query.version}\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n\n```java\n//初始化连接池\n HikariDataSource dataSource=new HikariDataSource();\n         dataSource.setJdbcUrl(\"jdbc:mysql://127.0.0.1:3306/easy-query-test?serverTimezone=GMT%2B8\u0026characterEncoding=utf-8\u0026useSSL=false\u0026allowMultiQueries=true\u0026rewriteBatchedStatements=true\");\n         dataSource.setUsername(\"root\");\n         dataSource.setPassword(\"root\");\n         dataSource.setDriverClassName(\"com.mysql.cj.jdbc.Driver\");\n         dataSource.setMaximumPoolSize(20);\n//非强类型api\n         EasyQueryClient easyQueryClient=EasyQueryBootstrapper.defaultBuilderConfiguration()\n         .setDataSource(dataSource)\n         .useDatabaseConfigure(new MySQLDatabaseConfiguration())\n         .build();\n//强类型api\n         EasyQuery easyQuery=new DefaultEasyQuery(easyQueryClient);\n```\n\n\n\n# 开始\nsql脚本\n```sql\ncreate table t_topic\n(\n  id varchar(32) not null comment '主键ID'primary key,\n  stars int not null comment '点赞数',\n  title varchar(50) null comment '标题',\n  create_time datetime not null comment '创建时间'\n)comment '主题表';\n\ncreate table t_blog\n(\n  id varchar(32) not null comment '主键ID'primary key,\n  deleted tinyint(1) default 0 not null comment '是否删除',\n  create_by varchar(32) not null comment '创建人',\n  create_time datetime not null comment '创建时间',\n  update_by varchar(32) not null comment '更新人',\n  update_time datetime not null comment '更新时间',\n  title varchar(50) not null comment '标题',\n  content varchar(256) null comment '内容',\n  url varchar(128) null comment '博客链接',\n  star int not null comment '点赞数',\n  publish_time datetime null comment '发布时间',\n  score decimal(18, 2) not null comment '评分',\n  status int not null comment '状态',\n  `order` decimal(18, 2) not null comment '排序',\n  is_top tinyint(1) not null comment '是否置顶',\n  top tinyint(1) not null comment '是否置顶'\n)comment '博客表';\n```\n查询对象\n```java\n\n@Data\npublic class BaseEntity implements Serializable {\n  private static final long serialVersionUID = -4834048418175625051L;\n\n  @Column(primaryKey = true)\n  private String id;\n  /**\n   * 创建时间;创建时间\n   */\n  private LocalDateTime createTime;\n  /**\n   * Update时间;Update时间\n   */\n  private LocalDateTime updateTime;\n  /**\n   * 创建人;创建人\n   */\n  private String createBy;\n  /**\n   * Update人;Update人\n   */\n  private String updateBy;\n  /**\n   * 是否Delete;是否Delete\n   */\n  @LogicDelete(strategy = LogicDeleteStrategyEnum.BOOLEAN)\n  private Boolean deleted;\n}\n\n\n@Data\n@Table(\"t_topic\")\n@EntityProxy //or @EntityFileProxy\n@ToString\npublic class Topic implements ProxyEntityAvailable\u003cTopic , TopicProxy\u003e {\n\n  @Column(primaryKey = true)\n  private String id;\n  private Integer stars;\n  private String title;\n  private LocalDateTime createTime;\n\n}\n\n//The ProxyEntityAvailable interface can be quickly generated using the IDEA plugin EasyQueryAssistant.\n\n\n@Data\n@Table(\"t_blog\")\n@EntityProxy //or @EntityFileProxy\npublic class BlogEntity extends BaseEntity implements ProxyEntityAvailable\u003cBlogEntity , BlogEntityProxy\u003e{\n\n  /**\n   * 标题\n   */\n  private String title;\n  /**\n   * 内容\n   */\n  private String content;\n  /**\n   * 博客链接\n   */\n  private String url;\n  /**\n   * 点赞数\n   */\n  private Integer star;\n  /**\n   * 发布时间\n   */\n  private LocalDateTime publishTime;\n  /**\n   * 评分\n   */\n  private BigDecimal score;\n  /**\n   * 状态\n   */\n  private Integer status;\n  /**\n   * 排序\n   */\n  private BigDecimal order;\n  /**\n   * 是否置顶\n   */\n  private Boolean isTop;\n  /**\n   * 是否置顶\n   */\n  private Boolean top;\n\n}\n\n```\n## 单表查询\n```java\nTopic topic = easyEntityQuery\n                .queryable(Topic.class)\n                .where(o -\u003e o.id().eq(\"3\"))\n                .firstOrNull();        \n```\n```sql\n==\u003e Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE t.`id` = ? LIMIT 1\n==\u003e Parameters: 3(String)\n\u003c== Time Elapsed: 15(ms)\n\u003c== Total: 1     \n```\n\n## 多表查询\n```java\nTopic topic = entityQuery\n               .queryable(Topic.class)\n               .leftJoin(BlogEntity.class, (t, t1) -\u003e t.id().eq(t1.id()))\n               .where(o -\u003e {\n                    o.id().eq(\"3\");\n                    o.title().eq(\"4\");\n                })\n               .firstOrNull();\n```\n```sql\n==\u003e Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t LEFT JOIN `t_blog` t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t.`id` = ? LIMIT 1\n==\u003e Parameters: false(Boolean),3(String)\n\u003c== Time Elapsed: 2(ms)\n\u003c== Total: 1\n```\n\n## 复杂查询\njoin + group +分页\n```java\nEasyPageResult\u003cBlogEntity\u003e page = easyEntityQuery\n        .queryable(Topic.class)\n        .innerJoin(BlogEntity.class,(t1,t2)-\u003et1.id().eq(t2.id()))\n        .where((t1,t2)-\u003et2.title().isNotNull())\n        .groupBy((t1,t2)-\u003eGroupKeys.TABLE2.of(t2.id()))\n        .select(g-\u003e{\n              BlogEntityProxy r = new BlogEntityProxy();\n              r.id().set(g.key1());\n              r.score().set(g.sum(g.group().t2.score()));\n              return r;\n        })\n        .toPageResult(1, 20);\n```\n```sql\n==\u003e Preparing: SELECT COUNT(*) FROM (SELECT t1.`id` AS `id`,SUM(t1.`score`) AS `score` FROM `t_topic` t INNER JOIN `t_blog` t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t1.`title` IS NOT NULL GROUP BY t1.`id`) t2\n  ==\u003e Parameters: false(Boolean)\n\u003c== Time Elapsed: 4(ms)\n\u003c== Total: 1\n==\u003e Preparing: SELECT t1.`id` AS `id`,SUM(t1.`score`) AS `score` FROM `t_topic` t INNER JOIN `t_blog` t1 ON t1.`deleted` = ? AND t.`id` = t1.`id` WHERE t1.`title` IS NOT NULL GROUP BY t1.`id` LIMIT 20\n==\u003e Parameters: false(Boolean)\n\u003c== Time Elapsed: 2(ms)\n\u003c== Total: 20\n```\n\n## 动态表名\n```java\neasyEntityQuery.queryable(BlogEntity.class)\n        .asTable(a-\u003e\"aa_bb_cc\")\n        .where(o -\u003e o.id().eq(\"123\"))\n        .toList();\n     \n```\n```sql\n SELECT t.`id`,t.`create_time`,t.`update_time`,t.`create_by`,t.`update_by`,t.`deleted`,t.`title`,t.`content`,t.`url`,t.`star`,t.`publish_time`,t.`score`,t.`status`,t.`order`,t.`is_top`,t.`top` FROM `aa_bb_cc` t WHERE t.`deleted` = ? AND t.`id` = ?  \n```\n\n## 新增\n```java\n\nTopic topic = new Topic();\ntopic.setId(String.valueOf(0));\ntopic.setStars(100);\ntopic.setTitle(\"标题0\");\ntopic.setCreateTime(LocalDateTime.now().plusDays(i));\n\nlong rows = easyEntityQuery.insertable(topic).executeRows();\n```\n```sql\n\n//返回结果rows为1\n==\u003e Preparing: INSERT INTO `t_topic` (`id`,`stars`,`title`,`create_time`) VALUES (?,?,?,?) \n==\u003e Parameters: 0(String),100(Integer),标题0(String),2023-03-16T21:34:13.287(LocalDateTime)\n\u003c== Total: 1\n```\n\n## 修改\n```java\n//实体更新\n Topic topic = easyEntityQuery.queryable(Topic.class)\n        .where(o -\u003e o.id().eq(\"7\")).firstNotNull(\"未找到对应的数据\");\n        String newTitle = \"test123\" + new Random().nextInt(100);\n        topic.setTitle(newTitle);\n\nlong rows=easyQuery.updatable(topic).executeRows();\n```\n```sql\n==\u003e Preparing: UPDATE t_topic SET `stars` = ?,`title` = ?,`create_time` = ? WHERE `id` = ?\n==\u003e Parameters: 107(Integer),test12364(String),2023-03-27T22:05:23(LocalDateTime),7(String)\n\u003c== Total: 1\n```\n```java\n//表达式更新\nlong rows = easyEntityQuery.updatable(Topic.class)\n                    .setColumns(o-\u003e{\n                        o.stars().set(12);\n                    })\n                    .where(o-\u003eo.id().eq(\"2\"))\n                    .executeRows();\n//rows为1\neasyEntityQuery.updatable(Topic.class)\n        .setColumns(o-\u003e{\n            o.stars().set(12);\n        })\n        .where(o-\u003eo.id().eq(\"2\"))\n                    .executeRows(1,\"更新失败\");\n//判断受影响行数并且进行报错,如果当前操作不在事务内执行那么会自动开启事务!!!会自动开启事务!!!会自动开启事务!!!来实现并发更新控制,异常为:EasyQueryConcurrentException \n//抛错后数据将不会被更新\n```\n```sql\n==\u003e Preparing: UPDATE t_topic SET `stars` = ? WHERE `id` = ?\n==\u003e Parameters: 12(Integer),2(String)\n\u003c== Total: 1\n```\n\n## 删除\n\n```java\nlong l = easyQuery.deletable(Topic.class)\n                    .where(o-\u003eo.title().eq(\"title998\"))\n                    .executeRows();\n```\n```sql\n==\u003e Preparing: DELETE FROM t_topic WHERE `title` = ?\n==\u003e Parameters: title998(String)\n\u003c== Total: 1\n```\n```java\nTopic topic = easyQuery.queryable(Topic.class).whereId(\"997\").firstNotNull(\"未找到当前主题数据\");\nlong l = easyQuery.deletable(topic).executeRows();\n```\n```sql\n==\u003e Preparing: DELETE FROM t_topic WHERE `id` = ?\n==\u003e Parameters: 997(String)\n\u003c== Total: 1\n```\n\n## 联合查询\n```java\nQueryable\u003cTopic\u003e q1 = easyQuery\n                .queryable(Topic.class);\nQueryable\u003cTopic\u003e q2 = easyQuery\n        .queryable(Topic.class);\nQueryable\u003cTopic\u003e q3 = easyQuery\n        .queryable(Topic.class);\nList\u003cTopic\u003e list = q1.union(q2, q3).where(o -\u003e o.eq(Topic::getId, \"123321\")).toList();\n```\n```sql\n\n==\u003e Preparing: SELECT t1.`id`,t1.`stars`,t1.`title`,t1.`create_time` FROM (SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t UNION SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t UNION SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t) t1 WHERE t1.`id` = ?\n==\u003e Parameters: 123321(String)\n\u003c== Time Elapsed: 19(ms)\n\u003c== Total: 0\n```\n\n## 子查询\n### in子查询\n```java\nEntityQueryable\u003cStringProxy, String\u003e idQuery = easyEntityQuery.queryable(BlogEntity.class)\n        .where(o -\u003e o.id().eq(\"1\" ))\n        .select(o -\u003e new StringProxy(o.id()));\n\n        List\u003cTopic\u003e list1 = easyEntityQuery.queryable(Topic.class)\n        .where(o -\u003e o.id().in(idQuery))\n        .toList();\n```\n```sql\n==\u003e Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE t.`id` IN (SELECT t1.`id` FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ?)\n  ==\u003e Parameters: false(Boolean),1(String)\n\u003c== Time Elapsed: 6(ms)\n\u003c== Total: 1 \n```\n\n### exists子查询\n```java\n\nEntityQueryable\u003cBlogEntityProxy, BlogEntity\u003e where = easyEntityQuery.queryable(BlogEntity.class)\n        .where(o -\u003e o.id().eq(\"1\" ));\n\nList\u003cTopic\u003e list2 = easyEntityQuery.queryable(Topic.class)\n        .where(o -\u003e {\n        o.exists(() -\u003e where.where(q -\u003e q.id().eq(o.id())));\n        }).toList();\n```\n```sql\n==\u003e Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic` t WHERE EXISTS (SELECT 1 FROM `t_blog` t1 WHERE t1.`deleted` = ? AND t1.`id` = ? AND t1.`id` = t.`id`)\n==\u003e Parameters: false(Boolean),1(String)\n\u003c== Time Elapsed: 2(ms)\n\u003c== Total: 1\n```\n\n# 分片\n`easy-query`支持分表、分库、分表+分库\n## 分表\n```java\n//创建分片对象\n@Data\n@Table(value = \"t_topic_sharding_time\",shardingInitializer = TopicShardingTimeShardingInitializer.class)\n@ToString\npublic class TopicShardingTime {\n\n    @Column(primaryKey = true)\n    private String id;\n    private Integer stars;\n    private String title;\n    @ShardingTableKey\n    private LocalDateTime createTime;\n}\n//分片初始化器很简单 假设我们是2020年1月到2023年5月也就是当前时间进行分片那么要生成对应的分片表每月一张\npublic class TopicShardingTimeShardingInitializer extends AbstractShardingMonthInitializer\u003cTopicShardingTime\u003e {\n\n    @Override\n    protected LocalDateTime getBeginTime() {\n        return LocalDateTime.of(2020, 1, 1, 1, 1);\n    }\n\n    @Override\n    protected LocalDateTime getEndTime() {\n        return LocalDateTime.of(2023, 5, 1, 0, 0);\n    }\n\n\n    @Override\n    public void configure0(ShardingEntityBuilder\u003cTopicShardingTime\u003e builder) {\n\n////以下条件可以选择配置也可以不配置用于优化分片性能\n//        builder.paginationReverse(0.5,100)\n//                .ascSequenceConfigure(new TableNameStringComparator())\n//                .addPropertyDefaultUseDesc(TopicShardingTime::getCreateTime)\n//                .defaultAffectedMethod(false, ExecuteMethodEnum.LIST,ExecuteMethodEnum.ANY,ExecuteMethodEnum.COUNT,ExecuteMethodEnum.FIRST)\n//                .useMaxShardingQueryLimit(2,ExecuteMethodEnum.LIST,ExecuteMethodEnum.ANY,ExecuteMethodEnum.FIRST);\n\n    }\n}\n//分片时间路由规则按月然后bean分片属性就是LocalDateTime也可以自定义实现\npublic class TopicShardingTimeTableRoute extends AbstractMonthTableRoute\u003cTopicShardingTime\u003e {\n\n    @Override\n    protected LocalDateTime convertLocalDateTime(Object shardingValue) {\n        return (LocalDateTime)shardingValue;\n    }\n}\n\n```\n[数据库脚本参考源码](https://github.com/xuejmnet/easy-query/blob/main/sql-test/src/main/resources/mysql-init-sqk-easy-sharding.sql)\n\n其中`shardingInitializer`为分片初始化器用来初始化告诉框架有多少分片的表名(支持动态添加)\n\n`ShardingTableKey`表示哪个字段作为分片键(分片键不等于主键)\n\n执行sql\n```java\nLocalDateTime beginTime = LocalDateTime.of(2021, 1, 1, 1, 1);\nLocalDateTime endTime = LocalDateTime.of(2021, 5, 2, 1, 1);\nDuration between = Duration.between(beginTime, endTime);\nlong days = between.toDays();\nList\u003cTopicShardingTime\u003e list = easyQuery.queryable(TopicShardingTime.class)\n        .where(o-\u003eo.rangeClosed(TopicShardingTime::getCreateTime,beginTime,endTime))\n        .orderByAsc(o -\u003e o.column(TopicShardingTime::getCreateTime))\n        .toList();\n```\n```sql\n\n\n==\u003e SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202101` t WHERE t.`create_time` \u003e= ? AND t.`create_time` \u003c= ? ORDER BY t.`create_time` ASC\n==\u003e SHARDING_EXECUTOR_3, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202102` t WHERE t.`create_time` \u003e= ? AND t.`create_time` \u003c= ? ORDER BY t.`create_time` ASC\n==\u003e SHARDING_EXECUTOR_2, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)\n==\u003e SHARDING_EXECUTOR_3, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)\n\u003c== SHARDING_EXECUTOR_3, name:ds2020, Time Elapsed: 3(ms)\n\u003c== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 3(ms)\n==\u003e SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202103` t WHERE t.`create_time` \u003e= ? AND t.`create_time` \u003c= ? ORDER BY t.`create_time` ASC\n==\u003e SHARDING_EXECUTOR_3, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202104` t WHERE t.`create_time` \u003e= ? AND t.`create_time` \u003c= ? ORDER BY t.`create_time` ASC\n==\u003e SHARDING_EXECUTOR_2, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)\n==\u003e SHARDING_EXECUTOR_3, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)\n\u003c== SHARDING_EXECUTOR_3, name:ds2020, Time Elapsed: 2(ms)\n\u003c== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 2(ms)\n==\u003e main, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_time_202105` t WHERE t.`create_time` \u003e= ? AND t.`create_time` \u003c= ? ORDER BY t.`create_time` ASC\n==\u003e main, name:ds2020, Parameters: 2021-01-01T01:01(LocalDateTime),2021-05-02T01:01(LocalDateTime)\n\u003c== main, name:ds2020, Time Elapsed: 2(ms)\n\u003c== Total: 122\n```\n\n\n## 分库\n\n```java\n\n@Data\n@Table(value = \"t_topic_sharding_ds\",shardingInitializer = DataSourceAndTableShardingInitializer.class)\n@ToString\npublic class TopicShardingDataSource {\n\n    @Column(primaryKey = true)\n    private String id;\n    private Integer stars;\n    private String title;\n    @ShardingDataSourceKey\n    private LocalDateTime createTime;\n}\npublic class DataSourceShardingInitializer implements EntityShardingInitializer\u003cTopicShardingDataSource\u003e {\n    @Override\n    public void configure(ShardingEntityBuilder\u003cTopicShardingDataSource\u003e builder) {\n        EntityMetadata entityMetadata = builder.getEntityMetadata();\n        String tableName = entityMetadata.getTableName();\n        List\u003cString\u003e tables = Collections.singletonList(tableName);\n        LinkedHashMap\u003cString, Collection\u003cString\u003e\u003e initTables = new LinkedHashMap\u003cString, Collection\u003cString\u003e\u003e() {{\n            put(\"ds2020\", tables);\n            put(\"ds2021\", tables);\n            put(\"ds2022\", tables);\n            put(\"ds2023\", tables);\n        }};\n        builder.actualTableNameInit(initTables);\n\n\n    }\n}\n//分库数据源路由规则\npublic class TopicShardingDataSourceRoute extends AbstractDataSourceRoute\u003cTopicShardingDataSource\u003e {\n    @Override\n    protected RouteFunction\u003cString\u003e getRouteFilter(TableAvailable table, Object shardingValue, ShardingOperatorEnum shardingOperator, boolean withEntity) {\n        LocalDateTime createTime = (LocalDateTime) shardingValue;\n        String dataSource = \"ds\" + createTime.getYear();\n        switch (shardingOperator){\n            case GREATER_THAN:\n            case GREATER_THAN_OR_EQUAL:\n                return ds-\u003e dataSource.compareToIgnoreCase(ds)\u003c=0;\n            case LESS_THAN:\n            {\n                //如果小于月初那么月初的表是不需要被查询的\n                LocalDateTime timeYearFirstDay = LocalDateTime.of(createTime.getYear(),1,1,0,0,0);\n                if(createTime.isEqual(timeYearFirstDay)){\n                    return ds-\u003edataSource.compareToIgnoreCase(ds)\u003e0;\n                }\n                return ds-\u003edataSource.compareToIgnoreCase(ds)\u003e=0;\n            }\n            case LESS_THAN_OR_EQUAL:\n                return ds-\u003edataSource.compareToIgnoreCase(ds)\u003e=0;\n\n            case EQUAL:\n                return ds-\u003edataSource.compareToIgnoreCase(ds)==0;\n            default:return t-\u003etrue;\n        }\n    }\n}\n\n```\n\n```java\nLocalDateTime beginTime = LocalDateTime.of(2020, 1, 1, 1, 1);\nLocalDateTime endTime = LocalDateTime.of(2023, 5, 1, 1, 1);\nDuration between = Duration.between(beginTime, endTime);\nlong days = between.toDays();\nEasyPageResult\u003cTopicShardingDataSource\u003e pageResult = easyQuery.queryable(TopicShardingDataSource.class)\n        .orderByAsc(o -\u003e o.column(TopicShardingDataSource::getCreateTime))\n        .toPageResult(1, 33);\n```\n```sql\n\n==\u003e SHARDING_EXECUTOR_23, name:ds2022, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33\n==\u003e SHARDING_EXECUTOR_11, name:ds2021, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33\n==\u003e SHARDING_EXECUTOR_2, name:ds2020, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33\n==\u003e SHARDING_EXECUTOR_4, name:ds2023, Preparing: SELECT t.`id`,t.`stars`,t.`title`,t.`create_time` FROM `t_topic_sharding_ds` t ORDER BY t.`create_time` ASC LIMIT 33\n\u003c== SHARDING_EXECUTOR_4, name:ds2023, Time Elapsed: 4(ms)\n\u003c== SHARDING_EXECUTOR_23, name:ds2022, Time Elapsed: 4(ms)\n\u003c== SHARDING_EXECUTOR_2, name:ds2020, Time Elapsed: 4(ms)\n\u003c== SHARDING_EXECUTOR_11, name:ds2021, Time Elapsed: 6(ms)\n\u003c== Total: 33\n```\n\n## 捐赠\n\u003cimg src=\"./imgs/zfb.jpg\" title=\"JetBrains\" width=200 /\u003e\n\u003cimg src=\"./imgs/wx.jpg\" title=\"JetBrains\" width=222 /\u003e\n[博客](https://www.cnblogs.com/xuejiaming)\n\n\n个人QQ:326308290(欢迎技术支持提供您宝贵的意见)\n\n个人邮箱:326308290@qq.com","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdromara%2Feasy-query","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdromara%2Feasy-query","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdromara%2Feasy-query/lists"}