복붙노트

[SPRING] Spring 부트에서 Generic JPA Repository를 구현하는 방법 - 어떤 엔티티 / 클래스 유형에 대해 Spring 서비스로 자동 변환 될 수 있는가?

SPRING

Spring 부트에서 Generic JPA Repository를 구현하는 방법 - 어떤 엔티티 / 클래스 유형에 대해 Spring 서비스로 자동 변환 될 수 있는가?

다음은 스프링 PagingAndSortingRepository를 확장 한 샘플 Generic Repository 구현입니다.

@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 );

}

팩토리 빈 클래스,

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 등과 같은 여러 유형의 서비스에 Autowire하려고 할 때 GenericRepository 인터페이스가 적어도 하나의 bean 유형을 필요로합니다. 이는 고객 및 메시지 유형 모두에 대해 개별 리포지토리를 생성 할 때 작동합니다. 여러 저장소를 만들지 않고도이 기능을 구현할 수 없습니다.

@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. ==============================

    1.내가 읽은 것을 위해 새로운 인터페이스 메소드에 @Query 어노테이션으로 할 일을 알려주고 BeanFactory 또는 impl으로 귀찮게하지 말아라.

    내가 읽은 것을 위해 새로운 인터페이스 메소드에 @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 );
    ...
    }
    

    스프링 데이터 JPA 저장소에서 제네릭 사용하기

    그게 당신에게 적용 할 수 없다면 당신의 코드가 하나의 저장소와 함께 작동하지만 두번째를 추가 할 때 실패 할 것이라는 말은 프로토 타입으로 bean의 범위를 설정하려고 시도하는 것입니다.하지만 이것은 단지 추측 일뿐입니다. 나는 정말로 도움이되지 않는다면 미안하다. 나를 너무 미워하지 마라. ^^

  2. from https://stackoverflow.com/questions/33511537/how-to-implement-generic-jpa-repository-in-spring-boot-which-can-be-autowired by cc-by-sa and MIT license