Support list parameters for JPQL query (rollback our changes and use EL capability). #PL-5846 #PL-6108

This commit is contained in:
Konstantin Krivopustov 2015-10-07 06:58:47 +00:00
parent 24650c4d9f
commit 069b647bb9
10 changed files with 64 additions and 82 deletions

View File

@ -398,7 +398,7 @@ public class DataManagerBean implements DataManager {
View view = new View(baseAttributeValueView, null, false) View view = new View(baseAttributeValueView, null, false)
.addProperty("categoryAttribute", new View(baseAttributeView, null, false).addProperty("category")); .addProperty("categoryAttribute", new View(baseAttributeView, null, false).addProperty("category"));
return em.createQuery("select cav from sys$CategoryAttributeValue cav where cav.entityId in (:ids)", CategoryAttributeValue.class) return em.createQuery("select cav from sys$CategoryAttributeValue cav where cav.entityId in :ids", CategoryAttributeValue.class)
.setParameter("ids", entityIds) .setParameter("ids", entityIds)
.setView(view) .setView(view)
.getResultList(); .getResultList();

View File

@ -7,16 +7,15 @@ package com.haulmont.cuba.core.sys;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.haulmont.bali.util.ReflectionHelper; import com.haulmont.bali.util.ReflectionHelper;
import com.haulmont.cuba.core.TypedQuery; import com.haulmont.cuba.core.TypedQuery;
import com.haulmont.cuba.core.entity.BaseEntity;
import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.persistence.DbmsFeatures; import com.haulmont.cuba.core.sys.persistence.DbmsFeatures;
import com.haulmont.cuba.core.sys.persistence.DbmsSpecificFactory; import com.haulmont.cuba.core.sys.persistence.DbmsSpecificFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.persistence.config.HintValues; import org.eclipse.persistence.config.HintValues;
import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.jpa.JpaQuery; import org.eclipse.persistence.jpa.JpaQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
@ -135,8 +134,6 @@ public class QueryImpl<T> implements TypedQuery<T> {
for (Param param : params) { for (Param param : params) {
if (param.value instanceof String && ((String) param.value).startsWith("(?i)")) if (param.value instanceof String && ((String) param.value).startsWith("(?i)"))
result = replaceCaseInsensitiveParam(result, param); result = replaceCaseInsensitiveParam(result, param);
if (param.value instanceof Collection)
result = replaceCollectionParam(result, param);
} }
return result; return result;
@ -164,27 +161,13 @@ public class QueryImpl<T> implements TypedQuery<T> {
return result; return result;
} }
private String replaceCollectionParam(String queryStr, Param param) {
if (!(param.name instanceof String))
throw new UnsupportedOperationException("Only named collection parameters are supported");
Collection collection = (Collection) param.value;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < collection.size(); i++) {
sb.append(":").append(param.name).append(i+1);
if (i < collection.size() - 1)
sb.append(",");
}
return queryStr.replace(":" + param.name, sb.toString());
}
private void addMacroParams(javax.persistence.TypedQuery jpaQuery) { private void addMacroParams(javax.persistence.TypedQuery jpaQuery) {
if (macroHandlers != null) { if (macroHandlers != null) {
for (QueryMacroHandler handler : macroHandlers) { for (QueryMacroHandler handler : macroHandlers) {
Map<String, Object> namedParams = new HashMap<>(); Map<String, Object> namedParams = new HashMap<>();
for (Param param : params) { for (Param param : params) {
if (param.name instanceof String && !(param.value instanceof Collection)) if (param.name instanceof String)
namedParams.put((String) param.name, param.value); namedParams.put((String) param.name, param.value);
} }
handler.setQueryParams(namedParams); handler.setQueryParams(namedParams);
@ -263,20 +246,25 @@ public class QueryImpl<T> implements TypedQuery<T> {
checkState(); checkState();
if (implicitConversions) { if (implicitConversions) {
if (value instanceof Entity) value = handleImplicitConversions(value);
value = ((BaseEntity) value).getId();
else if (value instanceof Collection) {
List<Object> list = new ArrayList<>(((Collection) value).size());
for (Object obj : ((Collection) value)) {
list.add(obj instanceof Entity ? ((Entity) obj).getId() : obj);
}
value = list;
}
} }
params.add(new Param(name, value)); params.add(new Param(name, value));
return this; return this;
} }
private Object handleImplicitConversions(Object value) {
if (value instanceof Entity)
value = ((Entity) value).getId();
else if (value instanceof Collection) {
List<Object> list = new ArrayList<>(((Collection) value).size());
for (Object obj : ((Collection) value)) {
list.add(obj instanceof Entity ? ((Entity) obj).getId() : obj);
}
value = list;
}
return value;
}
@Override @Override
public TypedQuery<T> setParameter(String name, Date value, TemporalType temporalType) { public TypedQuery<T> setParameter(String name, Date value, TemporalType temporalType) {
checkState(); checkState();
@ -298,10 +286,11 @@ public class QueryImpl<T> implements TypedQuery<T> {
try { try {
value = ReflectionHelper.newInstance(c, value); value = ReflectionHelper.newInstance(c, value);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
throw new RuntimeException(e); throw new RuntimeException("Error setting parameter value", e);
} }
} else if (implicitConversions && value instanceof Entity) } else if (implicitConversions) {
value = ((BaseEntity) value).getId(); value = handleImplicitConversions(value);
}
params.add(new Param(position, value)); params.add(new Param(position, value));
return this; return this;
@ -405,29 +394,15 @@ public class QueryImpl<T> implements TypedQuery<T> {
public void apply(JpaQuery query) { public void apply(JpaQuery query) {
if (temporalType != null) { if (temporalType != null) {
if (name instanceof Integer) { if (name instanceof Integer)
query.setParameter((int) name, (Date) value, temporalType); query.setParameter((int) name, (Date) value, temporalType);
} else { else
query.setParameter((String) name, (Date) value, temporalType); query.setParameter((String) name, (Date) value, temporalType);
}
} else { } else {
if (name instanceof Integer) { if (name instanceof Integer)
query.setParameter((int) name, value); query.setParameter((int) name, value);
} else { else
if (value instanceof Collection) { query.setParameter((String) name, value);
applyCollection(query, (String) name, (Collection) value);
} else {
query.setParameter((String) name, value);
}
}
}
}
private void applyCollection(JpaQuery query, String name, Collection collection) {
int i = 1;
for (Object value : collection) {
query.setParameter(name + i, value);
i++;
} }
} }

View File

@ -125,7 +125,7 @@ public class UserManagementServiceBean implements UserManagementService {
throw new IllegalStateException("Could not found target access group with id: " + targetAccessGroupId); throw new IllegalStateException("Could not found target access group with id: " + targetAccessGroupId);
} }
TypedQuery<User> query = em.createQuery("select u from sec$User u where u.id in (:userIds)", User.class); TypedQuery<User> query = em.createQuery("select u from sec$User u where u.id in :userIds", User.class);
query.setParameter("userIds", userIds); query.setParameter("userIds", userIds);
query.setViewName(MOVE_USER_TO_GROUP_VIEW); query.setViewName(MOVE_USER_TO_GROUP_VIEW);
@ -232,7 +232,7 @@ public class UserManagementServiceBean implements UserManagementService {
try { try {
EntityManager em = persistence.getEntityManager(); EntityManager em = persistence.getEntityManager();
Query query = em.createQuery("delete from sec$RememberMeToken rt where rt.user.id in (:userIds)"); Query query = em.createQuery("delete from sec$RememberMeToken rt where rt.user.id in :userIds");
query.setParameter("userIds", userIds); query.setParameter("userIds", userIds);
query.executeUpdate(); query.executeUpdate();
@ -445,7 +445,7 @@ public class UserManagementServiceBean implements UserManagementService {
try { try {
EntityManager em = persistence.getEntityManager(); EntityManager em = persistence.getEntityManager();
TypedQuery<User> query = em.createQuery("select u from sec$User u where u.id in (:userIds)", User.class); TypedQuery<User> query = em.createQuery("select u from sec$User u where u.id in :userIds", User.class);
query.setParameter("userIds", userIds); query.setParameter("userIds", userIds);
query.setViewName(RESET_PASSWORD_VIEW); query.setViewName(RESET_PASSWORD_VIEW);

View File

@ -304,7 +304,7 @@ public class QueryTest extends CubaTestCase {
public void testListParameter() throws Exception { public void testListParameter() throws Exception {
try (Transaction tx = persistence.createTransaction()) { try (Transaction tx = persistence.createTransaction()) {
TypedQuery<User> query = persistence.getEntityManager().createQuery( TypedQuery<User> query = persistence.getEntityManager().createQuery(
"select u from sec$User u where u.id in (:ids) order by u.createTs", User.class); "select u from sec$User u where u.id in :ids order by u.createTs", User.class);
query.setParameter("ids", Arrays.asList(UUID.fromString("60885987-1b61-4247-94c7-dff348347f93"), userId, user2Id)); query.setParameter("ids", Arrays.asList(UUID.fromString("60885987-1b61-4247-94c7-dff348347f93"), userId, user2Id));
List<User> list = query.getResultList(); List<User> list = query.getResultList();
assertEquals(3, list.size()); assertEquals(3, list.size());
@ -326,7 +326,7 @@ public class QueryTest extends CubaTestCase {
try (Transaction tx = persistence.createTransaction()) { try (Transaction tx = persistence.createTransaction()) {
TypedQuery<User> query = persistence.getEntityManager().createQuery( TypedQuery<User> query = persistence.getEntityManager().createQuery(
"select u from sec$User u where u.id in (:ids) order by u.createTs", User.class); "select u from sec$User u where u.id in :ids order by u.createTs", User.class);
query.setParameter("ids", Arrays.asList(user1, user2, user3)); query.setParameter("ids", Arrays.asList(user1, user2, user3));
List<User> list = query.getResultList(); List<User> list = query.getResultList();
assertEquals(3, list.size()); assertEquals(3, list.size());
@ -334,18 +334,26 @@ public class QueryTest extends CubaTestCase {
tx.commit(); tx.commit();
} }
// Positional parameters are not supported // Positional parameters
try (Transaction tx = persistence.createTransaction()) { try (Transaction tx = persistence.createTransaction()) {
TypedQuery<User> query = persistence.getEntityManager().createQuery( TypedQuery<User> query = persistence.getEntityManager().createQuery(
"select u from sec$User u where u.id in (?1) order by u.createTs", User.class); "select u from sec$User u where u.id in ?1 order by u.createTs", User.class);
query.setParameter(1, Arrays.asList(user1.getId(), user2.getId(), user3.getId()));
List<User> list = query.getResultList();
assertEquals(3, list.size());
tx.commit();
}
// Positional parameters with implicit conversion
try (Transaction tx = persistence.createTransaction()) {
TypedQuery<User> query = persistence.getEntityManager().createQuery(
"select u from sec$User u where u.id in ?1 order by u.createTs", User.class);
query.setParameter(1, Arrays.asList(user1, user2, user3)); query.setParameter(1, Arrays.asList(user1, user2, user3));
try { List<User> list = query.getResultList();
query.getResultList(); assertEquals(3, list.size());
fail();
} catch (UnsupportedOperationException e) {
// ok
}
tx.commit(); tx.commit();
} }

View File

@ -6,7 +6,7 @@
package com.haulmont.cuba.core.global; package com.haulmont.cuba.core.global;
import com.haulmont.cuba.core.sys.jpql.DomainModel; import com.haulmont.cuba.core.sys.jpql.DomainModel;
import com.haulmont.cuba.core.sys.jpql.ErrorsFoundException; import com.haulmont.cuba.core.sys.jpql.QueryErrorsFoundException;
import com.haulmont.cuba.core.sys.jpql.model.Entity; import com.haulmont.cuba.core.sys.jpql.model.Entity;
import com.haulmont.cuba.core.sys.jpql.model.EntityBuilder; import com.haulmont.cuba.core.sys.jpql.model.EntityBuilder;
import com.haulmont.cuba.core.sys.jpql.model.EntityImpl; import com.haulmont.cuba.core.sys.jpql.model.EntityImpl;
@ -602,7 +602,7 @@ public class QueryTransformerAstBasedTest {
new QueryTransformerAstBased(model, new QueryTransformerAstBased(model,
"select h from sec$GroupHierarchy h join h.parent.constraints c group by c.level order by c.level having c.level > 0"); "select h from sec$GroupHierarchy h join h.parent.constraints c group by c.level order by c.level having c.level > 0");
fail("Incorrectly placed 'having' passed"); fail("Incorrectly placed 'having' passed");
} catch (ErrorsFoundException e) { } catch (QueryErrorsFoundException e) {
} }
} }
@ -614,7 +614,7 @@ public class QueryTransformerAstBasedTest {
new QueryTransformerAstBased(model, new QueryTransformerAstBased(model,
"select h from sec$GroupHierarchy h join h.parent.constraints"); "select h from sec$GroupHierarchy h join h.parent.constraints");
fail("Not named join variable passed"); fail("Not named join variable passed");
} catch (ErrorsFoundException e) { } catch (QueryErrorsFoundException e) {
} }
} }

View File

@ -108,14 +108,14 @@ public class QueryTransformerSoftDeleteBugsTest extends TestCase {
QueryTransformerRegex transformer = new QueryTransformerRegex( QueryTransformerRegex transformer = new QueryTransformerRegex(
"select j from taxi$IndividualTelephone it, taxi$Job j\n" + "select j from taxi$IndividualTelephone it, taxi$Job j\n" +
"where it.telephone like :phoneNumber and it.individual.id = j.caller.id\n" + "where it.telephone like :phoneNumber and it.individual.id = j.caller.id\n" +
"and j.executionStatus not in (:notActiveStatuses)"); "and j.executionStatus not in :notActiveStatuses");
transformer.addWhere("{E}.deleteTs is null"); transformer.addWhere("{E}.deleteTs is null");
String res = transformer.getResult(); String res = transformer.getResult();
assertEquals( assertEquals(
"select j from taxi$IndividualTelephone it, taxi$Job j\n" + "select j from taxi$IndividualTelephone it, taxi$Job j\n" +
"where it.telephone like :phoneNumber and it.individual.id = j.caller.id\n" + "where it.telephone like :phoneNumber and it.individual.id = j.caller.id\n" +
"and j.executionStatus not in (:notActiveStatuses) and (it.deleteTs is null)", "and j.executionStatus not in :notActiveStatuses and (it.deleteTs is null)",
res); res);
} }
} }

View File

@ -12,13 +12,14 @@ import java.util.List;
* @author chevelev * @author chevelev
* @version $Id$ * @version $Id$
*/ */
public class ErrorsFoundException extends RuntimeException{ public class QueryErrorsFoundException extends RuntimeException{
private List<ErrorRec> errorRecs; private List<ErrorRec> errorRecs;
public ErrorsFoundException() { public QueryErrorsFoundException() {
} }
public ErrorsFoundException(String message, List<ErrorRec> errorRecs) { public QueryErrorsFoundException(String message, List<ErrorRec> errorRecs) {
super(message); super(message);
this.errorRecs = new ArrayList<>(errorRecs); this.errorRecs = new ArrayList<>(errorRecs);
} }
@ -28,11 +29,11 @@ public class ErrorsFoundException extends RuntimeException{
} }
@Override @Override
public String toString() { public String getMessage() {
String result = ""; String message = super.getMessage();
for (ErrorRec rec : errorRecs) { for (ErrorRec rec : errorRecs) {
result += rec + "\n"; message += "\n" + rec;
} }
return result; return message;
} }
} }

View File

@ -69,7 +69,7 @@ public class QueryTransformerAstBased implements QueryTransformer {
} }
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
throw new ErrorsFoundException("Errors found", errors); throw new QueryErrorsFoundException("Errors found", errors);
} }
} }

View File

@ -150,8 +150,6 @@ public class BulkEditorWindow extends AbstractWindow {
datasource.setAllowCommit(false); datasource.setAllowCommit(false);
createDataComponents(); createDataComponents();
datasource.refresh();
} }
protected void createDataComponents() { protected void createDataComponents() {
@ -552,7 +550,7 @@ public class BulkEditorWindow extends AbstractWindow {
} }
protected List<Entity> loadItems(View view) { protected List<Entity> loadItems(View view) {
LoadContext lc = new LoadContext(metaClass); LoadContext<Entity> lc = new LoadContext<>(metaClass);
lc.setSoftDeletion(false); lc.setSoftDeletion(false);
List<UUID> ids = new ArrayList<>(); List<UUID> ids = new ArrayList<>();

View File

@ -41,7 +41,7 @@ public class UserSetHelper {
String listOfId = createIdsString(ids); String listOfId = createIdsString(ids);
String randomName = RandomStringUtils.randomAlphabetic(10); String randomName = RandomStringUtils.randomAlphabetic(10);
condition.addText(entityAlias + ".id in (:component$" + componentId + "." + randomName + ")"); condition.addText(entityAlias + ".id in :component$" + componentId + "." + randomName);
Element param = condition.addElement("param"); Element param = condition.addElement("param");
param.addAttribute("name", "component$" + componentId + "." + randomName); param.addAttribute("name", "component$" + componentId + "." + randomName);