Mastering EasyQuery: A Strongly Typed Java ORM with Real‑World Query Examples
This article explains why the author created the EasyQuery ORM for Java, demonstrates a wide range of query patterns—including single‑record, pagination, joins, subqueries, streaming, custom VO mapping, dynamic conditions, grouping, native SQL, function columns, and high‑performance encryption—while providing complete code snippets and links to documentation and source repositories.
Background
After several years of using Java and searching for a .NET‑style ORM that allows intuitive, strongly‑typed SQL generation, the author found existing solutions like MyBatis‑Plus insufficient for many scenarios, especially when dealing with soft deletes, joins, and complex expressions. Consequently, a new ORM named EasyQuery was developed, inspired by the completeness of .NET ORM ecosystems and designed to bring fluent, type‑safe query building to Java.
Basic Queries
Querying the first record:
Topic topic = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "123"))
.firstOrNull();Querying a single record with an assertion:
Topic topic = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "123"))
.singleOrNull();Querying multiple records:
List<Topic> topics = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "123"))
.toList();Selecting specific columns:
Topic topic = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "1"))
.select(o -> o.column(Topic::getId).column(Topic::getTitle))
.firstOrNull();Paginated query:
EasyPageResult<Topic> page = easyQuery.queryable(Topic.class)
.where(o -> o.isNotNull(Topic::getId))
.toPageResult(1, 20);Advanced Queries
Transforming an expression into a nested anonymous table:
Queryable<Topic> query = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "1"))
.select(Topic.class, o -> o.column(Topic::getId).column(Topic::getTitle));
List<Topic> list = query.leftJoin(Topic.class, (t, t1) -> t.eq(t1, Topic::getId, Topic::getId))
.where((t, t1) -> {
t1.eq(Topic::getId, "123");
t.eq(Topic::getId, "456");
})
.toList();Subquery example:
Queryable<BlogEntity> sub = easyQuery.queryable(BlogEntity.class)
.where(o -> o.eq(BlogEntity::getId, "1"));
List<Topic> result = easyQuery.queryable(Topic.class)
.where(o -> o.exists(sub.where(q -> q.eq(o, BlogEntity::getId, Topic::getId))))
.toList();Multi‑table left join:
Topic topic = easyQuery.queryable(Topic.class)
.leftJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
.where(o -> o.eq(Topic::getId, "3"))
.firstOrNull();Streaming Large Result Sets
try (JdbcStreamResult<BlogEntity> stream = easyQuery.queryable(BlogEntity.class)
.where(o -> o.le(BlogEntity::getStar, 100))
.orderByAsc(o -> o.column(BlogEntity::getCreateTime))
.toStreamResult()) {
int i = 0;
for (BlogEntity blog : stream.getStreamIterable()) {
// assertions verifying each field against expected values
i++;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}Custom VO Mapping
List<QueryVO> list = easyQuery.queryable(Topic.class)
.leftJoin(BlogEntity.class, (t, t1) -> t.eq(t1, Topic::getId, BlogEntity::getId))
.leftJoin(SysUser.class, (t, t1, t2) -> t.eq(t2, Topic::getId, SysUser::getId))
.where(o -> o.eq(Topic::getId, "123"))
.where((t, t1, t2) -> t.eq(Topic::getId, "123").then(t1).like(BlogEntity::getTitle, "456").then(t2).eq(BaseEntity::getCreateTime, LocalDateTime.now()))
.select(QueryVO.class, (t, t1, t2) ->
t.column(Topic::getId)
.then(t1).columnAs(BlogEntity::getTitle, QueryVO::getField1)
.then(t2).columnAs(SysUser::getId, QueryVO::getField2)
)
.toList();Dynamic Form‑Based Conditions
BlogQuery2Request req = new BlogQuery2Request();
req.setContent("标题");
req.setPublishTimeEnd(LocalDateTime.now());
req.setStatusList(Arrays.asList(1, 2));
List<BlogEntity> list = easyQuery.queryable(BlogEntity.class)
.whereObject(req)
.toList();Basic Type Result
List<String> ids = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "1"))
.select(String.class, o -> o.column(Topic::getId))
.toList();Group By Queries
List<TopicGroupTestDTO> groups = easyQuery.queryable(Topic.class)
.where(o -> o.eq(Topic::getId, "3"))
.groupBy(o -> o.column(Topic::getId))
.select(TopicGroupTestDTO.class, o -> o.columnAs(Topic::getId, TopicGroupTestDTO::getId)
.columnCount(Topic::getId, TopicGroupTestDTO::getIdCount))
.toList();Native SQL Segment
String sql = easyQuery.queryable(H2BookTest.class)
.where(o -> o.sqlNativeSegment("regexp_like({0},{1})", it -> it
.expression(H2BookTest::getPrice)
.value("^Ste(v|ph)en$")))
.select(o -> o.columnAll())
.toSQL();Database Function Columns
EasyQuery can transparently handle columns that require database functions, such as Base64 decoding or PostgreSQL geographic types, without changing the Java model.
High‑Performance Encryption for LIKE
The library also provides encryption/decryption that works with the SQL LIKE operator, supporting both emoji and non‑emoji data.
Documentation: https://xuejm.gitee.io/easy-query-doc/guide/adv/column-sql-func-auto.html
Encryption guide: https://xuejm.gitee.io/easy-query-doc/guide/adv/column-encryption.html
Project Links
GitHub: https://github.com/xuejmnet/easy-query
Gitee: https://gitee.com/xuejm/easy-query
The article concludes with a brief thank‑you note and a source reference.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
