1. 简介
Jinq 为Java数据库查询提供了直观且便捷的解决方案。本教程将探讨如何在Spring项目中配置Jinq,并通过简单示例展示其核心功能。
2. Maven依赖
在 pom.xml 中添加 Jinq依赖:
<dependency>
<groupId>org.jinq</groupId>
<artifactId>jinq-jpa</artifactId>
<version>2.0.1</version>
</dependency>
添加 Spring ORM依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>6.0.13</version>
</dependency>
测试时使用H2内存数据库,添加以下依赖:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
3. 理解Jinq
Jinq通过**Java Stream API** 提供流畅的API,使数据库查询更易读、更简洁。
按车型筛选汽车的示例:
jinqDataProvider.streamAll(entityManager, Car.class)
.where(c -> c.getModel().equals(model))
.toList();
Jinq会将上述代码高效转换为SQL查询:
select c.* from car c where c.model=?
由于使用类型安全API而非纯文本查询,这种方式更不易出错。此外,Jinq通过通用易读的表达式加速开发,但存在类型和操作限制。
3.1. 局限性
Jinq仅支持JPA基础类型和特定SQL函数。它通过将lambda操作映射为JPA数据类型和SQL函数来生成原生SQL,因此无法转换所有自定义类型或方法。
3.2. 支持的数据类型
✅ 支持的类型及方法:
- String – 仅支持 equals(), compareTo()
- 基本数据类型 – 算术运算
- 枚举和自定义类 – 仅支持 == 和 != 操作
- java.util.Collection – 支持 contains()
- Date API – 仅支持 equals(), before(), after()
⚠️ 注意:若需自定义Java对象到数据库对象的转换,需在Jinq中注册 AttributeConverter 的具体实现。
4. Spring集成Jinq
Jinq需要 EntityManager 实例获取持久化上下文。本教程采用Spring与Hibernate提供的 EntityManager 集成方案。
4.1. 仓库接口
Spring通过仓库模式管理实体。定义 CarRepository 接口,包含按车型查询汽车的方法:
public interface CarRepository {
Optional<Car> findByModel(String model);
}
4.2. 抽象基础仓库
创建基础仓库提供Jinq核心能力:
public abstract class BaseJinqRepositoryImpl<T> {
@Autowired
private JinqJPAStreamProvider jinqDataProvider;
@PersistenceContext
private EntityManager entityManager;
protected abstract Class<T> entityType();
public JPAJinqStream<T> stream() {
return streamOf(entityType());
}
protected <U> JPAJinqStream<U> streamOf(Class<U> clazz) {
return jinqDataProvider.streamAll(entityManager, clazz);
}
}
4.3. 实现仓库
Jinq仅需 EntityManager 实例和实体类型类。基于基础仓库实现 Car 仓库:
@Repository
public class CarRepositoryImpl
extends BaseJinqRepositoryImpl<Car> implements CarRepository {
@Override
public Optional<Car> findByModel(String model) {
return stream()
.where(c -> c.getModel().equals(model))
.findFirst();
}
@Override
protected Class<Car> entityType() {
return Car.class;
}
}
4.4. 装配 JinqJPAStreamProvider
添加Jinq提供者配置:
@Configuration
public class JinqProviderConfiguration {
@Bean
@Autowired
JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) {
return new JinqJPAStreamProvider(emf);
}
}
4.5. 配置Spring应用
配置Spring应用使用Hibernate和Jinq。参考 application.properties(使用H2内存数据库):
spring.datasource.url=jdbc:h2:~/jinq
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
5. 查询指南
Jinq提供直观选项定制SQL查询(如 select, where, joins 等),但受限于前述约束。
5.1. Where条件
where 子句支持多重过滤。按车型和描述筛选汽车:
stream()
.where(c -> c.getModel().equals(model)
&& c.getDescription().contains(desc))
.toList();
Jinq生成的SQL:
select c.model, c.description from car c where c.model=? and locate(?, c.description)>0
5.2. Select投影
仅需查询部分字段时使用 select 子句。Jinq提供最多8个值的 Tuple 类:
stream()
.select(c -> new Tuple3<>(c.getModel(), c.getYear(), c.getEngine()))
.toList()
生成的SQL:
select c.model, c.year, c.engine from car c
5.3. 关联查询
Jinq可解析实体间的一对一和多对一关系。在 Car 中添加制造商关联:
@Entity(name = "CAR")
public class Car {
//...
@ManyToOne
@JoinColumn(name = "name")
public Manufacturer getManufacturer() {
return manufacturer;
}
}
Manufacturer 实体包含汽车列表:
@Entity(name = "MANUFACTURER")
public class Manufacturer {
// ...
@OneToMany(mappedBy = "model")
public List<Car> getCars() {
return cars;
}
}
查询指定车型的制造商:
Optional<Manufacturer> manufacturer = stream()
.where(c -> c.getModel().equals(model))
.select(c -> c.getManufacturer())
.findFirst();
Jinq自动使用内连接:
select m.name, m.city from car c inner join manufacturer m on c.name=m.name where c.model=?
需控制多对多等复杂关系时,使用 join 方法:
List<Pair<Manufacturer, Car>> list = streamOf(Manufacturer.class)
.join(m -> JinqStream.from(m.getCars()))
.toList()
左外连接则使用 leftOuterJoin 方法。
5.4. 聚合操作
除 toList 和 findFirst 外,Jinq支持多种聚合方法。统计指定车型的汽车数量:
long total = stream()
.where(c -> c.getModel().equals(model))
.count()
生成的SQL:
select count(c.model) from car c where c.model=?
还支持 sum, average, min, max 等,可组合不同聚合操作。
5.5. 分页查询
使用 skip 和 limit 实现分页。跳过前10条并获取20条记录:
stream()
.skip(10)
.limit(20)
.toList()
生成的SQL:
select c.* from car c limit ? offset ?
6. 总结
本文介绍了Spring与Jinq的集成方案(基于Hibernate最小配置),并简要探讨了Jinq的优势及核心功能。完整代码可在 GitHub 获取。