Cómo agregar un método personalizado a Spring Data JPA

Resuelto Sharad Yadav asked hace 12 años • 13 respuestas

Estoy investigando Spring Data JPA. Considere el siguiente ejemplo en el que haré que todas las funciones básicas y del buscador funcionen de forma predeterminada y, si quiero personalizar un buscador, también puedo hacerlo fácilmente en la propia interfaz.

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {

  @Query("<JPQ statement here>")
  List<Account> findByCustomer(Customer customer);
}

Me gustaría saber ¿cómo puedo agregar un método personalizado completo con su implementación para el AccountRepository anterior? Como es una interfaz, no puedo implementar el método allí.

Sharad Yadav avatar Aug 09 '12 17:08 Sharad Yadav
Aceptado

Necesita crear una interfaz separada para sus métodos personalizados:

public interface AccountRepository 
    extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }

public interface AccountRepositoryCustom {
    public void customMethod();
}

y proporcionar una clase de implementación para esa interfaz:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @Autowired
    @Lazy
    AccountRepository accountRepository;  /* Optional - if you need it */

    public void customMethod() { ... }
}

Ver también:

  • 4.6 Implementaciones personalizadas para repositorios de datos Spring

  • Tenga en cuenta que el esquema de nombres ha cambiado entre versiones. Consulte https://stackoverflow.com/a/52624752/66686 para obtener más detalles.

axtavt avatar Aug 09 '2012 10:08 axtavt

Además de la respuesta de axtavt , no olvide que puede inyectar Entity Manager en su implementación personalizada si lo necesita para crear sus consultas:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    public void customMethod() { 
        ...
        em.createQuery(yourCriteria);
        ...
    }
}
jelies avatar Mar 11 '2013 14:03 jelies

Existe una solución ligeramente modificada que no requiere interfaces adicionales.

Como se especifica en la funcionalidad documentada , el Implsufijo nos permite tener una solución tan limpia:

  • Defina en su interfaz habitual @Repository, por ejemplo MyEntityRepository, los métodos personalizados (además de sus métodos Spring Data)
  • Cree una clase MyEntityRepositoryImpl(el Implsufijo es mágico) en cualquier lugar (ni siquiera es necesario que esté en el mismo paquete) que implemente solo los métodos personalizados y anote dicha clase con @Component( @Repository no funcionará).
    • Esta clase puede incluso inyectar MyEntityRepositoryvía @Autowiredpara usar en métodos personalizados.

Ejemplo:

Clase de entidad (para que esté completo):

package myapp.domain.myentity;
@Entity
public class MyEntity {
    @Id     private Long id;
    @Column private String comment;
}

Interfaz del repositorio:

package myapp.domain.myentity;

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

    // EXAMPLE SPRING DATA METHOD
    List<MyEntity> findByCommentEndsWith(String x);

    List<MyEntity> doSomeHql(Long id);   // custom method, code at *Impl class below

    List<MyEntity> useTheRepo(Long id);  // custom method, code at *Impl class below

}

Bean de implementación de métodos personalizados:

package myapp.infrastructure.myentity;

@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the exact repo name + Impl !!

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private MyEntityRepository myEntityRepository;

    @SuppressWarnings("unused")
    public List<MyEntity> doSomeHql(Long id) {
        String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
        TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
        query.setParameter("id", id);
        return query.getResultList();
    }

    @SuppressWarnings("unused")
    public List<MyEntity> useTheRepo(Long id) {
        List<MyEntity> es = doSomeHql(id);
        es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
        es.add(myEntityRepository.findById(2L).get());
        return es;
    }

}

Uso:

// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
@Service
public class SomeService {
    @Autowired
    private MyEntityRepository myEntityRepository;

    public void someMethod(String x, long y) {
        // call any method as usual
        myEntityRepository.findByCommentEndsWith(x);
        myEntityRepository.doSomeHql(y);
    }
}

Y eso es todo, no necesita ninguna otra interfaz que la del repositorio de Spring Data que ya tiene.


Los únicos inconvenientes posibles que identifiqué son:

  • ImplEl compilador marca los métodos personalizados de la clase como no utilizados, de ahí la @SuppressWarnings("unused")sugerencia.
  • Tienes un límite de una Implclase. (Mientras que en la implementación normal de interfaces de fragmentos, los documentos sugieren que podría tener muchas).
  • Si coloca la Implclase en un paquete diferente y su prueba solo usa @DataJpaTest, debe agregarla @ComponentScan("package.of.the.impl.clazz")a su prueba para que Spring la cargue.
acdcjunior avatar Apr 06 '2019 00:04 acdcjunior