¿Por qué la colocación de una entidad en la clase POJO usando "seleccione nuevo" en JPA causa una N + 1 problema?

Jelly:

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.

wtf

Por lo tanto, tengo algunas preguntas:

  1. ¿Por qué funciona la APP de esta manera?
  2. ¿Hay alguna manera de obtener estos datos en una consulta en JPQL?
  3. ¿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)?
Lesiak:

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 Pairtiene 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
}

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=370824&siteId=1
Recomendado
Clasificación