Tengo la siguiente entidad:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "simple_entity")
public class SimpleEntity {
@Id
private Long id;
@Column(name = "text")
private String text;
}
Quiero conseguir algunas entidades con columna adicional de la base de datos. Para ello he creado un simple par de clases.
@Getter
@Setter
@AllArgsConstructor
public class Pair<First, Second> {
private First first;
private Second second;
}
Entonces me preparaba una consulta en JPQL que crea el resultado esperado.
@Repository
public interface SimpleEntityRepository extends JpaRepository<SimpleEntity, Long> {
@Query("SELECT new com.example.demo.Pair(m, false) FROM SimpleEntity m")
List<Pair<SimpleEntity, Boolean>> getRecords();
}
La consulta devuelve resultados correctos, pero hay un problema con consultas adicionales.
Por lo tanto, tengo algunas preguntas:
- ¿Por qué funciona la APP de esta manera?
- ¿Hay alguna manera de obtener estos datos en una consulta en JPQL?
- ¿Cómo debería obtener las entidades junto con algunos datos adicionales (he visto la solución con la devolución del objeto [], pero no se ve bien)?
No es una respuesta completa, pero creo que va a añadir algo de información a la discusión:
Resultado Transformador
Como se ha marcado su pregunta de hibernación, puede utilizar un transformador resultado de hibernación (en desuso en 5.2, 6.0, pero todavía está en Alpha):
List<Pair<SimpleEntity, Boolean>> resultList = entityManager.createQuery(
"select m as first, false as second from SimpleEntity m")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(Transformers.aliasToBean(Pair.class))
.getResultList();
Esto supone que Pair
tiene constructor sin argumentos y produce:
select
simpleenti0_.id as col_0_0_,
0 as col_1_0_,
simpleenti0_.id as id1_18_,
simpleenti0_.text as text2_18_
from
simple_entity simpleenti0_
DTO adecuada
Utilizar una clase que puede ser construido a través de una lista de campos, no a toda entidad. La consulta se comportará muy bien cuando lista de campos seleccionados se especifica, no toda entidad
public class SimpleEntityDTO {
private Long id;
private String text;
private Boolean second;
public SimpleEntityDTO() {
}
public SimpleEntityDTO(Long id, String text, Boolean second) {
this.id = id;
this.text = text;
this.second = second;
}
// getters and setters
}
y modificar su consulta:
@Query("SELECT new com.example.demo.SimpleEntityDTO(m.id, m.text, false) FROM SimpleEntity m")
List<SimpleEntityDTO> getRecordsExtended();
Esto produce:
select
simpleenti0_.id as col_0_0_,
simpleenti0_.text as col_1_0_,
0 as col_2_0_
from
simple_entity simpleenti0_
Observaciones generales
Creo que una de las razones de la utilización de dtos sobre las entidades en las proyecciones es que no se gestionan. En su caso, cuando una entidad es una parte de un DTO, la entidad se maneja:
@Transactional
public void changeValueInFirstRecord() {
List<Pair<SimpleEntity, Boolean>> all = simpleEntityRepository.getRecords();
SimpleEntity firstEntity = all.get(0).getFirst();
boolean managed = entityManager.contains(firstEntity);
System.out.println("managed: " + managed); // true
firstEntity.setText("new value"); // firstEntity is updated
}
Lo mismo es cierto en el caso de un transformador resultado:
@Transactional
public void changeValueInFirstEntityViaTransformer() {
List<Pair<SimpleEntity, Boolean>> all = entityManager.createQuery(
"select m as first, false as second from SimpleEntity m")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(Transformers.aliasToBean(Pair.class))
.getResultList();
SimpleEntity firstEntity = all.get(0).getFirst();
boolean managed = entityManager.contains(firstEntity);
System.out.println("managed: " + managed); // true
firstEntity.setText("new_value"); // firstEntity is updated
}