如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中

Posted

技术标签:

【中文标题】如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中【英文标题】:How to implement Generic JPA Repository in Spring Boot - Which can be autowired into spring services for any entity/class type 【发布时间】:2016-02-04 08:07:27 【问题描述】:

这里是示例 Generic Repository 实现,它扩展了 spring PagingAndSortingRepository,

@NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> 

  public List<T> findByNamedQuery( String name );
  public List<T> findByNamedQueryAndParams( String name, Map<String, Object> params );
  public T findOneByNamedQuery( String name );
  public T findOneByNamedQueryAndParams( String name, Map<String, Object> params );

工厂 Bean 类,

public class GenericRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends
    JpaRepositoryFactoryBean<R, T, I> 

   @SuppressWarnings( "rawtypes" )
   protected RepositoryFactorySupport createRepositoryFactory( EntityManager em )
   
    return new MyRepositoryFactory(em);
   

   private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory 

    private final EntityManager em;

    public MyRepositoryFactory( EntityManager em )
    
        super(em);
        this.em = em;
    

    @SuppressWarnings( "unchecked" )
    protected Object getTargetRepository( RepositoryMetadata metadata )
    
        return new GenericRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), em);
    

    protected Class<?> getRepositoryBaseClass( RepositoryMetadata metadata )
    
        return GenericRepositoryImpl.class;
    
  

实现类,

public final class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements
    GenericRepository<T, ID> 

  private final EntityManager em;
  private final Class<T> domainClass;

  public GenericRepositoryImpl( Class<T> domainClass, EntityManager entityManager )
  
      super(domainClass, entityManager);
      this.em = entityManager;
      this.domainClass = domainClass;
  

  @Override
  public List<T> findByNamedQuery( final String name )
  
      validate(name);
      return this.em.createNamedQuery(name, domainClass).getResultList();
  

  @Override
  public T findOneByNamedQuery( String name )
  
      validate(name);
      return this.em.createNamedQuery(name, domainClass).getSingleResult();
  

  @Override
  public List<T> findByNamedQueryAndParams( String name, Map<String, Object> params )
   
      validate(name, params);
      final TypedQuery<T> query = this.em.createQuery(name, domainClass);
      setParams(query, params);
      return query.getResultList();
   


因此,当我尝试将 GenericRepository 自动装配到不同类型(如 Customer.java、Message.java 等)的服务中时,它抛出的至少需要一种 bean 类型的接口 GenericRepository。如果我为客户和消息类型创建单独的存储库,这将起作用。如果不创建多个存储库,我将无法实现这一点。

@Service
@Transactional( noRollbackFor = Exception.class )
public class CustomerService 

@Autowired
private GenericRepository<Customer, Serializable> cr; works fine with just one entity type

@Autowired
private GenericRepository<Message, Serializable> cr; throws exception

如果有 100 个或更多实体类,那么我最终会创建 100 个存储库,这很糟糕。如果有更好的方法,请告诉我。

【问题讨论】:

我的情况完全相同。你找到什么帮助你了吗?谢谢! 你们知道如何实现吗?我也有同样的情况。 【参考方案1】:

对于我所阅读的内容,通过@Query 注释告诉新接口方法要做什么会更容易,并且不要用 BeanFactory 或 impl 来打扰自己......

 @Repository
 public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> 
@Query(value = "SELECT c FROM customers c WHERE c.name = :name")
public List<T> findByNamedQuery( String name );
...

Using generics in Spring Data JPA repositories

如果这不适用于您,并且您说您的代码适用于一个存储库,但是当您添加第二个时失败,我的第一个想法是尝试将 bean 的范围设置为原型,但这只是一个推测。如果我没有真正帮助我很抱歉,请不要太讨厌我^^

【讨论】:

以上是关于如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot 中实现基于角色权限的系统

如何在 Spring Boot 中实现 Camunda SendTask

Spring Boot JPA CrudRepository 选择性更新 - 只有一些属性

如何在spring boot中实现jms队列

如何使用hibernate在spring boot中实现分页

如何在 Spring Boot 中实现部分 GET 请求?