能否创建包含可继承JPA实体、Repository、服务的Spring Boot公共库?
当前实现存在的核心问题
- 实体基类使用错误:@MappedSuperclass 标注的类仅用于共享字段映射规则,本身不是JPA实体,不能直接作为
JpaRepository的泛型管理类型,JPA无法为非实体类创建持久化器,这就是Unable to locate persister异常的直接诱因。 - 基础组件未做泛型抽象:库中的Repository、Service、Controller直接绑定基类
AccountBase,上层应用扩展自定义实体后,无法自动适配返回上层自定义类型,导致返回类型不匹配。 - 库中基础组件错误实例化:库中的
AccountRepositoryBase、AccountServiceBase加了@Repository、@Service注解,Spring启动时会直接实例化这些绑定了非实体类的组件,直接触发异常。
修复方案
步骤1:重构公共库基础组件为泛型抽象结构
1.1 实体基类(无需修改)
package library; import lombok.Data; import javax.persistence.MappedSuperclass; @MappedSuperclass @Data public class AccountBase { public Long id; public String username; public String password; }
1.2 Repository基类(去掉实例化注解,改为泛型接口)
package library; import org.springframework.data.jpa.repository.JpaRepository; import java.util.UUID; // 去掉@Repository,不直接实例化,T为上层自定义的实体类型 public interface AccountRepositoryBase<T extends AccountBase> extends JpaRepository<T, UUID> { }
1.3 Service基类(改为抽象泛型类,去掉@Service注解)
package library; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Transactional public abstract class AccountServiceBase<T extends AccountBase> { protected final AccountRepositoryBase<T> accountRepository; // 构造函数注入,避免字段注入的问题 protected AccountServiceBase(AccountRepositoryBase<T> accountRepository) { this.accountRepository = accountRepository; } public List<T> listAll() { return accountRepository.findAll(); } }
1.4 Controller基类(改为抽象泛型类)
package library; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; public abstract class BaseController<T extends AccountBase> { protected final AccountServiceBase<T> accountService; protected BaseController(AccountServiceBase<T> accountService) { this.accountService = accountService; } @GetMapping("/accounts") public List<T> getAccounts() { return accountService.listAll(); } }
步骤2:上层应用实现扩展
2.1 自定义实体(无需修改)
package application; import library.AccountBase; import lombok.Data; import javax.persistence.Entity; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Entity @Data public class Account extends AccountBase { public String emailAddress; public String phoneNumber; }
2.2 自定义Repository
package application; import library.AccountRepositoryBase; import org.springframework.stereotype.Repository; @Repository public interface AccountRepository extends AccountRepositoryBase<Account> { }
2.3 自定义Service
package application; import library.AccountServiceBase; import org.springframework.stereotype.Service; @Service public class AccountService extends AccountServiceBase<Account> { public AccountService(AccountRepository accountRepository) { super(accountRepository); } // 可自行扩展业务方法 }
2.4 自定义Controller
package application; import library.BaseController; import org.springframework.web.bind.annotation.RestController; @RestController public class AccountController extends BaseController<Account> { public AccountController(AccountService accountService) { super(accountService); } // 可自行扩展接口 }
步骤3:配置JPA扫描范围
如果公共库和上层应用的包名不同,需要在启动类上增加扫描配置:
package application; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication // 扫描所有包下的JPA实体 @EntityScan(basePackages = {"library", "application"}) // 扫描所有包下的JPA Repository @EnableJpaRepositories(basePackages = {"library", "application"}) public class AppApplication { public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); } }
修改后两个问题都会解决:JPA会以上层自定义的实体作为持久化管理对象,不会再报找不到持久化器的异常;同时泛型会自动适配返回上层自定义的实体类型,包含扩展的字段。
内容的提问来源于stack exchange,提问作者Kerdos Media
相关产品推荐
相关产品推荐

