
com.blazebit.persistence.impl.JpaUtils Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2015 Blazebit.
*
* 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.blazebit.persistence.impl;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.logging.Logger;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import com.blazebit.reflection.ReflectionUtils;
/**
*
* @author Christian Beikov
* @since 1.0
*/
public final class JpaUtils {
private final static Logger LOG = Logger.getLogger(JpaUtils.class.getName());
public static ManagedType> getManagedType(EntityMetamodel metamodel, Class> managedTypeClass, String treatTypeName) {
if (treatTypeName != null) {
ManagedType> type = metamodel.managedType(treatTypeName);
if (!type.getJavaType().isAssignableFrom(managedTypeClass)) {
throw new IllegalArgumentException("Treat type '" + treatTypeName + "' is not a subtype of: " + managedTypeClass.getName());
}
return type;
}
return metamodel.managedType(managedTypeClass);
}
public static ManagedType> getManagedTypeOrNull(EntityMetamodel metamodel, Class> javaType) {
return metamodel.getManagedType(javaType);
}
public static Attribute super T, ?> getAttribute(ManagedType type, String attributeName) {
try {
return type.getAttribute(attributeName);
} catch (IllegalArgumentException ex) {
return null;
}
}
public static AttributePath getBasicAttributePath(Metamodel metamodel, ManagedType> type, String attributePath) {
List> attrPath;
if (attributePath.indexOf('.') == -1) {
attrPath = new ArrayList>(1);
Attribute, ?> attribute = type.getAttribute(attributePath);
if (attribute == null) {
// Well, some implementations might not be fully spec compliant..
throw new IllegalArgumentException("Attribute '" + attributePath + "' does not exist on '" + type.getJavaType().getName() + "'!");
}
attrPath.add(attribute);
return new AttributePath(attrPath, resolveFieldClass(type.getJavaType(), attribute));
} else {
attrPath = new ArrayList>();
}
String[] attributeParts = attributePath.split("\\.");
ManagedType> currentType = type;
Class> currentClass = type.getJavaType();
boolean joinableAllowed = true;
for (int i = 0; i < attributeParts.length; i++) {
Attribute, ?> attr = null;
if (currentType == null) {
// dereference basic
break;
}
attr = JpaUtils.getAttribute(currentType, attributeParts[i]);
if (attr == null) {
attrPath.clear();
break;
}
if (attr.getPersistentAttributeType() == PersistentAttributeType.EMBEDDED) {
currentType = metamodel.embeddable(attr.getJavaType());
} else if(attr.getPersistentAttributeType() == PersistentAttributeType.BASIC) {
currentType = null;
} else if(JpaUtils.isJoinable(attr) && joinableAllowed) {
joinableAllowed = false;
if (i + 1 < attributeParts.length) {
currentType = metamodel.entity(attr.getJavaType());
// look ahead
Attribute, ?> nextAttr = JpaUtils.getAttribute(currentType, attributeParts[i + 1]);
if (!JpaUtils.getIdAttribute((EntityType>) currentType).equals(nextAttr)) {
throw new IllegalArgumentException("Path joining not allowed in returning expression: " + attributePath);
}
}
} else {
throw new IllegalArgumentException("Path joining not allowed in returning expression: " + attributePath);
}
currentClass = resolveFieldClass(currentClass, attr);
attrPath.add(attr);
}
if (attrPath.isEmpty()) {
throw new IllegalArgumentException("Path " + attributePath + " does not exist on entity " + type.getJavaType().getName());
}
return new AttributePath(attrPath, currentClass);
}
public static Set> getAttributesPolymorphic(Metamodel metamodel, ManagedType> type, String attributeName) {
Attribute, ?> attr = getAttribute(type, attributeName);
if (attr != null) {
Set> set = new HashSet>(1);
set.add(attr);
return set;
}
// Try again polymorphic
Class> javaType = type.getJavaType();
Set> resolvedAttributes = new HashSet>();
// Collect all possible subtypes of the given type
for (ManagedType> subType : metamodel.getManagedTypes()) {
if (javaType.isAssignableFrom(subType.getJavaType()) && javaType != subType.getJavaType()) {
// Collect all the attributes that resolve on every possible subtype
attr = JpaUtils.getAttribute(subType, attributeName);
if (attr != null) {
resolvedAttributes.add(attr);
}
}
}
return resolvedAttributes;
}
public static boolean isMap(Attribute, ?> attr) {
return attr instanceof MapAttribute, ?, ?>;
}
public static boolean isOptional(Attribute, ?> attribute) {
if (attribute instanceof SingularAttribute, ?>) {
return ((SingularAttribute, ?>) attribute).isOptional();
}
return true;
}
public static Attribute, ?> getIdAttribute(EntityType> entityType) {
return entityType.getId(entityType.getIdType().getJavaType());
}
public static boolean isJoinable(Attribute, ?> attr) {
if (attr.isCollection()) {
return true;
}
SingularAttribute, ?> singularAttribute = (SingularAttribute, ?>) attr;
// This is a special case for datanucleus... apparently an embedded id is an ONE_TO_ONE association although I think it should be an embedded
// TODO: create a test case for datanucleus and report the problem
if (singularAttribute.isId()) {
return false;
}
return attr.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE
|| attr.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE;
}
public static Class> resolveType(Class> concreteClass, java.lang.reflect.Type type) {
if (type instanceof Class>) {
return (Class>) type;
} else if (type instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) type).getRawType();
} else if (type instanceof TypeVariable) {
return resolveType(concreteClass, ((TypeVariable>) type).getBounds()[0]);
} else {
throw new IllegalArgumentException("Unsupported type for resolving: " + type);
}
}
private static Class> getConcreterClass(Class> class1, Class> class2) {
if (class1.isAssignableFrom(class2)) {
return class2;
} else if (class2.isAssignableFrom(class1)) {
return class1;
} else {
throw new IllegalArgumentException("The classes [" + class1.getName() + ", " + class2.getName()
+ "] are not in a inheritance relationship, so there is no concreter class!");
}
}
public static Class> resolveFieldClass(Class> baseClass, Attribute, ?> attr) {
Class> resolverBaseClass = getConcreterClass(baseClass, attr.getDeclaringType().getJavaType());
Class> fieldClass;
if (attr.isCollection()) {
PluralAttribute, ?, ?> collectionAttr = (PluralAttribute, ?, ?>) attr;
if (collectionAttr.getCollectionType() == PluralAttribute.CollectionType.MAP) {
if (attr.getJavaMember() instanceof Method) {
Method method = (Method) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedMethodReturnTypeArguments(resolverBaseClass, method)[1];
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[1]);
}
} else {
Field field = (Field) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedFieldTypeArguments(resolverBaseClass, field)[1];
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1]);
}
}
} else {
if (attr.getJavaMember() instanceof Method) {
Method method = (Method) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedMethodReturnTypeArguments(resolverBaseClass, method)[0];
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0]);
}
} else {
Field field = (Field) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedFieldTypeArguments(resolverBaseClass, field)[0];
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]);
}
}
}
} else {
if (attr.getJavaMember() instanceof Method) {
Method method = (Method) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedMethodReturnType(resolverBaseClass, method);
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, method.getGenericReturnType());
}
} else {
Field field = (Field) attr.getJavaMember();
fieldClass = ReflectionUtils.getResolvedFieldType(resolverBaseClass, field);
if (fieldClass == null) {
fieldClass = resolveType(resolverBaseClass, field.getGenericType());
}
}
}
return fieldClass;
}
public static Attribute, ?> getPolymorphicSimpleAttribute(Metamodel metamodel, ManagedType> type, String attributeName) {
Set> resolvedAttributes = getAttributesPolymorphic(metamodel, type, attributeName);
Iterator> iter = resolvedAttributes.iterator();
if (resolvedAttributes.size() > 1) {
// If there is more than one resolved attribute we can still save the user some trouble
Attribute, ?> simpleAttribute = null;
Set> amiguousAttributes = new HashSet>();
for (Attribute, ?> attr : resolvedAttributes) {
if (isJoinable(attr)) {
amiguousAttributes.add(attr);
} else {
simpleAttribute = attr;
}
}
if (simpleAttribute == null) {
return null;
} else {
for (Attribute, ?> a : amiguousAttributes) {
LOG.warning("The attribute [" + attributeName + "] of the class [" + a.getDeclaringType().getJavaType().getName()
+ "] is ambiguous for polymorphic implicit joining on the type [" + type.getJavaType().getName() + "]");
}
return simpleAttribute;
}
} else if (iter.hasNext()) {
return iter.next();
} else {
return null;
}
}
public static Attribute, ?> getPolymorphicAttribute(Metamodel metamodel, ManagedType> type, String attributeName) {
Set> resolvedAttributes = getAttributesPolymorphic(metamodel, type, attributeName);
Iterator> iter = resolvedAttributes.iterator();
if (resolvedAttributes.size() > 1) {
// If there is more than one resolved attribute we can still save the user some trouble
Attribute, ?> joinableAttribute = null;
Attribute, ?> attr = null;
// Multiple non-joinable attributes would be fine since we only care for OUR join manager here
// Multiple joinable attributes are only fine if they all have the same type
while (iter.hasNext()) {
attr = iter.next();
if (isJoinable(attr)) {
if (joinableAttribute != null && !joinableAttribute.getJavaType().equals(attr.getJavaType())) {
throw new IllegalArgumentException("Multiple joinable attributes with the name [" + attributeName
+ "] but different java types in the types [" + joinableAttribute.getDeclaringType().getJavaType().getName()
+ "] and [" + attr.getDeclaringType().getJavaType().getName() + "] found!");
} else {
joinableAttribute = attr;
}
}
}
// We return the joinable attribute because OUR join manager needs it's type for further joining
if (joinableAttribute != null) {
return joinableAttribute;
}
return attr;
} else if (iter.hasNext()) {
return iter.next();
} else {
return null;
}
}
public static AttributeJoinResult getAttributeForJoining(EntityMetamodel metamodel, ManagedType> type, String attributeName) {
Attribute, ?> attr;
if (attributeName.indexOf('.') < 0) {
attr = getPolymorphicAttribute(metamodel, type, attributeName);
return new AttributeJoinResult(attr, type.getJavaType());
}
String[] attributeParts = attributeName.split("\\.");
attr = getPolymorphicAttribute(metamodel, type, attributeParts[0]);
for (int i = 1; i < attributeParts.length; i++) {
type = metamodel.managedType(resolveFieldClass(type.getJavaType(), attr));
attr = getPolymorphicAttribute(metamodel, type, attributeParts[i]);
}
return new AttributeJoinResult(attr, type.getJavaType());
}
public static Attribute, ?> getSimpleAttributeForImplicitJoining(EntityMetamodel metamodel, ManagedType> type, String attributeName) {
Attribute, ?> attr;
if (attributeName.indexOf('.') < 0) {
attr = getPolymorphicSimpleAttribute(metamodel, type, attributeName);
return attr;
}
String[] attributeParts = attributeName.split("\\.");
attr = getPolymorphicSimpleAttribute(metamodel, type, attributeParts[0]);
for (int i = 1; i < attributeParts.length; i++) {
type = metamodel.managedType(resolveFieldClass(type.getJavaType(), attr));
attr = getPolymorphicAttribute(metamodel, type, attributeParts[i]);
}
return attr;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy