spring-data-commons icon indicating copy to clipboard operation
spring-data-commons copied to clipboard

Projection Not working for Array Of Object [DATACMNS-1656]

Open spring-projects-issues opened this issue 6 years ago • 7 comments

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

spring-projects-issues avatar Jan 17 '20 19:01 spring-projects-issues

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

spring-projects-issues avatar Jan 17 '20 20:01 spring-projects-issues

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

spring-projects-issues avatar Jan 17 '20 20:01 spring-projects-issues

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

spring-projects-issues avatar Jan 17 '20 21:01 spring-projects-issues

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.

 

 

 

spring-projects-issues avatar Jan 17 '20 21:01 spring-projects-issues

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

spring-projects-issues avatar Jan 17 '20 21:01 spring-projects-issues

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();

spring-projects-issues avatar Jan 17 '20 22:01 spring-projects-issues

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.

mp911de avatar Jan 18 '21 13:01 mp911de