泛型dao接口应精简定义通用crud操作,t为实体类型、id为主键类型,仅包含save、findbyid、findall、count、existsbyid、deletebyid和delete方法;条件查询和分页由具体dao实现或service封装;避免事务与缓存职责,运行时类型通过构造传入class解决擦除问题。

泛型 DAO 接口的核心目标是统一数据访问层的通用操作,同时保留类型安全和扩展性。关键不是堆砌方法,而是聚焦真正可复用、不依赖具体业务逻辑的 CRUD 行为。
接口定义要精简,只暴露稳定契约
GenericDao
- save(T entity):插入或更新(根据主键是否存在判断)
- T findById(ID id):按主键查单个,查不到返回 null 或 Optional
-
List
findAll() :全量查询(慎用,生产环境建议限制或废弃) - long count():统计总数,比 findAll() 更轻量
- boolean existsById(ID id):存在性校验,比 findById() 更高效
- void deleteById(ID id):按主键删除
- void delete(T entity):按实体删除(需确保主键非空)
避免在泛型接口里塞查询条件或分页
where、order by、limit、offset 这些属于具体业务场景,强行抽象成泛型方法(比如 findByXxx、findPage)会导致接口膨胀、语义模糊、实现复杂。正确做法是:
- 泛型接口保持“无条件”操作
- 具体实体的 DAO 接口继承 GenericDao,并额外声明带条件的方法(如 UserDAO extends GenericDao
{ List findByEmail(String email); }) - 分页统一由上层 Service 封装,或通过独立的 Pageable 接口/参数配合具体 DAO 实现
泛型擦除不影响运行时行为,但影响实现方式
Java 泛型在编译后被擦除,所以 GenericDao 的实现类(如 JpaGenericDao)不能直接通过 T.class 获取实体类型。常见解决方式有:
- 构造时传入 Class
(推荐):new JpaGenericDao(User.class) - 利用反射从子类泛型签名中提取(需继承抽象类而非接口,且仅适用于固定继承链)
- Spring Data JPA 的 JpaRepository 已内置类型推导,自研框架建议优先采用显式 Class 参数
不要让泛型 DAO 承担事务或缓存职责
事务控制应放在 Service 层;缓存策略(如 @Cacheable)也更适合加在 Service 方法上。DAO 层保持纯粹的数据搬运角色:
- GenericDao 接口方法不声明事务注解
- 不引入 CacheManager、RedisTemplate 等缓存组件依赖
- 若需二级缓存(如 Hibernate),由 ORM 框架自身管理,DAO 不感知
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











