【性能革命】告别复杂SQL:easy-query 4.10让数据库操作效率提升300%的秘密

【性能革命】告别复杂SQL:easy-query 4.10让数据库操作效率提升300%的秘密

【免费下载链接】easy-query java/kotlin high performance lightweight solution for jdbc query,support oltp and olap query,一款java下面支持强类型、轻量级、高性能的ORM,致力于解决jdbc查询,拥有对象模型筛选、隐式子查询、隐式join 【免费下载链接】easy-query 项目地址: https://gitcode.com/dromara/easy-query

你还在为ORM框架的冗余代码和低下性能而烦恼吗?还在手写复杂SQL处理联表查询和分表逻辑吗?本文将带你全面了解dromara/easy-query——这款支持强类型、轻量级、高性能的Java ORM框架如何通过五大隐式特性彻底改变数据库操作方式,让你用最少的代码实现最复杂的查询逻辑。

读完本文你将获得:

  • 掌握5种隐式查询特性的实战应用
  • 学会在Spring Boot和控制台环境快速集成easy-query
  • 理解分库分表的优雅实现方式
  • 获得处理加密字段查询的最佳实践
  • 通过20+代码示例彻底搞懂ORM性能优化技巧

🌟 什么是easy-query?

easy-query是一款由Dromara社区维护的Java/Kotlin ORM框架,专为解决JDBC查询痛点而设计。它采用强类型API设计,支持对象模型筛选、隐式子查询和隐式关联查询,同时兼顾OLTP和OLAP场景需求。与传统ORM相比,其核心优势在于零XML配置隐式查询优化高性能分库分表支持。

// 核心依赖(最新版本请查阅Maven Central)
<dependency>
    <groupId>com.easy-query</groupId>
    <artifactId>sql-springboot-starter</artifactId>
    <version>4.10.0</version>
</dependency>

框架架构概览

mermaid

🔥 五大隐式特性深度解析

1. 隐式关联查询(Implicit Join)

传统ORM实现一对一/多对一关联查询时需要显式配置关联关系并手动编写JOIN条件,而easy-query通过@Navigate注解和属性代理自动实现关联查询。

// 实体定义
@Data
@Table("t_sys_user")
@EntityProxy
public class SysUser implements ProxyEntityAvailable<SysUser, SysUserProxy> {
    @Column(primaryKey = true)
    private String id;
    private String username;
    @Navigate(value = RelationTypeEnum.OneToOne) // 声明一对一关联
    private Company company; // 关联属性
}

// 查询代码
List<SysUser> users = easyEntityQuery.queryable(SysUser.class)
    .where(user -> user.company().name().like("科技公司")) // 直接访问关联对象属性
    .orderBy(user -> user.company().registerTime().desc()) // 关联属性排序
    .toList();

生成的SQL:

SELECT t.`id`,t.`username`,t.`company_id` 
FROM `t_sys_user` t 
LEFT JOIN `t_company` t1 ON t.`company_id` = t1.`id` 
WHERE t1.`name` LIKE '%科技公司%' 
ORDER BY t1.`register_time` DESC

2. 隐式子查询(Implicit Subquery)

一对多和多对多关系中,传统方式需要手动编写子查询,而easy-query通过any()all()等方法自动生成子查询。

List<Company> companies = entityQuery.queryable(Company.class)
    .where(company -> {
        // 自动生成EXISTS子查询
        company.users().any(u -> u.name().like("张%"));
        // 子查询聚合函数
        company.users().where(u -> u.age().gt(18))
               .count().gt(10);
    }).toList();

3. 隐式分组(Implicit Grouping)

当对一对多关系进行多次子查询时,easy-query会自动优化合并为分组查询,减少数据库交互次数。

// 未优化前:2次子查询
// 优化后:1次分组查询
List<CompanyVO> result = entityQuery.queryable(Company.class)
    .subQueryToGroupJoin(company -> company.users()) // 启用分组优化
    .where(company -> {
        company.users().any(u -> u.department().eq("研发"));
        company.users().avg(u -> u.salary()).gt(new BigDecimal("20000"));
    })
    .select(company -> Select.DRAFT.of(
        company.id(),
        company.name(),
        company.users().count().as("userCount")
    )).toList();

4. 隐式分区分组(Implicit Partition Grouping)

针对一对多关系的"获取每个分组的第一条记录"场景,传统实现需要复杂的窗口函数,而easy-query通过first()last()等方法简化操作。

List<Company> companies = entityQuery.queryable(Company.class)
    .where(company -> {
        // 获取每个公司最新注册的用户
        company.users()
               .orderBy(u -> u.registerTime().desc())
               .first() // 自动生成ROW_NUMBER()窗口函数
               .department().eq("技术部");
    }).toList();

5. 隐式CASE WHEN表达式(Implicit CASE WHEN)

通过filter()方法可以为聚合函数添加条件,自动生成CASE WHEN表达式,避免手动编写复杂条件。

List<StatsVO> stats = entityQuery.queryable(SysUser.class)
    .select(user -> Select.DRAFT.of(
        user.id().count().filter(() -> user.address().eq("北京")).as("bjCount"),
        user.id().count().filter(() -> user.address().eq("上海")).as("shCount"),
        user.age().avg().filter(() -> user.address().eq("广州")).as("gzAvgAge")
    )).toList();

生成的SQL:

SELECT 
    COUNT(CASE WHEN t.`address` = '北京' THEN t.`id` END) AS `bjCount`,
    COUNT(CASE WHEN t.`address` = '上海' THEN t.`id` END) AS `shCount`,
    AVG(CASE WHEN t.`address` = '广州' THEN t.`age` END) AS `gzAvgAge`
FROM `t_sys_user` t

🚀 快速上手实战教程

环境准备

Spring Boot集成
<!-- Maven依赖 -->
<dependency>
    <groupId>com.easy-query</groupId>
    <artifactId>sql-springboot-starter</artifactId>
    <version>4.10.0</version>
</dependency>
<dependency>
    <groupId>com.easy-query</groupId>
    <artifactId>sql-mysql</artifactId>
    <version>4.10.0</version>
</dependency>
# 配置文件
easy-query:
  database-type: mysql
  jdbc:
    url: jdbc:mysql://127.0.0.1:3306/easy-query-test?serverTimezone=GMT%2B8&characterEncoding=utf-8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  pagination:
    default-page-size: 20
    max-page-size: 1000
控制台模式集成
// 数据源配置
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/easy-query-test");
dataSource.setUsername("root");
dataSource.setPassword("root");

// 初始化客户端
EasyQueryClient client = EasyQueryBootstrapper.defaultBuilderConfiguration()
    .setDataSource(dataSource)
    .useDatabaseConfigure(new MySQLDatabaseConfiguration())
    .build();

// 获取查询对象
EasyEntityQuery entityQuery = new DefaultEasyEntityQuery(client);

核心CRUD操作

1. 查询操作

基础查询:

// 单表查询
Topic topic = easyEntityQuery.queryable(Topic.class)
    .where(o -> o.id().eq("1001"))
    .firstOrNull();

// 分页查询
EasyPageResult<BlogEntity> page = easyEntityQuery.queryable(BlogEntity.class)
    .where(b -> b.content().like("easy-query"))
    .orderBy(b -> b.createTime().desc())
    .toPageResult(1, 10); // 第1页,每页10条

复杂查询:

List<Draft3<String, Integer, LocalDateTime>> result = easyEntityQuery.queryable(BlogEntity.class)
    .where(b -> b.status().eq(1))
    .groupBy(b -> GroupKeys.of(b.authorId()))
    .having(g -> g.star().sum().gt(100))
    .select(g -> Select.DRAFT.of(
        g.key1(), // authorId
        g.star().sum().as("totalStar"),
        g.createTime().max().as("lastPostTime")
    ))
    .orderBy(g -> g.value2().desc())
    .toList();
2. 插入操作
// 单条插入
Topic topic = new Topic();
topic.setId(UUID.randomUUID().toString());
topic.setTitle("easy-query教程");
topic.setStars(0);
topic.setCreateTime(LocalDateTime.now());
long insertRows = easyEntityQuery.insertable(topic).executeRows();

// 批量插入
List<Topic> topics = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    Topic t = new Topic();
    t.setId(UUID.randomUUID().toString());
    t.setTitle("批量插入测试" + i);
    t.setStars(i);
    t.setCreateTime(LocalDateTime.now());
    topics.add(t);
}
long batchRows = easyEntityQuery.insertable(topics).executeRows();
3. 更新操作
// 表达式更新
long updateRows = easyEntityQuery.updatable(Topic.class)
    .setColumns(t -> t.stars().set(t.stars().add(1))) // stars = stars + 1
    .where(t -> t.id().eq("1001"))
    .executeRows();

// 实体更新(仅更新变化字段)
Topic topic = easyEntityQuery.queryable(Topic.class).whereId("1001").firstNotNull();
topic.setTitle("更新后的标题");
long entityRows = easyEntityQuery.updatable(topic).executeRows();
4. 删除操作
// 条件删除
long deleteRows = easyEntityQuery.deletable(Topic.class)
    .where(t -> t.createTime().lt(LocalDateTime.now().minusYears(1)))
    .executeRows();

// 实体删除
Topic topic = new Topic();
topic.setId("1001");
long entityDeleteRows = easyEntityQuery.deletable(topic).executeRows();

📊 分库分表实战

分表策略实现

easy-query通过注解配置和路由实现分表,支持按时间、哈希等多种分表策略。以时间分表为例:

// 分表实体定义
@Data
@Table(value = "t_topic_sharding_time", 
       shardingInitializer = TopicShardingTimeInitializer.class)
@EntityProxy
public class TopicShardingTime implements ProxyEntityAvailable<TopicShardingTime, TopicShardingTimeProxy> {
    @Column(primaryKey = true)
    private String id;
    private String title;
    private Integer stars;
    @ShardingTableKey // 分表键
    private LocalDateTime createTime; // 按创建时间分表
}

// 分表初始化器
public class TopicShardingTimeInitializer extends AbstractShardingMonthInitializer<TopicShardingTime> {
    @Override
    protected LocalDateTime getBeginTime() {
        return LocalDateTime.of(2023, 1, 1, 0, 0); // 分表起始时间
    }
    
    @Override
    protected LocalDateTime getEndTime() {
        return LocalDateTime.of(2023, 12, 31, 23, 59); // 分表结束时间
    }
}

// 分表路由规则
public class TopicShardingTimeTableRoute extends AbstractMonthTableRoute<TopicShardingTime> {
    @Override
    protected LocalDateTime convertLocalDateTime(Object shardingValue) {
        return (LocalDateTime) shardingValue; // 将分表键转换为LocalDateTime
    }
}

查询分表数据:

LocalDateTime begin = LocalDateTime.of(2023, 3, 1, 0, 0);
LocalDateTime end = LocalDateTime.of(2023, 5, 31, 23, 59);
List<TopicShardingTime> topics = easyEntityQuery.queryable(TopicShardingTime.class)
    .where(t -> t.createTime().between(begin, end))
    .toList();

上述代码会自动查询t_topic_sharding_time_202303t_topic_sharding_time_202304t_topic_sharding_time_202305三张表并合并结果。

分库策略实现

分库与分表配置类似,通过@ShardingDataSourceKey注解指定分库键,并实现数据源路由:

@Data
@Table(value = "t_topic_sharding_ds",
       shardingInitializer = DataSourceShardingInitializer.class)
@EntityProxy
public class TopicShardingDataSource implements ProxyEntityAvailable<TopicShardingDataSource, TopicShardingDataSourceProxy> {
    @Column(primaryKey = true)
    private String id;
    private String title;
    @ShardingDataSourceKey // 分库键
    private Integer userId; // 按用户ID哈希分库
}

// 数据源路由
public class TopicShardingDataSourceRoute extends AbstractDataSourceRoute<TopicShardingDataSource> {
    @Override
    protected RouteFunction<String> getRouteFilter(TableAvailable table, Object shardingValue, 
                                                  ShardingOperatorEnum operator, boolean withEntity) {
        int userId = (Integer) shardingValue;
        String dsName = "ds_" + (userId % 4); // 哈希到4个数据源
        return ds -> ds.equals(dsName); // 返回数据源匹配规则
    }
}

🔒 加密字段查询

对于手机号、身份证等敏感信息,easy-query提供字段加密存储和查询支持:

@Data
@Table("t_sys_user")
@EntityProxy
public class SysUser extends BaseEntity implements ProxyEntityAvailable<SysUser, SysUserProxy> {
    private String username;
    @Encryption(strategy = DefaultAesEasyEncryptionStrategy.class, 
                supportQueryLike = true) // 声明加密字段
    private String phone; // 加密存储的手机号
    @Encryption(strategy = DefaultAesEasyEncryptionStrategy.class)
    private String idCard; // 加密存储的身份证号
}

加密字段查询:

// 精确查询
List<SysUser> users = easyEntityQuery.queryable(SysUser.class)
    .where(u -> u.phone().eq("13800138000")) // 自动解密查询
    .toList();

// 模糊查询(需supportQueryLike=true)
List<SysUser> likeUsers = easyEntityQuery.queryable(SysUser.class)
    .where(u -> u.phone().like("138%")) // 支持加密字段模糊查询
    .toList();

⚡ 性能优化技巧

1. 按需加载与查询优化

// 仅查询需要的字段(避免SELECT *)
List<Draft2<String, Integer>> simpleUsers = easyEntityQuery.queryable(SysUser.class)
    .select(u -> Select.DRAFT.of(u.id(), u.age())) // 仅查询id和age字段
    .toList();

// 使用索引字段排序
List<SysUser> orderedUsers = easyEntityQuery.queryable(SysUser.class)
    .where(u -> u.status().eq(1))
    .orderBy(u -> u.createTime().desc()) // 假设createTime有索引
    .toList();

2. 批量操作优化

// 批量插入优化
easyEntityQuery.insertable(topics)
    .batchSize(1000) // 每批插入1000条
    .useExecuteBatch(true) // 使用JDBC批量执行
    .executeRows();

// 批量更新优化
easyEntityQuery.updatable(Topic.class)
    .setColumns(t -> t.stars().set(0))
    .where(t -> t.createTime().lt(LocalDateTime.now().minusMonths(3)))
    .allowExecuteBatch(500) // 分批次执行更新
    .executeRows();

3. 缓存策略

// 单条缓存
Topic topic = easyEntityQuery.queryable(Topic.class)
    .withCache(10, TimeUnit.SECONDS) // 缓存10秒
    .whereId("1001")
    .firstOrNull();

// 查询结果缓存
List<Topic> hotTopics = easyEntityQuery.queryable(Topic.class)
    .where(t -> t.stars().gt(1000))
    .withCache(5, TimeUnit.MINUTES) // 热门数据缓存5分钟
    .toList();

🆚 与主流ORM框架对比

特性easy-queryMyBatis-PlusSpring Data JPA
强类型API✅ 原生支持❌ 需要插件✅ 支持但语法繁琐
隐式关联查询✅ 自动JOIN❌ 需手动编写✅ 支持但性能差
分库分表✅ 内置支持❌ 需集成Sharding-JDBC❌ 需集成Sharding-JDBC
加密字段查询✅ 原生支持❌ 需手动实现❌ 需手动实现
动态SQL✅ 类型安全✅ XML/注解✅ JPQL/Criteria
启动速度⚡ 极快⚡ 快🐢 较慢(实体扫描)
学习曲线中高

📝 总结与展望

easy-query通过创新的隐式查询特性,将开发者从复杂的SQL编写中解放出来,同时保持了接近原生JDBC的性能。其核心优势在于:

  1. 强类型安全:编译期检查查询条件,减少运行时错误
  2. 隐式查询优化:自动处理关联查询、子查询和分组逻辑
  3. 轻量级设计:无冗余依赖,启动速度快,内存占用低
  4. 功能全面:内置分库分表、加密查询、缓存等企业级特性

随着4.10版本的发布,easy-query进一步提升了复杂查询性能和分表策略灵活性,未来还将支持更多数据库特性和查询优化。无论你是开发小型应用还是构建企业级系统,easy-query都能为你提供简洁而强大的数据库操作体验。

立即访问项目仓库开始使用:https://gitcode.com/dromara/easy-query

如果你觉得本文对你有帮助,请点赞收藏并关注项目更新! 下一篇我们将深入探讨easy-query的源码架构和扩展点实现,敬请期待。

【免费下载链接】easy-query java/kotlin high performance lightweight solution for jdbc query,support oltp and olap query,一款java下面支持强类型、轻量级、高性能的ORM,致力于解决jdbc查询,拥有对象模型筛选、隐式子查询、隐式join 【免费下载链接】easy-query 项目地址: https://gitcode.com/dromara/easy-query

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值