HHH-7714 Add support for EntityMode.MAP to JPA Criteria API

This commit is contained in:
Brad Koehn 2013-03-27 09:41:19 -05:00 committed by Steve Ebersole
parent 1df99fc066
commit 2758b8b494
8 changed files with 156 additions and 94 deletions

View File

@ -23,24 +23,14 @@
*/
package org.hibernate.type;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import org.dom4j.Element;
import org.dom4j.Node;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.*;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
@ -48,6 +38,11 @@ import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.tuple.ElementWrapper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
/**
* Base for types which map associations to persistent entities.
*
@ -236,14 +231,16 @@ public abstract class EntityType extends AbstractType implements AssociationType
return returnedClass;
}
private Class determineAssociatedEntityClass() {
try {
return ReflectHelper.classForName( getAssociatedEntityName() );
}
catch ( ClassNotFoundException cnfe ) {
return java.util.Map.class;
}
}
private Class determineAssociatedEntityClass() {
final String entityName = getAssociatedEntityName();
try {
return ReflectHelper.classForName(entityName);
}
catch ( ClassNotFoundException cnfe ) {
return this.scope.resolveFactory().getEntityPersister(entityName).
getEntityTuplizer().getMappedClass();
}
}
/**
* {@inheritDoc}

View File

@ -323,7 +323,8 @@ public class CriteriaQueryImpl<T> extends AbstractNode implements CriteriaQuery<
}
Root root = getRoots().iterator().next();
if ( root.getModel().getJavaType() != returnType ) {
Class<?> javaType = root.getModel().getJavaType();
if ( javaType != null && javaType != returnType ) {
return false;
}

View File

@ -23,6 +23,9 @@
*/
package org.hibernate.ejb.criteria.predicate;
import org.hibernate.ejb.criteria.*;
import org.hibernate.ejb.criteria.expression.LiteralExpression;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
@ -31,13 +34,6 @@ import java.util.List;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Subquery;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.ParameterRegistry;
import org.hibernate.ejb.criteria.Renderable;
import org.hibernate.ejb.criteria.ValueHandlerFactory;
import org.hibernate.ejb.criteria.expression.LiteralExpression;
/**
* Models an <tt>[NOT] IN</tt> restriction
*
@ -119,8 +115,9 @@ public class InPredicate<T>
super( criteriaBuilder );
this.expression = expression;
this.values = new ArrayList<Expression<? extends T>>( values.size() );
ValueHandlerFactory.ValueHandler<? extends T> valueHandler = ValueHandlerFactory.isNumeric( expression.getJavaType() )
? ValueHandlerFactory.determineAppropriateHandler( (Class<? extends T>) expression.getJavaType() )
final Class<? extends T> javaType = expression.getJavaType();
ValueHandlerFactory.ValueHandler<? extends T> valueHandler = javaType != null && ValueHandlerFactory.isNumeric(javaType)
? ValueHandlerFactory.determineAppropriateHandler((Class<? extends T>) javaType)
: new ValueHandlerFactory.NoOpValueHandler<T>();
for ( T value : values ) {
this.values.add(

View File

@ -28,16 +28,30 @@ import javax.persistence.metamodel.Type;
/**
* Defines commonality for the JPA {@link Type} hierarchy of interfaces.
*
* This adds a type name so we don't rely on the class name. That allows
* for non-class-based (metadata-driven) models. --koehn
*
* @author Steve Ebersole
*/
public abstract class AbstractType<X> implements Type<X>, Serializable {
private final Class<X> javaType;
private final Class<X> javaType;
private String typeName;
public AbstractType(Class<X> javaType) {
this.javaType = javaType;
}
public AbstractType(Class<X> javaType) {
this.javaType = javaType;
public Class<X> getJavaType() {
return javaType;
}
this.typeName = javaType != null ? javaType.getName() : "unknown";
}
public Class<X> getJavaType() {
return javaType;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String name) {
this.typeName = name;
}
}

View File

@ -88,12 +88,12 @@ public class AttributeFactory {
// hide synthetic/virtual properties (fabricated by Hibernate) from the JPA metamodel.
LOG.tracef(
"Skipping synthetic property %s(%s)",
ownerType.getJavaType().getName(),
ownerType.getTypeName(),
property.getName()
);
return null;
}
LOG.trace("Building attribute [" + ownerType.getJavaType().getName() + "." + property.getName() + "]");
LOG.trace("Building attribute [" + ownerType.getTypeName() + "." + property.getName() + "]");
final AttributeContext<X> attributeContext = wrap( ownerType, property );
final AttributeMetadata<X,Y> attributeMetadata =
determineAttributeMetadata( attributeContext, NORMAL_MEMBER_RESOLVER );
@ -141,7 +141,7 @@ public class AttributeFactory {
*/
@SuppressWarnings({ "unchecked" })
public <X, Y> SingularAttributeImpl<X, Y> buildIdAttribute(AbstractIdentifiableType<X> ownerType, Property property) {
LOG.trace("Building identifier attribute [" + ownerType.getJavaType().getName() + "." + property.getName() + "]");
LOG.trace("Building identifier attribute [" + ownerType.getTypeName() + "." + property.getName() + "]");
final AttributeContext<X> attributeContext = wrap( ownerType, property );
final SingularAttributeMetadata<X,Y> attributeMetadata =
(SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, IDENTIFIER_MEMBER_RESOLVER );
@ -167,7 +167,7 @@ public class AttributeFactory {
*/
@SuppressWarnings({ "unchecked" })
public <X, Y> SingularAttributeImpl<X, Y> buildVersionAttribute(AbstractIdentifiableType<X> ownerType, Property property) {
LOG.trace("Building version attribute [ownerType.getJavaType().getName()" + "." + "property.getName()]");
LOG.trace("Building version attribute [ownerType.getTypeName()" + "." + "property.getName()]");
final AttributeContext<X> attributeContext = wrap( ownerType, property );
final SingularAttributeMetadata<X,Y> attributeMetadata =
(SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, VERSION_MEMBER_RESOLVER );
@ -234,11 +234,11 @@ public class AttributeFactory {
}
}
private EntityMetamodel getDeclarerEntityMetamodel(IdentifiableType<?> ownerType) {
private EntityMetamodel getDeclarerEntityMetamodel(AbstractIdentifiableType<?> ownerType) {
final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
if ( persistenceType == Type.PersistenceType.ENTITY) {
return context.getSessionFactory()
.getEntityPersister( ownerType.getJavaType().getName() )
.getEntityPersister( ownerType.getTypeName() )
.getEntityMetamodel();
}
else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS) {
@ -553,6 +553,9 @@ public class AttributeFactory {
? Attribute.PersistentAttributeType.ONE_TO_ONE
: Attribute.PersistentAttributeType.MANY_TO_ONE;
}
else if (MapMember.class.isInstance( member )) {
return Attribute.PersistentAttributeType.MANY_TO_ONE; // curious to see how this works for non-annotated methods
}
else {
return ( (Method) member ).getAnnotation( OneToOne.class ) != null
? Attribute.PersistentAttributeType.ONE_TO_ONE
@ -585,6 +588,9 @@ public class AttributeFactory {
else if ( Method.class.isInstance( member ) ) {
declaredType = ( (Method) member ).getReturnType();
}
else if ( MapMember.class.isInstance( member ) ) {
declaredType = ((MapMember) member).getType();
}
else {
throw new IllegalArgumentException( "Cannot determine java-type from given member [" + member + "]" );
}
@ -844,14 +850,21 @@ public class AttributeFactory {
}
}
public static ParameterizedType getSignatureType(Member member) {
final java.lang.reflect.Type type = Field.class.isInstance( member )
? ( ( Field ) member ).getGenericType()
: ( ( Method ) member ).getGenericReturnType();
//this is a raw type
if ( type instanceof Class ) return null;
return (ParameterizedType) type;
}
public static ParameterizedType getSignatureType(Member member) {
final java.lang.reflect.Type type;
if (Field.class.isInstance( member )) {
type = ( ( Field ) member ).getGenericType();
}
else if ( Method.class.isInstance( member ) ) {
type = ( ( Method ) member ).getGenericReturnType();
}
else {
type = ( (MapMember) member ).getType();
}
//this is a raw type
if ( type instanceof Class ) return null;
return (ParameterizedType) type;
}
public static PluralAttribute.CollectionType determineCollectionType(Class javaType) {
if ( java.util.List.class.isAssignableFrom( javaType ) ) {
@ -871,11 +884,16 @@ public class AttributeFactory {
}
}
public static boolean isManyToMany(Member member) {
return Field.class.isInstance( member )
? ( (Field) member ).getAnnotation( ManyToMany.class ) != null
: ( (Method) member ).getAnnotation( ManyToMany.class ) != null;
}
public static boolean isManyToMany(Member member) {
if ( Field.class.isInstance( member ) ) {
return ( (Field) member ).getAnnotation( ManyToMany.class ) != null;
}
else if ( Method.class.isInstance( member ) ) {
return ( (Method) member ).getAnnotation( ManyToMany.class ) != null;
}
return false;
}
private final MemberResolver EMBEDDED_MEMBER_RESOLVER = new MemberResolver() {
/**
@ -897,7 +915,7 @@ public class AttributeFactory {
* {@inheritDoc}
*/
public Member resolveMember(AttributeContext attributeContext) {
final IdentifiableType identifiableType = (IdentifiableType) attributeContext.getOwnerType();
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
if ( ! entityMetamodel.getIdentifierProperty().isVirtual() ) {
throw new IllegalArgumentException( "expecting IdClass mapping" );
@ -931,7 +949,7 @@ public class AttributeFactory {
}
else if ( Type.PersistenceType.ENTITY == persistenceType
|| Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) {
final IdentifiableType identifiableType = (IdentifiableType) ownerType;
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) ownerType;
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
final String propertyName = property.getName();
final Integer index = entityMetamodel.getPropertyIndexOrNull( propertyName );
@ -953,7 +971,7 @@ public class AttributeFactory {
private final MemberResolver IDENTIFIER_MEMBER_RESOLVER = new MemberResolver() {
public Member resolveMember(AttributeContext attributeContext) {
final IdentifiableType identifiableType = (IdentifiableType) attributeContext.getOwnerType();
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
if ( ! attributeContext.getPropertyMapping().getName()
.equals( entityMetamodel.getIdentifierProperty().getName() ) ) {
@ -966,7 +984,7 @@ public class AttributeFactory {
private final MemberResolver VERSION_MEMBER_RESOLVER = new MemberResolver() {
public Member resolveMember(AttributeContext attributeContext) {
final IdentifiableType identifiableType = (IdentifiableType) attributeContext.getOwnerType();
final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType();
final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
final String versionPropertyName = attributeContext.getPropertyMapping().getName();
if ( ! versionPropertyName.equals( entityMetamodel.getVersionProperty().getName() ) ) {

View File

@ -0,0 +1,40 @@
package org.hibernate.ejb.metamodel;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
/**
* Defines a Member implementation used to represent attibutes that
* aren't defined my fields or methods for non-class-based metamodels.
*
* @author Brad Koehn
*/
public class MapMember implements Member {
private String name;
private final Class<?> type;
public MapMember( String name, Class<?> type ) {
this.name = name;
this.type = type;
}
public Class<?> getType() {
return type;
}
public int getModifiers() {
return Modifier.PUBLIC;
}
public boolean isSynthetic() {
return false;
}
public String getName() {
return name;
}
public Class<?> getDeclaringClass() {
return null;
}
}

View File

@ -21,12 +21,15 @@
*/
package org.hibernate.ejb.metamodel;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.ejb.internal.EntityManagerMessageLogger;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.*;
import org.jboss.logging.Logger;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -35,18 +38,6 @@ import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.MappedSuperclassType;
import javax.persistence.metamodel.SingularAttribute;
import org.jboss.logging.Logger;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.ejb.internal.EntityManagerMessageLogger;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
/**
* Defines a context for storing information during the building of the {@link MetamodelImpl}.
* <p/>
@ -183,7 +174,11 @@ class MetadataContext {
return entityTypesByEntityName.get( entityName );
}
@SuppressWarnings({ "unchecked" })
public Map<String, EntityTypeImpl<?>> getEntityTypesByEntityName() {
return Collections.unmodifiableMap( entityTypesByEntityName );
}
@SuppressWarnings({ "unchecked" })
public void wrapUp() {
LOG.trace("Wrapping up metadata context...");
//we need to process types from superclasses to subclasses

View File

@ -21,21 +21,17 @@
*/
package org.hibernate.ejb.metamodel;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.persistence.metamodel.EmbeddableType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.MappedSuperclassType;
import javax.persistence.metamodel.Metamodel;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import javax.persistence.metamodel.*;
/**
* Hibernate implementation of the JPA {@link Metamodel} contract.
@ -47,6 +43,7 @@ public class MetamodelImpl implements Metamodel, Serializable {
private final Map<Class<?>,EntityTypeImpl<?>> entities;
private final Map<Class<?>, EmbeddableTypeImpl<?>> embeddables;
private final Map<Class<?>, MappedSuperclassType<?>> mappedSuperclassTypeMap;
private final Map<String, EntityTypeImpl<?>> entityTypesByEntityName;
/**
* Build the metamodel using the information from the collection of Hibernate
@ -81,12 +78,10 @@ public class MetamodelImpl implements Metamodel, Serializable {
MetadataContext context = new MetadataContext( sessionFactory, ignoreUnsupported );
while ( persistentClasses.hasNext() ) {
PersistentClass pc = persistentClasses.next();
if ( pc.getMappedClass() != null ) {
locateOrBuildEntityType( pc, context );
}
locateOrBuildEntityType( pc, context );
}
context.wrapUp();
return new MetamodelImpl( context.getEntityTypeMap(), context.getEmbeddableTypeMap(), context.getMappedSuperclassTypeMap() );
return new MetamodelImpl( context.getEntityTypeMap(), context.getEmbeddableTypeMap(), context.getMappedSuperclassTypeMap(), context.getEntityTypesByEntityName() );
}
private static EntityTypeImpl<?> locateOrBuildEntityType(PersistentClass persistentClass, MetadataContext context) {
@ -120,7 +115,10 @@ public class MetamodelImpl implements Metamodel, Serializable {
persistentClass.hasIdentifierProperty(),
persistentClass.isVersioned()
);
context.registerEntityType( persistentClass, entityType );
entityType.setTypeName(persistentClass.getEntityName());
context.registerEntityType( persistentClass, entityType );
context.popEntityWorkedOn(persistentClass);
return entityType;
}
@ -170,10 +168,12 @@ public class MetamodelImpl implements Metamodel, Serializable {
private MetamodelImpl(
Map<Class<?>, EntityTypeImpl<?>> entities,
Map<Class<?>, EmbeddableTypeImpl<?>> embeddables,
Map<Class<?>, MappedSuperclassType<?>> mappedSuperclassTypeMap) {
Map<Class<?>, MappedSuperclassType<?>> mappedSuperclassTypeMap,
Map<String, EntityTypeImpl<?>> entityTypesByEntityName) {
this.entities = entities;
this.embeddables = embeddables;
this.mappedSuperclassTypeMap = mappedSuperclassTypeMap;
this.entityTypesByEntityName = entityTypesByEntityName;
}
@Override
@ -226,7 +226,7 @@ public class MetamodelImpl implements Metamodel, Serializable {
@Override
public Set<EntityType<?>> getEntities() {
return new HashSet<EntityType<?>>( entities.values() );
return new HashSet<EntityType<?>>( entityTypesByEntityName.values() );
}
@Override