Projection Not working for Array Of Object [DATACMNS-1656]
vr025 opened DATACMNS-1656 and commented
We are using Projection to select just single Column that is String Array (String[]) and it is falling with Index out of bound exception.
Here is my table:
create table IF NOT EXISTS ORGANIZATION ( uuid uuid not NULL PRIMARY KEY, name varchar(127) not NULL, appliance_tags varchar(255)[] constraint name_key unique (name) );
Here is my original entity mapping:
@Entity
@TypeDefs(
{ @TypeDef( name = "string-array", typeClass = StringArrayType.class ), @TypeDef( name = "pgsql_enum", typeClass = PostgreSQLEnumType.class) }
)
@Table(name = "organization")
@XmlRootElement(name = "organization")
@JsonInclude(Include.NON_EMPTY)
public class Organization
@Column(name = "name")
private String name;
@Id
@Column(name = "uuid")
private UUID uuid;
@Type( type = "string-array" )
@Column(name = "appliance_tags")
private String [] applianceTags;
//getters and setters for the above fields
Here is Projection Interface:
@Projection(name="appliacneTags", types = {Organization.class})
public interface OrgTag
{ public String[] getApplianceTags(); }
Repository Query:
@Query(value = "select org.applianceTags from Organization as org")
public List<OrgTag> findAllTags();
Error Logs :
java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70) at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248) at java.base/java.util.Objects.checkIndex(Objects.java:372) at java.base/java.util.ArrayList.get(ArrayList.java:458) at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.toMap(ResultProcessor.java:310) at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.getProjectionTarget(ResultProcessor.java:298) at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:285) at org.springframework.data.repository.query.ResultProcessor$ChainingConverter$1.convert(ResultProcessor.java:210) at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:221) at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:152) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:133) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:118)
Affects: 2.2.4 (Moore SR4)
Reference URL: https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/query/ResultProcessor.java
Mark Paluch commented
If you would like us to spend some time helping you to diagnose the problem, please spend some time describing it and, ideally, providing a minimal sample that reproduces the problem
vr025 commented
I think toMap() method needs to check boundary condition and if it is array or list set value.
private Object getProjectionTarget(Object source) { private Object getProjectionTarget(Object source) {
if (source != null && source.getClass().isArray()) { source = Arrays.asList((Object[]) source); } if (source instanceof Collection) { return toMap((Collection<?>) source, type.getInputProperties()); } return source;
} private static Map<String, Object> toMap(Collection<?> values, List<String> names) { int i = 0; Map<String, Object> result = new HashMap<>(values.size()); for (Object element : values) { result.put(names.get(i++), element); } return result; }
Oliver Drotbohm commented
It looks like we're not properly handling the returned array and invalidly try to create a single projecting instance of OrgTag around the returned array.
That said, your projection declaration is invalid: the query returns an array of String. The result is declared as a List<OrgTag>. I.e. even if our code was working correctly you'd get us trying to project OrgTag onto a single String which would produce a proper instance but you're never gonna see getApplianceTags() working as that method does not exist on String.
You should be able to see this working by changing the type of applianceTags to List<String> instead of String[].
That said, please make sure you properly format code in the examples you provide using the \{{code}} tags
vr025 commented
I have a PostgreSQL table which I mentioned in the description. That table has a colunmn with String Array type. When I try to access that column values through projection, the ResultProcessor.java file is throwing "index out of bound exception", I pasted the errors for your reference.
There is no Boundary conditions checked for list or set from a column.
Oliver Drotbohm commented
Did you read my response to what you provided?
Also, please refrain from messing with the ticket details. We use those for internal organization. Thanks
vr025 commented
After changing the repository method to List<String[]> I'm able to get the tags array for all the names in the organization table. Thanks Oliver for your inputs. However please let me know how to use projections for String Array.
Now I completely removed projection interface and changed my repository query like below to get all the list of tags for each name.
Modified Repository Query is here :
@Query(value = "select org.applianceTags from Organization as org")
public List<String[]> findAllTags();
Projections are intended to retain their multiplicity. That being said, a String[] must remain a collection type of String and cannot be projected into a singular type.