java - pagingandsortingrepository - CrudRepository dentro de mi repositorio personalizado implementado
spring framework repository (6)
Estoy intentando obtener una referencia a mi interfaz de repositorio ( UserRepository
) que extiende CrudRepository
dentro de mi implementación personalizada ( UserRepositoryExtensionImpl
) para obtener acceso a todos los métodos proporcionados por Spring JPA.
Extensión Crud:
@Repository
public interface UserRepository extends CrudRepository<User, String>, UserRepositoryExtension<RosterUser> {
...any custom spring JPA methods...
}
Interfaz de extensión:
@Repository
public interface UserRepositoryExtension <T> {
public T put(T entity);
}
Implementación personalizada:
public class UserRepositoryExtensionImpl implements UserRepositoryExtension<User> {
UserRepository userRepository;
@Autowired
public UserRepositoryExtensionImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User put(User user) {
System.out.println(user + "was put");
// ...put logic here
return null;
}...
}
Sin embargo, no puedo inyectar UserRepository
debido a que existe una dependencia circular (dado que UserRepository
extiende la interfaz implementada por mi UserRepositoryImpl
). Estoy teniendo el siguiente error:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name '' userRepositoryImpl'': Requested bean is currently in creation: Is there an unresolvable circular reference?
Una solución posible, pero menos que ideal, sería inyectar EntityManager
en UserRepositoryImp
, pero en ese caso, no tengo acceso a ninguno de los métodos Spring JPA proporcionados por CrudRepository
, ni a ningún otro método adicional que pueda haber creado en UserRepository.
¿Alguna sugerencia sobre cómo solucionar esto?
Cualquier ayuda sería muy apreciada.
EDITAR: Como se mencionó en la respuesta de @ shelley, pude resolver esto haciendo 3 cambios:
- Eliminando el
@Repository
deUserRepositoryExtensionImpl
- Renombrando
UserRepositoryExtensionImpl
aUserRepositoryImpl
. Aparentemente esto hace que Spring sea consciente de la existencia de la implementación. Ver Spring Doc - Eliminando mi constructor y moviendo el
@Autowired
al campouserRepository
¡ÉXITO!
Bueno, en este caso sugiero usar la anotación @Lazy
.
public class MyCustomRepositoryImpl implements MyCustomRepository {
@Lazy
@Autowired
private MyRepository myRepository;
@Override
public boolean customMethod() {
return myRepository.count() > 0;
}
}
Con el parámetro constructor, Spring intenta crear la clase de repositorio "básica" que requiere su repositorio personalizado, que requiere su repositorio "básico", el caso típico con dependencia circular.
Sin @Lazy
pero solo con @Autowired
tampoco funcionará (habrá un problema con el bean de fábrica para el repositorio básico).
Creo que en este caso el @Lazy
es la solución más elegante.
Como Shelley señaló, el nombramiento es realmente importante para que el autowire funcione. En el siguiente ejemplo, sigo el estándar de nombres correcto para mi interfaz personalizada y su implementación. Pero mi interfaz que extendió JpaRepository se llamó "ItemDao" en lugar de "ItemRepository", esto dio como resultado que esa primavera ignoré por completo mi implementación personalizada ...
OBS !!! Debería ser "ItemRepository"
@Repository
public interface ItemDao extends JpaRepository<Item, Long>, ItemRepositoryCustom {}
mi interfaz
interface ItemRepositoryCustom {...}
mi clase de implementación
class ItemRepositoryImpl implements ItemRepositoryCustom {...}
Si alguien tiene problemas similares, comience por seguir el estándar de denominación que se usa en la documentación de Spring en el enlace a continuación.
Encontré una forma de hacerlo sin la necesidad de @Autowire
:
public interface UserRepository extends
UserRepositoryBasic,
UserRepositoryExtension
{
}
public interface UserRepositoryBasic extends
JpaRepository<User, String>
{
// standard Spring Data methods, like findByLogin
}
public interface UserRepositoryExtension
{
public void customMethod();
}
public class UserRepositoryExtensionImpl implements
UserRepositoryExtension
{
private final UserRepositoryBasic userRepositoryBasic;
// constructor-based injection
public UserRepositoryExtensionImpl(
UserRepositoryBasic userRepositoryBasic)
{
this.userRepositoryBasic = userRepositoryBasic;
}
public void customMethod()
{
// we can call all basic Spring Data methods using
// userRepositoryBasic
}
}
Es necesario cambiar un par de cosas pequeñas para que esto funcione:
Elimine la anotación
@Repository
de la interfaz del repositorio personalizado (UserRepositoryExtension
).La implementación del repositorio personalizado en realidad debería llamarse "
<StandardRepository>Impl
" en lugar de "<CustomRepository>Impl
". En su ejemplo de código, esto debería serUserRepositoryImpl
lugar deUserRepositoryExtensionImpl
.
Hay una forma bien definida de crear implementaciones de repositorio personalizadas en Spring Data JPA que debe seguir. Básicamente, necesita extender CrudRepository
para no tener que inyectar una instancia de él en su implementación personalizada.
He resuelto el problema inyectando ApplicationContext
y obteniendo bean de forma perezosa utilizando applicationContext.getBean(UserRepository.class)
. Funciona de esta manera.