Tengo 2 entidades CallRecords y CallRecordOperators con una relación de uno a muchos como se muestra a continuación

 public class CallRecords {

    @Id
    @Column(name = "id", unique = true)
    private String id;

    @Column(columnDefinition = "varchar(255) default ''")
    private String callerNumber = "";

    @OneToMany(mappedBy="callrecord")
    private List<CallRecordOperators> callRecordOperators = new ArrayList<CallRecordOperators>();


   //getter setters
}

public class CallRecordOperators {

    @Id
    @Column(name = "id", length = 50, unique = true, nullable = false, insertable = false, updatable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "callRecordId")
    private CallRecords callrecord;

    @ManyToOne
    @JoinColumn(name = "operatorId")
    private Operator operator;

    @Formats.DateTime(pattern = "yyyy-MM-dd HH:mm:yy")
    @Column(columnDefinition = "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP")
    private Date startTime = new Date();

    @Column(columnDefinition = "varchar(100) default ''")
    private String dialStatus;

   //getter setter
}

Entonces, si el usuario solicita todos los datos de "CallRecords", también tengo que dar "CallRecordOperators", ya que están relacionados.

Código actual para Mapper y DTO

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface CallRecordsMapper {

    CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);

    @Mapping(source="callRecordOperators",target = "operators")
    CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);

    public abstract CallRecordOperatorsDto toTarget(CallRecordOperators source);

    List<CallRecordsDto> callRecordsToCallRecordsDtos(List<CallRecords> callRecords);

}

public class CallRecordsDto {

    private String callerNumber;

    private List<CallRecordOperatorsDto> operators;

    //getter setters
}

public class CallRecordOperatorsDto {

    private String callRecordsId;

    private String operatorId;
    private String operatorName;

    private String currentTime;

   // getter setter

}

Pero para el código anterior estoy obteniendo

{
    "callerNumber": "9898989898",
    "operators": [{
        "callRecordsId": null,
        "operatorId": null,
        "operatorName": null,
        "currentTime": null
    }, {
        "callRecordsId": null,
        "operatorId": null,
        "operatorName": null,
        "currentTime": null
    }]
}

Los valores de la matriz del operador son nulos. ¿Cuál podría ser el problema?

5
silentprogrammer 29 dic. 2016 a las 12:25

3 respuestas

La mejor respuesta

Parece que le faltan las asignaciones de CallRecordOperators a CallRecordOperatorsDto:

@Mapper
public interface CallRecordsMapper {

    CallRecordsMapper INSTANCE = Mappers.getMapper(CallRecordsMapper.class);

    @Mapping(source="callRecordOperators",target = "operators")
    CallRecordsDto callRecordsToCallRecordsDto(CallRecords callRecords);

    @Mapping(target = "callRecordsId", source = "callrecord.id")
    @Mapping(target = "operatorId", source = "operator.id")
    @Mapping(target = "operatorName", source = "operator.name")
    @Mapping(target = "currentTime", source = "startTime")
    CallRecordOperatorsDto callRecordOperatorsToDto(CallRecordOperators source);
}
3
Gunnar 3 ene. 2017 a las 11:15

¿Qué tal cambiar a un enfoque ligeramente diferente que también funcione mejor? Mediante el uso de Vistas de entidades Blaze-Persistence puede definir su asignación directamente en las clases DTO y aplicarla en un generador de consultas para generar consultas eficientes que se adapten perfectamente a su estructura DTO.

@EntityView(CallRecords.class)
public interface CallRecordsDto {
    // The id of the CallRecords entity
    @JsonIgnore
    @IdMapping("id") String getId();

    String getCallerNumber();

    @Mapping("callRecordOperators")
    List<CallRecordOperatorsDto> getOperators();
}

@EntityView(CallRecordOperators.class)
public interface CallRecordOperatorsDto {

    // The id of the CallRecordOperators entity
    @JsonIgnore
    @IdMapping("id") Long getId();

    @Mapping("callrecord.id")
    String getCallRecordId();

    @Mapping("operator.id")
    String getOperatorId();

    @Mapping("operator.name")
    String getOperatorName();

    @Mapping("startTime")
    String getCurrentTime();

    // Whatever properties you want
}

¿Ves cómo puedes mapear los atributos de la entidad directamente en tus DTO? Y aquí viene el código para consultar

EntityManager entityManager = // jpa entity manager
CriteriaBuilderFactory cbf = // query builder factory from Blaze-Persistence
EntityViewManager evm = // manager that can apply entity views to query builders

CriteriaBuilder<User> builder = cbf.create(entityManager, CallRecords.class)
    .where("callerNumber").eq("123456789");
List<CallRecordsDto> result = evm.applySetting(
    builder, 
    EntityViewSetting.create(CallRecordsDto.class)
).getResultList();

Tenga en cuenta que esto generará aproximadamente la siguiente consulta optimizada

SELECT 
    c.id, 
    c.callerNumber, 
    o.callrecord.id, 
    o.id,
    o.startTime,
    op.id,
    op.name
FROM CallRecords c
LEFT JOIN c.callRecordOperators o
LEFT JOIN o.operator op
WHERE c.callerNumber = :param_1
-1
Christian Beikov 15 feb. 2017 a las 17:15

Cuando realiza una consulta de Hibernate de elementos A, puede buscar los elementos B relacionados de la colección bs utilizando diferentes estrategias. Algunos de ellos son:

  1. Si usa HQL para construir sus consultas, puede hacer un JOIN FETCH o LEFT JOIN FETCH para completar la colección bs:

    String hql = "SELECT DISTINCT a FROM " + A.class.getName() 
        + " a LEFT JOIN FETCH a.bs WHERE ...";
    

    Esta consulta cargará todos los datos utilizando una única consulta SQL.

  2. Utilice la búsqueda entusiasta de la colección bs, cambiando la anotación @OneToMany:

    @OneToMany(fetch=FetchType.EAGER)
    private List<B> bs;
    

    En este caso, cuando ejecuta una consulta de elementos A, se iniciará una consulta SQL para recuperar los datos A, y para cada objeto A en el resultado, se realizará una consulta SQL se ejecutará para cargar la colección correspondiente bs.

  3. Si usa Criteria para construir la consulta, puede cambiar el modo de recuperación de la colección bs de una manera similar a la HQL JOIN FETCH:

    Criteria c = session.createCriteria(A.class);
    c.setFetchMode("bs", FetchMode.JOIN);
    c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    
1
JMSilla 29 dic. 2016 a las 13:49