如何为不同实体类创建通用的queryEntity查询方法?
通用实体查询解决方案
核心思路
用泛型+Map存储实体资源替代每个实体单独写重复代码,通过实体类Class作为标识,动态获取对应的Repository和Queue,实现调用时仅需指定实体类的通用方法。
具体实现步骤
1. 统一Repository接口
让所有实体的Repository继承同一个通用接口,确保泛型操作的一致性:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.io.Serializable; // 通用基础Repository,继承JpaRepository和JpaSpecificationExecutor支持条件查询 public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { }
然后每个实体的Repository实现这个接口:
// AdventureHolidays对应的Repository public interface AdventureHolidaysRepo extends BaseRepository<AdventureHolidays, Long> { } // Backpacking对应的Repository public interface BackpackingRepo extends BaseRepository<Backpacking, Long> { }
2. 改造QueryEntitiesImpl服务类
用两个ConcurrentHashMap分别存储每个实体的Queue和Repository,避免静态变量的冗余:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; @Service public class QueryEntitiesImpl implements QueryEntities { // 存储每个实体对应的查询队列,键为实体Class,值为实体队列 private final Map<Class<?>, Queue<?>> entityQueues = new ConcurrentHashMap<>(); // 存储每个实体对应的Repository,键为实体Class,值为对应的BaseRepository private Map<Class<?>, BaseRepository<?>> entityRepos; // 注入所有BaseRepository的实现类 @Autowired private List<BaseRepository<?>> allRepos; // 初始化Repository映射 @PostConstruct private void initRepoMap() { entityRepos = new ConcurrentHashMap<>(); for (BaseRepository<?> repo : allRepos) { // 通过JpaRepository的getDomainClass()获取实体类 Class<?> entityClass = repo.getDomainClass(); entityRepos.put(entityClass, repo); } } // 通用查询方法:指定实体类和查询条件,随机返回结果 @Override @SuppressWarnings("unchecked") public <T> T queryEntity(Class<T> entityClass, Specification<T> spec) { // 获取或初始化当前实体的队列 Queue<T> queue = (Queue<T>) entityQueues.computeIfAbsent(entityClass, k -> new LinkedList<>()); // 队列空时,从数据库查询数据并打乱顺序填充队列 if (queue.isEmpty()) { BaseRepository<T> repo = (BaseRepository<T>) entityRepos.get(entityClass); if (repo == null) { throw new IllegalArgumentException("未找到实体[" + entityClass.getName() + "]对应的Repository"); } List<T> entities = repo.findAll(spec); Collections.shuffle(entities); queue.addAll(entities); } // 返回队列头部元素 return queue.poll(); } }
3. 调用方式
在需要查询的地方,直接指定实体类和查询条件即可:
// 查询AdventureHolidays实体 Specification<AdventureHolidays> adventureSpec = (root, query, cb) -> cb.equal(root.get("status"), "ACTIVE"); AdventureHolidays result1 = queryEntities.queryEntity(AdventureHolidays.class, adventureSpec); // 查询Backpacking实体 Specification<Backpacking> backpackingSpec = (root, query, cb) -> cb.greaterThan(root.get("price"), 1000); Backpacking result2 = queryEntities.queryEntity(Backpacking.class, backpackingSpec);
关键细节说明
- 线程安全:用
ConcurrentHashMap存储队列和Repository,队列使用LinkedList(如果是多线程高并发场景,可替换为ConcurrentLinkedQueue) - 类型安全:通过
@SuppressWarnings("unchecked")处理泛型转换,结合Repository的getDomainClass()确保实体与Repo的映射正确性 - 扩展性:新增实体时只需创建对应的Repository并继承
BaseRepository,无需修改QueryEntitiesImpl的代码
内容的提问来源于stack exchange,提问作者Stefan Jankovic
相关产品推荐
相关产品推荐

