0

I have this sql query I am running in my database:

Select ce.external_id, ce.isid, ji.name, ji.controlled_status, ji.jurisdiction_name, ji.result_comments, ji.code_name, s.corporate_id from cs_jurisdiction_information ji Join substance s on ji.substance_id = s.id Join controlled_event ce on ce.id = s.controlled_event_id and ce.calling_system_id = 402 and ce.external_id = 'EXP-raoan-000011774';

Results

Which produces the above results. Everything following the first two fields are compounds that I would like to map to my model, so when I run this query from my repository:

public interface ExperimentDetailsRepository extends JpaRepository<ExperimentDetails, String> {

    @Query(value = "Select ce.external_id, ce.isid, ji.name, ji.controlled_status, " +
    "ji.jurisdiction_name, ji.result_comments," +
    "ji.code_name, s.corporate_id " +
    "from cs_jurisdiction_information ji " +
    "Join substance s on ji.substance_id = s.id " +
    "Join controlled_event ce on ce.id = s.controlled_event_id " +
    "and ce.calling_system_id = 402 " +
    "and ce.external_id = :externalId", nativeQuery = true)
    List<ExperimentDetails> findByExternalId(@Param("externalId") String externalId);
}

I first tried this model:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class ExperimentDetails {
    @Id
    private String externalId;
    private String isid;
    private String name;
    private String controlledStatus;
    private String jurisdictionName;
    private String resultComments;
    private String codeName;
    private String corporateId;
}

Calling findByExternalId(externalId) in my service class produces the incorrect results, the method returns 3 objects which is fine, but only maps the first compound Cathine, and duplicates the result for the next two objects, which makes sense because my structure here is flat, when it actually should be an array for anything after name attribute.

So I tried using @Embeddable annotation to modify the model to the correct structure as below:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class ExperimentDetails {
    @Id
    private String externalId;
    private String isid;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "name", column = @Column(name = "name")),
        @AttributeOverride(name = "controlledStatus", column = @Column(name = "controlled_status")),
        @AttributeOverride(name = "jurisdictionName", column = @Column(name = "jurisdiction_name")),
        @AttributeOverride(name = "resultComments", column = @Column(name = "result_comments")),
        @AttributeOverride(name = "codeName", column = @Column(name = "code_name")),
        @AttributeOverride(name = "corporateId", column = @Column(name = "corporate_id")),
    })
    private Compound compounds;
}

and I created the Embeddable class:

@Embeddable
@Data
public class Compound {
    private String name;
    private String controlledStatus;
    private String jurisdictionName;
    private String resultComments;
    private String codeName;
    private String corporateId;
}

This duplicates the same error in results as the previous model, which I then realized still doesn't produce the array of compounds, so I modified the model again:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class ExperimentDetails {
    @Id
    private String externalId;
    private String isid;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "name", column = @Column(name = "name")),
        @AttributeOverride(name = "controlledStatus", column = @Column(name = "controlled_status")),
        @AttributeOverride(name = "jurisdictionName", column = @Column(name = "jurisdiction_name")),
        @AttributeOverride(name = "resultComments", column = @Column(name = "result_comments")),
        @AttributeOverride(name = "codeName", column = @Column(name = "code_name")),
        @AttributeOverride(name = "corporateId", column = @Column(name = "corporate_id")),
    })
    private List<Compound> compounds;
}

This time making Compound a list. However, running findByExternalId(externalId) now produces null for the embedded class, so no compounds are actually mapped.

How can I map my model correctly to produce the correct results for my query?

Altaf
  • 399
  • 1
  • 5
  • 15
  • Your first attempt is fine, but you mapped the ExperimentDetails instance as an entity and marked externalId as THE unique identifier when it is certainly not unique. Since multiple rows of your query returned the same externalId, JPA returns the same instance for those rows - that is the point of object identity within entities. So you don't really want a managed entity for this. If you map the underlying tables 1:1 with entity classes, you can easily do something in JPQL – Chris Feb 03 '23 at 19:24
  • something like like JPQL query ""Select package.ExperimentDetails(ce.externalId, ce.isid, ji.name.. ) from JurisdictionInfo ji join ji.substance s join s.controlledEvent ce on ce.callingSystemId = 402 AND ce.externalId = :externalId" – Chris Feb 03 '23 at 19:25
  • Thanks @Chris. The `externalId` is unique, but `ExperimentDetails` can contain many compounds, so it's the way my query is written, I was just returning the `externalId` and `ISID`, so I can map it using a `HashMap` and return an API call in my service layer. If I understand you correctly you're saying take out `@Embeddable` make `Compound` table an entity class and annotate with `@ONETOMANY` bc each experiment can have more than one compound, and then instead of Native Query use a JPQL query, I am not familiar with JPQL, but I'll research it. Is that correct? – Altaf Feb 03 '23 at 19:52
  • No, I've suggested you make 3 entities, one for each table in your Native SQL query, and map them as they look in the database. JPQL is pretty similar to SQL, the difference being it is more DB agnostic and based on your entity/java objects, not table constructs. It isn't necessary to be able to build the scalar results from the Native SQL query into a pojo object as I've shown in the JPQL query, but it is the easiest to show and describe - I don't know how to get Spring to use a Result set mapping but it is possible – Chris Feb 03 '23 at 20:57
  • 1
    see https://stackoverflow.com/a/13013323/496099 , https://stackoverflow.com/questions/13012584/jpa-how-to-convert-a-native-query-result-set-to-pojo-class-collection and https://thorben-janssen.com/spring-data-jpa-dto-native-queries/ – Chris Feb 03 '23 at 20:59
  • used the thorben-janssen article to solve my issue thanks – Altaf Feb 04 '23 at 06:07

0 Answers0