Cómo agregar un método personalizado a Spring Data JPA
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í.
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.
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);
...
}
}
Existe una solución ligeramente modificada que no requiere interfaces adicionales.
Como se especifica en la funcionalidad documentada , el Impl
sufijo nos permite tener una solución tan limpia:
- Defina en su interfaz habitual
@Repository
, por ejemploMyEntityRepository
, los métodos personalizados (además de sus métodos Spring Data) - Cree una clase
MyEntityRepositoryImpl
(elImpl
sufijo 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
MyEntityRepository
vía@Autowired
para usar en métodos personalizados.
- Esta clase puede incluso inyectar
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:
Impl
El compilador marca los métodos personalizados de la clase como no utilizados, de ahí la@SuppressWarnings("unused")
sugerencia.- Tienes un límite de una
Impl
clase. (Mientras que en la implementación normal de interfaces de fragmentos, los documentos sugieren que podría tener muchas). - Si coloca la
Impl
clase 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.