org.apache.openejb.util.proxy.QueryProxy Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.openejb.util.proxy;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class QueryProxy implements InvocationHandler {
private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB, QueryProxy.class);
// keywords
public static final String PERSIST_NAME = "save";
public static final String MERGE_NAME = "update";
public static final String REMOVE_NAME = "delete";
public static final String NAMED_QUERY_NAME = "namedQuery";
public static final String NATIVE_QUERY_NAME = "nativeQuery";
public static final String QUERY_NAME = "query";
public static final String FIND_PREFIX = "find";
public static final String BY = "By";
public static final String AND = "And";
// cache for finders of the current instance
private final Map> returnsTypes = new ConcurrentHashMap>();
private final Map> conditions = new ConcurrentHashMap>();
private EntityManager em;
private enum QueryType {
NAMED, NATIVE, OTHER
}
public void setEntityManager(final EntityManager entityManager) {
em = entityManager;
}
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return method.invoke(this, args);
}
final String methodName = method.getName();
final Class> returnType = method.getReturnType();
// simple cases
if (PERSIST_NAME.equals(methodName)) {
persist(args, returnType);
return null; // void
}
if (MERGE_NAME.equals(methodName)) {
return merge(args, returnType);
}
if (REMOVE_NAME.equals(methodName)) {
remove(args, returnType);
return null; // void
}
// queries
if (NAMED_QUERY_NAME.equals(methodName)) {
return query(method, args, QueryType.NAMED);
}
if (NATIVE_QUERY_NAME.equals(methodName)) {
return query(method, args, QueryType.NATIVE);
}
if (QUERY_NAME.equals(methodName)) {
return query(method, args, QueryType.OTHER);
}
// finders
if (methodName.startsWith(FIND_PREFIX)) {
return find(method, args);
}
throw new IllegalArgumentException("method not yet managed");
}
/**
* @param method the method
* @param args queryName (String) -> first parameter, parameters (Map) or (Object[]), first and max (int) -> max follows first
* @param type the query type
* @return the expected result
*/
private Object query(final Method method, final Object[] args, final QueryType type) {
if (args.length < 1) {
throw new IllegalArgumentException("query() needs at least the query name");
}
int matched = 0;
Query query;
if (String.class.isAssignableFrom(args[0].getClass())) {
switch (type) {
case NAMED:
query = em.createNamedQuery((String) args[0]);
break;
case NATIVE:
query = em.createNativeQuery((String) args[0]);
break;
default:
query = em.createQuery((String) args[0]);
}
matched++;
for (int i = 1; i < args.length; i++) {
if (args[i] == null) {
continue;
}
if (Map.class.isAssignableFrom(args[i].getClass())) {
for (final Map.Entry entry : ((Map) args[i]).entrySet()) {
query = query.setParameter(entry.getKey(), entry.getValue());
}
matched++;
} else if (args[i].getClass().isArray()) {
final Object[] array = (Object[]) args[i];
for (int j = 0; j < array.length; j++) {
query = query.setParameter(j, array[j]);
}
matched++;
} else if (isInt(args[i].getClass())) {
final int next = i + 1;
if (args.length == next || !isInt(args[next].getClass())) {
throw new IllegalArgumentException("if you provide a firstResult (first int parameter)" +
"you should provide a maxResult too");
}
final int first = (Integer) args[i];
final int max = (Integer) args[next];
query = query.setFirstResult(first);
query = query.setMaxResults(max);
matched += 2;
i++;
} else {
throw new IllegalArgumentException("not managed parameter " + args[i]
+ " of type " + args[i].getClass());
}
}
if (matched != args.length) {
throw new IllegalArgumentException("all argument was not used, please check you signature looks like:" +
" query(String name, Map parameters, int firstResult, int maxResult)");
}
} else {
throw new IllegalArgumentException("query() needs at least the query name of type String");
}
return getQueryResult(method, query);
}
private Class> getReturnedType(final Method method) {
final String methodName = method.getName();
final Class> type;
if (returnsTypes.containsKey(methodName)) {
type = returnsTypes.get(methodName);
} else {
type = getGenericType(method.getGenericReturnType());
returnsTypes.put(methodName, type);
}
return type;
}
private Object getQueryResult(final Method method, final Query query) {
if (Collection.class.isAssignableFrom(method.getReturnType())) {
return query.getResultList();
}
return query.getSingleResult();
}
private Object find(final Method method, final Object[] args) {
final String methodName = method.getName();
final Class> type = getReturnedType(method);
final Query query = createFinderQuery(em, methodName, type, args);
return getQueryResult(method, query);
}
private void remove(final Object[] args, final Class> returnType) {
if (args != null && args.length == 1 && returnType.equals(Void.TYPE)) {
Object entity = args[0];
if (!em.contains(entity)) { // reattach the entity if possible
final Class> entityClass = entity.getClass();
final EntityType extends Object> et = em.getMetamodel().entity(entityClass);
if (!et.hasSingleIdAttribute()) {
throw new IllegalArgumentException("Dynamic EJB doesn't manage IdClass yet");
}
SingularAttribute, ?> id = null; // = et.getId(entityClass); doesn't work with openJPA
for (final SingularAttribute, ?> sa : et.getSingularAttributes()) {
if (sa.isId()) {
id = sa;
break;
}
}
if (id == null) {
throw new IllegalArgumentException("id field not found");
}
final String idName = id.getName();
final Object idValue;
try {
idValue = BeanUtils.getProperty(entity, idName);
} catch (final InvocationTargetException e) {
throw new IllegalArgumentException("can't invoke to get entity id");
} catch (final NoSuchMethodException e) {
throw new IllegalArgumentException("can't find the method to get entity id");
} catch (final IllegalAccessException e) {
throw new IllegalArgumentException("can't access field/method to get entity id");
}
entity = em.getReference(et.getJavaType(), idValue);
if (entity == null) {
throw new IllegalArgumentException("entity " + entity + " is not managed and can't be found.");
}
}
em.remove(entity);
} else {
throw new IllegalArgumentException(REMOVE_NAME + " should have only one parameter and return void");
}
}
private Object merge(final Object[] args, final Class> returnType) {
if (args != null && args.length == 1 && returnType.equals(args[0].getClass())) {
return em.merge(args[0]);
} else {
throw new IllegalArgumentException(MERGE_NAME + " should have only one parameter and return the same" +
" type than the parameter type");
}
}
private void persist(final Object[] args, final Class> returnType) {
if (args != null && args.length == 1 && returnType.equals(Void.TYPE)) {
em.persist(args[0]);
} else {
throw new IllegalArgumentException(PERSIST_NAME + " should have only one parameter and return void");
}
}
private Class> getGenericType(final Type type) {
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
if (pt.getActualTypeArguments().length == 1) {
return (Class>) pt.getActualTypeArguments()[0];
}
}
return Class.class.cast(type);
}
private Query createFinderQuery(final EntityManager entityManager, final String methodName, final Class entityType, final Object[] args) {
final List conditions = parseMethodName(methodName);
final EntityType et = entityManager.getMetamodel().entity(entityType);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery
© 2015 - 2025 Weber Informatics LLC | Privacy Policy