Spring Data JDBC提供了基础的CRUD能力,但在实际项目中我们往往需要封装通用仓库来复用公共逻辑,同时支持灵活的动态查询条件,避免重复编写相似的查询代码。

通用自定义仓库基础实现
首先我们需要定义一个通用的基础仓库接口,封装常用的增删改查方法,后续的业务仓库可以直接继承这个接口,减少重复代码。
定义通用仓库接口
我们创建一个BaseRepository接口,使用泛型来适配不同的实体类型,接口中定义通用的操作方法。
import org.springframework.data.repository.CrudRepository;
import java.io.Serializable;
import java.util.List;
// 通用基础仓库接口,ID类型限定为Serializable
public interface BaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
// 根据条件查询所有数据
List<T> findAllByCondition(String conditionSql, Object... params);
// 批量插入数据
void batchInsert(List<T> entityList);
// 根据自定义SQL更新数据
int updateByCustomSql(String updateSql, Object... params);
}
实现通用仓库接口
接下来需要为通用接口提供实现类,Spring Data JDBC的仓库实现可以通过继承SimpleJdbcRepository或者自定义实现类来完成。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import java.util.List;
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJdbcRepository<T, ID> implements BaseRepository<T, ID> {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
private JdbcAggregateTemplate jdbcAggregateTemplate;
public BaseRepositoryImpl(RepositoryMetadata metadata, JdbcAggregateTemplate template) {
super(metadata, template);
}
@Override
public List<T> findAllByCondition(String conditionSql, Object... params) {
// 这里需要根据实体类动态生成基础查询SQL,实际项目中可以结合反射获取表名和字段信息
String baseSql = "SELECT * FROM table_name";
String fullSql = baseSql + " " + conditionSql;
return namedParameterJdbcTemplate.query(fullSql, new MapSqlParameterSource(), (rs, rowNum) -> {
// 这里需要自定义结果集映射逻辑,实际项目中可以结合反射自动映射
return null;
});
}
@Override
public void batchInsert(List<T> entityList) {
// 批量插入实现逻辑,可结合反射生成插入SQL
}
@Override
public int updateByCustomSql(String updateSql, Object... params) {
return namedParameterJdbcTemplate.update(updateSql, new MapSqlParameterSource());
}
}
动态查询实现方案
动态查询是实际项目中最常用的需求,Spring Data JDBC本身没有像JPA那样内置的动态查询能力,我们可以通过以下几种方式实现。
方案一:自定义SQL拼接
对于简单的动态条件,我们可以通过拼接SQL字符串的方式实现,这种方式适合条件较少的场景。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Repository
public class UserRepositoryImpl implements UserCustomRepository {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Override
public List<Map<String, Object>> findUsersByDynamicCondition(String name, Integer age, String email) {
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
MapSqlParameterSource params = new MapSqlParameterSource();
// 动态拼接条件
if (name != null && !name.isEmpty()) {
sql.append(" AND name = :name");
params.addValue("name", name);
}
if (age != null) {
sql.append(" AND age = :age");
params.addValue("age", age);
}
if (email != null && !email.isEmpty()) {
sql.append(" AND email LIKE :email");
params.addValue("email", "%" + email + "%");
}
return namedParameterJdbcTemplate.queryForList(sql.toString(), params);
}
}
方案二:使用Querydsl实现类型安全的动态查询
Querydsl可以提供类型安全的动态查询能力,避免SQL拼接的错误,首先需要引入相关依赖。
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql</artifactId>
<version>5.0.0</version>
</dependency>
然后生成查询实体类,接着编写动态查询代码:
import com.querydsl.core.types.Predicate;
import com.querydsl.sql.SQLQueryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
public class UserQuerydslRepository {
@Autowired
private SQLQueryFactory sqlQueryFactory;
private final QUser qUser = QUser.user;
public List<User> findUsersByDynamicCondition(String name, Integer minAge, Integer maxAge) {
List<Predicate> predicates = new ArrayList<>();
if (name != null && !name.isEmpty()) {
predicates.add(qUser.name.eq(name));
}
if (minAge != null) {
predicates.add(qUser.age.goe(minAge));
}
if (maxAge != null) {
predicates.add(qUser.age.loe(maxAge));
}
return sqlQueryFactory.selectFrom(qUser)
.where(predicates.toArray(new Predicate[0]))
.fetch();
}
}
仓库配置与使用示例
要让自定义的通用仓库生效,需要配置Spring Data JDBC的仓库扫描,指定自定义实现类的后缀。
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
@Configuration
@EnableJdbcRepositories(basePackages = "com.example.repository", repositoryBaseClass = BaseRepositoryImpl.class)
public class JdbcConfig {
}
业务仓库继承通用仓库后就可以直接使用通用方法:
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserRepository extends BaseRepository<User, Long>, UserCustomRepository {
// 可以直接使用BaseRepository中的通用方法,也可以定义自己的查询方法
List<User> findByName(String name);
}
使用时直接注入业务仓库即可调用对应方法:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/users")
public List<User> getUsers(String name, Integer age) {
// 调用动态查询方法
return userRepository.findUsersByDynamicCondition(name, age, null);
}
}
注意事项
- SQL拼接方案需要注意SQL注入风险,参数必须通过参数化查询传入,不要直接拼接字符串参数
- 通用仓库的反射逻辑需要根据项目的实体规范统一实现,避免不同实体的映射逻辑冲突
- Querydsl方案需要额外维护查询实体类的生成,适合条件复杂、对类型安全要求高的场景
- 动态查询的性能需要关注,避免生成过于复杂的SQL语句影响数据库查询效率
Spring_Data_JDBC自定义仓库动态查询JDBCJPA修改时间:2026-07-01 16:24:40