Luego de consultar expertos en este tema, programadores de varias ramas y maestros dimos con la solución al problema y la dejamos plasmada en esta publicación.
Solución:
En primer lugar, sé que estamos subiendo un poco el listón, pero esto ya es muchísimo menos código del que tenías que escribir sin la ayuda de Spring Data JPA.
En segundo lugar, creo que no necesita la clase de servicio en primer lugar, si todo lo que hace es reenviar una llamada al repositorio. Recomendamos utilizar servicios frente a los repositorios si tiene una lógica empresarial que necesita la orquestación de diferentes repositorios dentro de una transacción o tiene otra lógica empresarial para encapsular.
En términos generales, por supuesto, puede hacer algo como esto:
interface ProductRepository extends CrudRepository
@Query("select p from ##entityName p where ?1 member of p.categories")
Iterable findByCategory(String category);
Iterable findByName(String name);
Esto le permitirá usar el repositorio en el lado del cliente de esta manera:
class MyClient
@Autowired
public MyClient(ProductRepository carRepository,
ProductRepository wineRepository) …
y funcionará como se esperaba. Sin embargo, hay algunas cosas a tener en cuenta:
Esto solo funciona si las clases de dominio usan herencia de tabla única. La única información sobre la clase de dominio que podemos obtener en el momento del arranque es que será Product
objetos. Entonces, para métodos como findAll()
e incluso findByName(…)
las consultas relevantes comenzarán con select p from Product p where…
. Esto se debe al hecho de que la búsqueda de reflexión nunca podrá producir Wine
o Car
a no ser que crea una interfaz de repositorio dedicada para que capture la información de tipo concreto.
En términos generales, recomendamos crear interfaces de repositorio por raíz agregada. Esto significa que no tiene un repositorio para cada clase de dominio per se. Aún más importante, una abstracción 1: 1 de un servicio sobre un repositorio también pierde completamente el sentido. Si crea servicios, no crea uno para cada repositorio (un mono podría hacer eso, y nosotros no somos monos, ¿verdad?). Un servicio está exponiendo una API de nivel superior, es mucho más una unidad de casos de uso y, por lo general, organiza las llamadas a varios repositorios.
Además, si crea servicios sobre repositorios, generalmente desea obligar a los clientes a usar el servicio en lugar del repositorio (un ejemplo clásico aquí es que un servicio para la administración de usuarios también activa la generación de contraseñas y el cifrado, por lo que de ninguna manera Sería una buena idea permitir que los desarrolladores utilicen el repositorio directamente, ya que trabajarían eficazmente en torno al cifrado). Por lo tanto, generalmente desea ser selectivo sobre quién puede persistir qué objetos de dominio para no crear dependencias en todo el lugar.
Resumen
Sí, puede crear repositorios genéricos y usarlos con varios tipos de dominio, pero existen limitaciones técnicas bastante estrictas. Aún así, desde un punto de vista arquitectónico, el escenario que describe arriba ni siquiera debería aparecer, ya que esto significa que está enfrentando un olor a diseño de todos modos.
¡Esto es muy posible! Probablemente llego muy tarde a la fiesta. Pero esto ciertamente ayudará a alguien en el futuro. ¡Aquí hay una solución completa que funciona a las mil maravillas!
Crear BaseEntity
class para sus entidades de la siguiente manera:
@MappedSuperclass
public class AbstractBaseEntity implements Serializable
@Id @GeneratedValue
private Long id;
@Version
private int version;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public AbstractBaseEntity()
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
// getters and setters
Cree una interfaz de repositorio JPA genérico para la persistencia de su DAO de la siguiente manera: NB. Recuerde poner el @NoRepositoryBean
¡para que JPA no intente encontrar una implementación para el repositorio!
@NoRepositoryBean
public interface AbstractBaseRepository
extends JpaRepository
Cree una clase de servicio base que utilice el repositorio JPA base anterior. Este es el que otras interfaces de servicio en su dominio simplemente extenderán de la siguiente manera:
public interface AbstractBaseService
public abstract T save(T entity);
public abstract List findAll(); // you might want a generic Collection if u prefer
public abstract Optional findById(ID entityId);
public abstract T update(T entity);
public abstract T updateById(T entity, ID entityId);
public abstract void delete(T entity);
public abstract void deleteById(ID entityId);
// other methods u might need to be generic
Luego, cree una implementación abstracta para el repositorio JPA base y los métodos CRUD básicos también recibirán sus implementaciones como se muestra a continuación:
@Service
@Transactional
public abstract class AbstractBaseRepositoryImpl
implements AbstractBaseService
private AbstractBaseRepository abstractBaseRepository;
@Autowired
public AbstractBaseRepositoryImpl(AbstractBaseRepository abstractBaseRepository)
this.abstractBaseRepository = abstractBaseRepository;
@Override
public T save(T entity)
return (T) abstractBaseRepository.save(entity);
@Override
public List findAll()
return abstractBaseRepository.findAll();
@Override
public Optional findById(ID entityId)
return abstractBaseRepository.findById(entityId);
@Override
public T update(T entity)
return (T) abstractBaseRepository.save(entity);
@Override
public T updateById(T entity, ID entityId)
Optional optional = abstractBaseRepository.findById(entityId);
if(optional.isPresent())
return (T) abstractBaseRepository.save(entity);
else
return null;
@Override
public void delete(T entity)
abstractBaseRepository.delete(entity);
@Override
public void deleteById(ID entityId)
abstractBaseRepository.deleteById(entityId);
Cómo utilizar el resumen anterior entity
, service
, repository
, y implementation
:
Ejemplo aquí será un MyDomain
entidad. Cree una entidad de dominio que amplíe el AbstractBaseEntity
como sigue: NB. ID
, createdAt
, updatedAt
, version
, etc se incluirán automáticamente en el MyDomain
entidad de la AbstractBaseEntity
@Entity
public class MyDomain extends AbstractBaseEntity
private String attribute1;
private String attribute2;
// getters and setters
Entonces crea un repository
Para el MyDomain
entidad que extiende el AbstractBaseRepository
como sigue:
@Repository
public interface MyDomainRepository extends AbstractBaseRepository
Además, cree un service
interfaz para el MyDomain
entidad de la siguiente manera:
public interface MyDomainService extends AbstractBaseService
Luego proporcione una implementación para el MyDomain
entidad que extiende el AbstractBaseRepositoryImpl
implementación de la siguiente manera:
@Service
@Transactional
public class MyDomainServiceImpl extends AbstractBaseRepositoryImpl
implements MyDomainService
private MyDomainRepository myDomainRepository;
public MyDomainServiceImpl(MyDomainRepository myDomainRepository)
super(myDomainRepository);
// other specialized methods from the MyDomainService interface
Now use your `MyDomainService` service in your controller as follows:
@RestController // or @Controller
@CrossOrigin
@RequestMapping(value = "/")
public class MyDomainController
private final MyDomainService myDomainService;
@Autowired
public MyDomainController(MyDomainService myDomainService)
this.myDomainService = myDomainService;
@GetMapping
public List getMyDomains()
return myDomainService.findAll();
// other controller methods
NÓTESE BIEN. Asegúrese de que el AbstractBaseRepository
está anotado con @NoRepositoryBean
así que eso JPA
no intenta encontrar una implementación para el bean. También el AbstractBaseServiceImpl
debe marcarse como abstracto, de lo contrario, JPA intentará conectar automáticamente todos los datos secundarios del AbstractBaseRepository
en el constructor de la clase que conduce a un NoUniqueBeanDefinitionException
¡ya que se inyectará más de 1 daos (repositorio) cuando se cree el bean! Ahora tu service
, repository
, y implementations
son más reutilizables. ¡Todos odiamos la repetición!
Espero que esto ayude a alguien.
Te mostramos las comentarios y valoraciones de los usuarios
Puedes añadir valor a nuestra información colaborando tu veteranía en los comentarios.