-
Notifications
You must be signed in to change notification settings - Fork 176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JPQL queries that select ElementCollection attributes have incorrect results #2193
Comments
I am looking into this issue |
Please attach there entities code. |
Hi @rfelcman Please find the entity below :
|
Hi @rfelcman Please find the recreate details : @Test
public void testElementCollection() throws Exception {
ECEntity e1 = new ECEntity();
e1.setId("EC1");
e1.setIntArray(new int[] { 14, 12, 1 });
e1.setLongList(new ArrayList<>(List.of(14L, 12L, 1L)));
e1.setLongListEC(new ArrayList<>(List.of(14L, 12L, 1L)));
e1.setStringSet(Set.of("fourteen", "twelve", "one"));
e1.setStringSetEC(Set.of("fourteen", "twelve", "one"));
ECEntity e2 = new ECEntity();
e2.setId("EC2");
e2.setIntArray(new int[] { 14, 12, 2 });
e2.setLongList(new ArrayList<>(List.of(14L, 12L, 2L)));
e2.setLongListEC(new ArrayList<>(List.of(14L, 12L, 2L)));
e2.setStringSet(Set.of("fourteen", "twelve", "two"));
e2.setStringSetEC(Set.of("fourteen", "twelve", "two"));
tx.begin();
em.persist(e1);
em.persist(e2);
tx.commit();
// Test JPQL queries
String jpql;
List<?> results;
// Query for intArray
tx.begin();
try {
jpql = "SELECT intArray FROM ECEntity WHERE id=?1";
results = em.createQuery(jpql)
.setParameter(1, "EC1")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
// Query for longList
tx.begin();
try {
jpql = "SELECT longList FROM ECEntity WHERE id=?1";
results = em.createQuery(jpql)
.setParameter(1, "EC1")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
// Query for stringSet
tx.begin();
try {
jpql = "SELECT stringSet FROM ECEntity WHERE id=?1";
results = em.createQuery(jpql)
.setParameter(1, "EC1")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.begin();
try {
jpql = "SELECT longListEC FROM ECEntity WHERE id=?1";
results = em.createQuery(jpql)
.setParameter(1, "EC1")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
// Query for longListEC
tx.begin();
try {
jpql = "SELECT longListEC FROM ECEntity WHERE id LIKE ?1";
results = em.createQuery(jpql)
.setParameter(1, "EC%")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.begin();
try {
jpql = "SELECT longList FROM ECEntity WHERE id LIKE ?1";
results = em.createQuery(jpql)
.setParameter(1, "EC%")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
// Query for stringSetEC
tx.begin();
try {
jpql = "SELECT stringSetEC FROM ECEntity WHERE id LIKE ?1";
results = em.createQuery(jpql)
.setParameter(1, "EC%")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.begin();
try {
jpql = "SELECT stringSet FROM ECEntity WHERE id LIKE ?1";
results = em.createQuery(jpql)
.setParameter(1, "EC%")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.begin();
try {
jpql = "SELECT stringSetEC FROM ECEntity WHERE id=?1";
results = em.createQuery(jpql)
.setParameter(1, "EC1")
.getResultList();
logQueryResults(jpql,results);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
}
public void logQueryResults(String jpql, Collection<?> results) {
System.out.println();
System.out.println(jpql);
System.out.println("getResultList returned a " + results.getClass().getTypeName());
if (!results.isEmpty()) {
System.out.println(" elements are of type " + results.iterator().next().getClass().getTypeName());
} else {
System.out.println(" elements are of type <empty>");
}
StringBuilder s = new StringBuilder();
boolean first = true;
for (Object element : results) {
if (first)
first = false;
else
s.append(", ");
if (element instanceof int[])
s.append(Arrays.toString((int[]) element));
else
s.append(element);
}
System.out.println(" contents are [" + s.toString() + "]");
} ECEntity.java is like below : package io.openliberty.jpa.data.tests.models;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
/**
* Entity with and without ElementCollection attributes.
*/
@Entity
public class ECEntity {
@Id
String id;
int[] intArray = new int[] {};
ArrayList<Long> longList = new ArrayList<>();
@ElementCollection(fetch = FetchType.EAGER)
ArrayList<Long> longListEC = new ArrayList<>();
Set<String> stringSet = new HashSet<>();
@ElementCollection(fetch = FetchType.EAGER)
Set<String> stringSetEC = new HashSet<>();
public String getId() {
return id;
}
public int[] getIntArray() {
return intArray;
}
public ArrayList<Long> getLongList() {
return longList;
}
public ArrayList<Long> getLongListEC() {
return longListEC;
}
public Set<String> getStringSet() {
return stringSet;
}
public Set<String> getStringSetEC() {
return stringSetEC;
}
public void setId(String id) {
this.id = id;
}
public void setIntArray(int[] intArray) {
this.intArray = intArray;
}
public void setLongList(ArrayList<Long> longList) {
this.longList = longList;
}
public void setLongListEC(ArrayList<Long> longListEC) {
this.longListEC = longListEC;
}
public void setStringSet(Set<String> stringSet) {
this.stringSet = stringSet;
}
public void setStringSetEC(Set<String> stringSetEC) {
this.stringSetEC = stringSetEC;
}
} This demonstrates that EclipseLink is allowing ElementCollection attributes to be returned by JPQL queries, but with results that are both inconsistent with non-ElementCollection attributes of the same type, and are also incorrect. Especially when returning multiple results which get erroneously combined together into a single collection! Here is the output of this test. Entity attribute names ending in "EC" use ElementCollection. The others have the same types but don't use ElementCollection: SELECT intArray FROM ECEntity WHERE id=?1
getResultList returned a java.util.Vector
elements are of type int[]
contents are [[14, 12, 1]]
SELECT longList FROM ECEntity WHERE id=?1
getResultList returned a java.util.Vector
elements are of type java.util.ArrayList
contents are [[14, 12, 1]]
SELECT stringSet FROM ECEntity WHERE id=?1
getResultList returned a java.util.Vector
elements are of type java.util.ImmutableCollections$SetN
contents are [[fourteen, twelve, one]]
SELECT longListEC FROM ECEntity WHERE id=?1
getResultList returned a java.util.Vector
elements are of type java.lang.Long
contents are [14, 12, 1]
SELECT longListEC FROM ECEntity WHERE id LIKE ?1
getResultList returned a java.util.Vector
elements are of type java.lang.Long
contents are [14, 12, 1, 14, 12, 2]
SELECT longList FROM ECEntity WHERE id LIKE ?1
getResultList returned a java.util.Vector
elements are of type java.util.ArrayList
contents are [[14, 12, 1], [14, 12, 2]]
SELECT stringSetEC FROM ECEntity WHERE id LIKE ?1
getResultList returned a java.util.Vector
elements are of type java.lang.String
contents are [fourteen, twelve, one, fourteen, twelve, two]
SELECT stringSet FROM ECEntity WHERE id LIKE ?1
getResultList returned a java.util.Vector
elements are of type java.util.ImmutableCollections$SetN
contents are [[fourteen, twelve, one], [fourteen, twelve, two]]
SELECT stringSetEC FROM ECEntity WHERE id=?1
getResultList returned a java.util.Vector
elements are of type java.lang.String
contents are [fourteen, twelve, one]
<<< END: testElementCollection If EclipseLink wants to reject this outright because the Jakarta Persistence specification does not require element collections to be selected by JPQL queries, that would be perfectly fine. But if EclipseLink doesn't reject it, it at least needs to get the behavior correct and return valid results. |
Hi @rfelcman Please find the DDL statements below as you mentioned :
|
Describe the bug
When JPQL queries select an attribute that is a
ElementCollection
(if using annotations) orelement-collection
(if using XML) and getResultList is used to obtain the result, instead of returning a List of one or more Collection(s), EclipseLink appears to be combining all of the collection values into a single list of values and returning it to the caller.This can be surfaced to the application in a variety of ways, such as:
In the above case, the result list contained an
Integer
rather than aSet<Integer>
, despite issuing a querySELECT o.areaCodes FROM City o WHERE (o.name=?1 AND o.stateName=?2)
ono.areaCodes
which is a collection.In the above case, it complains about the value being used for
o.areaCodes
not being a collection, when it ought to be a collection becauseo.areaCodes
is defined as a collection on the entity.Steps to Reproduce
This fails a variety of test cases so there are various places where you can reproduce it.
One way: uncomment the line
@ElementCollection(fetch = FetchType.EAGER)
on test.jakarta.data.jpa.web.City and run the io.openliberty.data.internal_fat_jpa test bucket. The testtestCollectionAttribute
will start failing.Another way: uncomment the lines of
testRecordWithEmbeddables
in test.jakarta.data.web.DataTestServlet and run the io.openliberty.data.internal_fat test bucket.Another way: uncomment the lines of
testEmbeddableCollection
in test.jakarta.data.jpa.web.DataJPATestServlet and run the io.openliberty.data.internal_fat_jpa test bucket.Expected behavior
JPQL queries for an attribute that is a
Set<Integer>
should return a result list that containsSet<Integer>
, not Integer.JPQL queries that construct a new class instance with an attribute that is a
Set<Integer>
as a parameter should receive aSet<Integer>
and successfully construct an instance, not be rejected saying that the collection attribute is not a collection attribute.Diagnostic information:
server.xml configuration (WITHOUT sensitive information like passwords) see test buckets
The text was updated successfully, but these errors were encountered: