Verificamos de forma exhaustivamente cada uno de los tutoriales en nuestro espacio con el objetivo de mostrarte siempre la información con la mayor veracidad y certera.
Solución:
Ahora, si realmente quieres arreglar las cosas por tu cuenta:
1) En la clase de mapeador, puede definir mapeadores de implementos que resuelvan este problema haciéndolos unidireccionales. Con métodos como
MapPlanWithActivities()
, MapPlan()
, MapActivitiesWithPlan()
y MapActivities()
. de esta forma podría saber qué datos necesita y según la función que utilice sabrá cuándo detener la recursividad.
2) La otra solución (mucho) más compleja sería resolver el problema por lógica y detectar el bucle. Por ejemplo, puede definir una anotación para ese caso como lo hace Jackson Library. para eso tendrás que usar alguna reflexión de Java. Vea la Reflexión de Java aquí
3) la forma más fácil sería usar Dozer como dije en mi comentario: Dozer
Relaciones de mesa
Supongamos que tenemos lo siguiente post
y post_comment
tablas, que forman una relación de uno a varios a través de la post_id
Columna de clave externa en el post_comment
mesa.
Obtener una proyección DTO de uno a varios con JPA e Hibernate
Teniendo en cuenta que tenemos un caso de uso que solo requiere obtener el id
y title
columnas de la post
mesa, así como la id
y review
columnas de la post_comment
tablas, podríamos usar la siguiente consulta JPQL para obtener la proyección requerida:
select p.id as p_id,
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id
Al ejecutar la consulta de proyección anterior, obtenemos los siguientes resultados:
| p.id | p.title | pc.id | pc.review |
|------|-----------------------------------|-------|---------------------------------------|
| 1 | High-Performance Java Persistence | 1 | Best book on JPA and Hibernate! |
| 1 | High-Performance Java Persistence | 2 | A must-read for every Java developer! |
| 2 | Hypersistence Optimizer | 3 | It's like pair programming with Vlad! |
Sin embargo, no queremos utilizar una tabla ResultSet
o el predeterminado List
Proyección de consultas JPA o Hibernate. Queremos transformar el conjunto de resultados de la consulta mencionado anteriormente en un List
de PostDTO
objetos, cada uno de estos objetos tiene un comments
colección que contiene todos los asociados PostCommentDTO
objetos:
Podemos usar un Hibernate ResultTransformer
, como se ilustra en el siguiente ejemplo:
List postDTOs = entityManager.createQuery("""
select p.id as p_id,
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id
""")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(new PostDTOResultTransformer())
.getResultList();
assertEquals(2, postDTOs.size());
assertEquals(2, postDTOs.get(0).getComments().size());
assertEquals(1, postDTOs.get(1).getComments().size());
El PostDTOResultTransformer
va a definir el mapeo entre el Object[]
proyección y la PostDTO
objeto que contiene el PostCommentDTO
objetos DTO secundarios:
public class PostDTOResultTransformer
implements ResultTransformer
private Map postDTOMap = new LinkedHashMap<>();
@Override
public Object transformTuple(
Object[] tuple,
String[] aliases)
Map aliasToIndexMap = aliasToIndexMap(aliases);
Long postId = longValue(tuple[aliasToIndexMap.get(PostDTO.ID_ALIAS)]);
PostDTO postDTO = postDTOMap.computeIfAbsent(
postId,
id -> new PostDTO(tuple, aliasToIndexMap)
);
postDTO.getComments().add(
new PostCommentDTO(tuple, aliasToIndexMap)
);
return postDTO;
@Override
public List transformList(List collection)
return new ArrayList<>(postDTOMap.values());
El aliasToIndexMap
es solo una pequeña utilidad que nos permite construir un Map
estructura que asocia los alias de columna y el índice donde se encuentra el valor de la columna en el Object[]
tuple
array:
public Map aliasToIndexMap(
String[] aliases)
Map aliasToIndexMap = new LinkedHashMap<>();
for (int i = 0; i < aliases.length; i++)
aliasToIndexMap.put(aliases[i], i);
return aliasToIndexMap;
El postDTOMap
es donde vamos a almacenar todo PostDTO
entidades que, al final, serán devueltas por la ejecución de la consulta. La razón por la que estamos usando el postDTOMap
es que las filas principales se duplican en el conjunto de resultados de la consulta SQL para cada registro secundario.
El computeIfAbsent
El método nos permite crear un PostDTO
Objeto solo si no existe PostDTO
referencia ya almacenada en el postDTOMap
.
El PostDTO
la clase tiene un constructor que puede establecer el id
y title
propiedades usando los alias de columna dedicados:
public class PostDTO
public static final String ID_ALIAS = "p_id";
public static final String TITLE_ALIAS = "p_title";
private Long id;
private String title;
private List comments = new ArrayList<>();
public PostDTO(
Object[] tuples,
Map aliasToIndexMap)
this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.title = stringValue(tuples[aliasToIndexMap.get(TITLE_ALIAS)]);
//Getters and setters omitted for brevity
El PostCommentDTO
está construido de manera similar:
public class PostCommentDTO
public static final String ID_ALIAS = "pc_id";
public static final String REVIEW_ALIAS = "pc_review";
private Long id;
private String review;
public PostCommentDTO(
Object[] tuples,
Map aliasToIndexMap)
this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.review = stringValue(tuples[aliasToIndexMap.get(REVIEW_ALIAS)]);
//Getters and setters omitted for brevity
¡Eso es!
Utilizando el PostDTOResultTransformer
, el conjunto de resultados de SQL se puede transformar en una proyección DTO jerárquica, con la que es muy conveniente trabajar, especialmente si necesita ser calculado como una respuesta JSON:
postDTOs = ArrayList, size = 2
0 = PostDTO
id = 1L
title = "High-Performance Java Persistence"
comments = ArrayList, size = 2
0 = PostCommentDTO
id = 1L
review = "Best book on JPA and Hibernate!"
1 = PostCommentDTO
id = 2L
review = "A must read for every Java developer!"
1 = PostDTO
id = 2L
title = "Hypersistence Optimizer"
comments = ArrayList, size = 1
0 = PostCommentDTO
id = 3L
review = "It's like pair programming with Vlad!"
Más adelante puedes encontrar las referencias de otros programadores, tú asimismo tienes el poder dejar el tuyo si te gusta.