com.jaxio.jpa.querybyexample.JpaUtil Maven / Gradle / Ivy
Show all versions of jpa-querybyexample Show documentation
/*
* Copyright 2015 JAXIO http://www.jaxio.com
*
* Licensed 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 com.jaxio.jpa.querybyexample;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.WordUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.i18n.LocaleContextHolder;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.persistence.*;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.toArray;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static java.lang.reflect.Modifier.isPublic;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.hibernate.proxy.HibernateProxyHelper.getClassWithoutInitializingProxy;
@Named
@Singleton
@Lazy(false)
public class JpaUtil {
private Map, String> compositePkCache = newHashMap();
private static JpaUtil instance;
public static JpaUtil getInstance() {
return instance;
}
public JpaUtil() {
instance = this;
}
public boolean isEntityIdManuallyAssigned(Class> type) {
for (Method method : type.getMethods()) {
if (isPrimaryKey(method)) {
return isManuallyAssigned(method);
}
}
return false; // no pk found, should not happen
}
private boolean isPrimaryKey(Method method) {
return isPublic(method.getModifiers()) && (method.getAnnotation(Id.class) != null || method.getAnnotation(EmbeddedId.class) != null);
}
private boolean isManuallyAssigned(Method method) {
if (method.getAnnotation(Id.class) != null) {
return method.getAnnotation(GeneratedValue.class) == null;
}
return method.getAnnotation(EmbeddedId.class) != null;
}
public Predicate concatPredicate(SearchParameters sp, CriteriaBuilder builder, Predicate... predicatesNullAllowed) {
return concatPredicate(sp, builder, newArrayList(predicatesNullAllowed));
}
public Predicate concatPredicate(SearchParameters sp, CriteriaBuilder builder, Iterable predicatesNullAllowed) {
if (sp.isAndMode()) {
return andPredicate(builder, predicatesNullAllowed);
} else {
return orPredicate(builder, predicatesNullAllowed);
}
}
public Predicate andPredicate(CriteriaBuilder builder, Predicate... predicatesNullAllowed) {
return andPredicate(builder, newArrayList(predicatesNullAllowed));
}
public Predicate orPredicate(CriteriaBuilder builder, Predicate... predicatesNullAllowed) {
return orPredicate(builder, newArrayList(predicatesNullAllowed));
}
public Predicate andPredicate(CriteriaBuilder builder, Iterable predicatesNullAllowed) {
List predicates = newArrayList(filter(predicatesNullAllowed, notNull()));
if (predicates == null || predicates.isEmpty()) {
return null;
} else if (predicates.size() == 1) {
return predicates.get(0);
} else {
return builder.and(toArray(predicates, Predicate.class));
}
}
public Predicate orPredicate(CriteriaBuilder builder, Iterable predicatesNullAllowed) {
List predicates = newArrayList(filter(predicatesNullAllowed, notNull()));
if (predicates == null || predicates.isEmpty()) {
return null;
} else if (predicates.size() == 1) {
return predicates.get(0);
} else {
return builder.or(toArray(predicates, Predicate.class));
}
}
public Predicate stringPredicate(Expression path, Object attrValue, SearchMode searchMode, SearchParameters sp, CriteriaBuilder builder) {
if (sp.isCaseInsensitive()) {
path = builder.lower(path);
attrValue = ((String) attrValue).toLowerCase(LocaleContextHolder.getLocale());
}
switch (searchMode != null ? searchMode : sp.getSearchMode()) {
case EQUALS:
return builder.equal(path, attrValue);
case ENDING_LIKE:
return builder.like(path, "%" + attrValue);
case STARTING_LIKE:
return builder.like(path, attrValue + "%");
case ANYWHERE:
return builder.like(path, "%" + attrValue + "%");
case LIKE:
return builder.like(path, (String) attrValue); // assume user provide the wild cards
default:
throw new IllegalStateException("expecting a search mode!");
}
}
public Predicate stringPredicate(Expression path, Object attrValue, SearchParameters sp, CriteriaBuilder builder) {
return stringPredicate(path, attrValue, null, sp, builder);
}
/*
* Convert the passed propertyPath into a JPA path.
*
* Note: JPA will do joins if the property is in an associated entity.
*/
@SuppressWarnings("unchecked")
public Path getPath(Root root, List> attributes) {
Path> path = root;
for (Attribute, ?> attribute : attributes) {
boolean found = false;
if (path instanceof FetchParent) {
for (Fetch fetch : ((FetchParent, E>) path).getFetches()) {
if (attribute.getName().equals(fetch.getAttribute().getName()) && (fetch instanceof Join, ?>)) {
path = (Join) fetch;
found = true;
break;
}
}
}
if (!found) {
if (attribute instanceof PluralAttribute) {
path = ((From, ?>) path).join(attribute.getName(), JoinType.LEFT);
} else {
path = path.get(attribute.getName());
}
}
}
return (Path) path;
}
public void verifyPath(Attribute, ?>... path) {
verifyPath(newArrayList(path));
}
public void verifyPath(List> path) {
List> attributes = newArrayList(path);
Class> from = null;
if (attributes.get(0).isCollection()) {
from = ((PluralAttribute) attributes.get(0)).getElementType().getJavaType();
} else {
from = attributes.get(0).getJavaType();
}
attributes.remove(0);
for (Attribute, ?> attribute : attributes) {
if (!attribute.getDeclaringType().getJavaType().isAssignableFrom(from)) {
throw new IllegalStateException("Wrong path.");
}
from = attribute.getJavaType();
}
}
public > String compositePkPropertyName(T entity) {
Class> entityClass = entity.getClass();
if (compositePkCache.containsKey(entityClass)) {
return compositePkCache.get(entityClass);
}
for (Method m : entity.getClass().getMethods()) {
if (m.getAnnotation(EmbeddedId.class) != null) {
String propertyName = methodToProperty(m);
compositePkCache.put(entityClass, propertyName);
return propertyName;
}
}
for (Field f : entity.getClass().getFields()) {
if (f.getAnnotation(EmbeddedId.class) != null) {
String propertyName = f.getName();
compositePkCache.put(entityClass, propertyName);
return propertyName;
}
}
compositePkCache.put(entityClass, null);
return null;
}
public boolean isPk(ManagedType mt, SingularAttribute super T, ?> attr) {
try {
Method m = MethodUtils.getAccessibleMethod(mt.getJavaType(), "get" + WordUtils.capitalize(attr.getName()), (Class>) null);
if (m != null && m.getAnnotation(Id.class) != null) {
return true;
}
Field field = mt.getJavaType().getField(attr.getName());
return field.getAnnotation(Id.class) != null;
} catch (Exception e) {
return false;
}
}
public Object getValue(T example, Attribute super T, ?> attr) {
try {
if (attr.getJavaMember() instanceof Method) {
return ((Method) attr.getJavaMember()).invoke(example);
} else {
return ((Field) attr.getJavaMember()).get(example);
}
} catch (Exception e) {
throw propagate(e);
}
}
public SingularAttribute super T, A> attribute(ManagedType super T> mt, Attribute super T, A> attr) {
return mt.getSingularAttribute(attr.getName(), attr.getJavaType());
}
public SingularAttribute super T, String> stringAttribute(ManagedType super T> mt, Attribute super T, ?> attr) {
return mt.getSingularAttribute(attr.getName(), String.class);
}
public > boolean hasSimplePk(T entity) {
for (Method m : entity.getClass().getMethods()) {
if (m.getAnnotation(Id.class) != null) {
return true;
}
}
for (Field f : entity.getClass().getFields()) {
if (f.getAnnotation(Id.class) != null) {
return true;
}
}
return false;
}
public String[] toNames(Attribute, ?>... attributes) {
return toNamesList(newArrayList(attributes)).toArray(new String[0]);
}
public List toNamesList(List> attributes) {
List ret = newArrayList();
for (Attribute, ?> attribute : attributes) {
ret.add(attribute.getName());
}
return ret;
}
public String getEntityName(Identifiable> entity) {
Entity entityAnnotation = entity.getClass().getAnnotation(Entity.class);
if (isBlank(entityAnnotation.name())) {
return getClassWithoutInitializingProxy(entity).getSimpleName();
}
return entityAnnotation.name();
}
public String methodToProperty(Method m) {
PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(m.getDeclaringClass());
for (PropertyDescriptor pd : pds) {
if (m.equals(pd.getReadMethod()) || m.equals(pd.getWriteMethod())) {
return pd.getName();
}
}
return null;
}
public Object getValueFromField(Field field, Object object) {
boolean accessible = field.isAccessible();
try {
return getField(field, object);
} finally {
field.setAccessible(accessible);
}
}
public void applyPagination(Query query, SearchParameters sp) {
if (sp.getFirst() > 0) {
query.setFirstResult(sp.getFirst());
}
if (sp.getPageSize() > 0) {
query.setMaxResults(sp.getPageSize());
} else if (sp.getMaxResults() > 0) {
query.setMaxResults(sp.getMaxResults());
}
}
@SuppressWarnings("unchecked")
private T getField(Field field, Object target) {
try {
return (T) field.get(target);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}