'Strange bootloader casting issue caused by different packages: "class [Ljava.lang.Object; cannot be cast to class ([Ljava.lang.Object; "

I'm trying to create a nativeQuery in Spring Boot, but I'm getting a strange bootloader message when I try it:

"class [Ljava.lang.Object; cannot be cast to class com.sick.as.models.common.v1.INorcaSummarySystemDTO ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; com.sick.as.models.common.v1.INorcaSummarySystemDTO is in unnamed module of loader 'app')",

Here's the code which triggers the exception:

@Override
public List<INorcaSummarySystemDTO> getNorcaSummaryBySystem(Long startDate, Long endDate){
    
    
Query nativeQuery = this.entityManager.createNativeQuery(norcaSummaryBySystemAndDeviceQuery);
nativeQuery.setParameter("startDate", startDate);
nativeQuery.setParameter("endDate", endDate);

List<INorcaSummarySystemDTO> results =  (List<INorcaSummarySystemDTO>) nativeQuery.getResultList();


for (INorcaSummarySystemDTO  norcaSummarySystemDTO:results) {
    LOG.error(norcaSummarySystemDTO.toString());     <========== exception here
}

The exception is generated on the LOG message.

When I hover over the results returned, it shows a list of objects rather than INorcaSummarySystemDTO

[[Ljava.lang.Object;@9648014, [Ljava.lang.Object;@35cfd4d1, [Ljava.lang.Object;@39be3202, [Ljava.lang.Object;@42efd1c9, [Ljava.lang.Object;@6fa68da0, [Ljava.lang.Object;

Based on the error message shown at the start (which is returned to Postman from an API call), there apparently is an issue with the fact that the executing code is getting the DTO from a different bootloader.

For reference, here is the code for the interface and the implementing class:

package com.sick.as.models.common.v1;


public interface INorcaSummarySystemDTO extends Comparable<INorcaSummarySystemDTO> {
        
    
        /**
         * @return system name
         */
    
        public String getSystemName();
        
        public void setSystemName(String systemName);

        /**
         * @return system label
         */
    
        public String getSystemLabel();

        public void setSystemLabel(String systemLabel);

        
        /**
         * @return Feature Vector
         */     
        public Long getFeatureVector();

        public void setFeatureVector(Long featureVector);
        
        /**
         * @return Norca Code
         */     
        public String getNorcaCode();

        public void setNorcaCode(String norcaCode);
        
        /**
         * @return count
         */     
        
        public Integer getSum();

        public void setSum(Integer sum);

}

package com.sick.as.models.common.v1;

import java.util.Comparator;

public class NorcaSummarySystemDTO implements INorcaSummarySystemDTO {

private String systemName; 
private String systemLabel;     
private Long featureVector;
private String norcaCode;
private Integer sum;

public NorcaSummarySystemDTO() {
}

    public NorcaSummarySystemDTO(INorcaSummarySystemDTO extractDTO) {
        super();
        this.systemName = extractDTO.getSystemName();
        this.systemLabel = extractDTO.getSystemLabel(); 
        this.featureVector = extractDTO.getFeatureVector();
        this.norcaCode = extractDTO.getNorcaCode();
        this.sum = extractDTO.getSum();
    }

    
    public String getSystemName() {
        return systemName;
    }
    public void setSystemName(String systemName) {
        this.systemName = systemName;
    }
    public String getSystemLabel() {
        return systemLabel;
    }
    public void setSystemLabel(String systemLabel) {
        this.systemLabel = systemLabel;
    }
    public Long getFeatureVector() {
        return featureVector;
    }
    public void setFeatureVector(Long featureVector) {
        this.featureVector = featureVector;
    }
    public String getNorcaCode() {
        return norcaCode;
    }
    public void setNorcaCode(String norcaCode) {
        this.norcaCode = norcaCode;
    }
    public Integer getSum() {
        return sum;
    }
    public void setSum(Integer sum) {
        this.sum = sum;
    }

    @Override
    public int compareTo(INorcaSummarySystemDTO o){
        return Comparator.comparing(INorcaSummarySystemDTO::getSystemName)
                  .thenComparing(INorcaSummarySystemDTO::getFeatureVector)
                  .compare(this, o);
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((featureVector == null) ? 0 : featureVector.hashCode());
        result = prime * result + ((systemName == null) ? 0 : systemName.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        NorcaSummarySystemDTO other = (NorcaSummarySystemDTO) obj;
        if (featureVector == null) {
            if (other.featureVector != null)
                return false;
        } else if (!featureVector.equals(other.featureVector))
            return false;
        if (systemName == null) {
            if (other.systemName != null)
                return false;
        } else if (!systemName.equals(other.systemName))
            return false;
        return true;
    }

    
}

Any ideas what's causing this? The code is based on an example in this url:

https://thorben-janssen.com/jpa-native-queries/


Solution 1:[1]

Effectively the reason is that JPA does not know of any way convert the Object that is read, into your desired POJO.

One way you can achieve is to declare your DTO as an @Entity which will ensure that it is JPA managed and then you can call the following overloaded method of createNativeQuery :

    /**
     * Create an instance of <code>Query</code> for executing
     * a native SQL query.
     * @param sqlString a native SQL query string
     * @param resultClass the class of the resulting instance(s)
     * @return the new query instance
     */
    public Query createNativeQuery(String sqlString, Class resultClass);

    //-----------------
    Query nativeQuery = 
    this.entityManager.createNativeQuery(norcaSummaryBySystemAndDeviceQuery, NorcaSummarySystemDTO.class);

Once you declare it as an entity there are multiple ways where you can map the result to the DTO.

You can also try one of the manual way , which is get the list and individually initialize the dto by fetching individual records.

List<Object[]>  results =  nativeQuery.getResultList();
for(Object[] entry: results){
   //init NorcaSummarySystemDTO
   // entry[0] - > set first attribute 
   // entry[1] - > set second attribute and so on
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rambler