Package org.hibernate.metamodel.source.annotations.entity

Source Code of org.hibernate.metamodel.source.annotations.entity.ConfiguredClass

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.metamodel.source.annotations.entity;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.persistence.AccessType;

import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.metamodel.source.MappingException;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.source.annotations.HibernateDotNames;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.ReflectionHelper;
import org.hibernate.metamodel.source.annotations.attribute.AssociationAttribute;
import org.hibernate.metamodel.source.annotations.attribute.AttributeNature;
import org.hibernate.metamodel.source.annotations.attribute.AttributeOverride;
import org.hibernate.metamodel.source.annotations.attribute.BasicAttribute;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;

import com.fasterxml.classmate.ResolvedTypeWithMembers;
import com.fasterxml.classmate.members.HierarchicType;
import com.fasterxml.classmate.members.ResolvedMember;

/**
* Base class for a configured entity, mapped super class or embeddable
*
* @author Hardy Ferentschik
*/
public class ConfiguredClass {
  public static final Logger LOG = Logger.getLogger( ConfiguredClass.class.getName() );

  /**
   * The parent of this configured class or {@code null} in case this configured class is the root of a hierarchy.
   */
  private final ConfiguredClass parent;

  /**
   * The Jandex class info for this configured class. Provides access to the annotation defined on this configured class.
   */
  private final ClassInfo classInfo;

  /**
   * The actual java type.
   */
  private final Class<?> clazz;

  /**
   * The default access type for this entity
   */
  private final AccessType classAccessType;

  /**
   * The type of configured class, entity, mapped super class, embeddable, ...
   */
  private final ConfiguredClassType configuredClassType;

  /**
   * The id attributes
   */
  private final Map<String, BasicAttribute> idAttributeMap;

  /**
   * The mapped association attributes for this entity
   */
  private final Map<String, AssociationAttribute> associationAttributeMap;

  /**
   * The mapped simple attributes for this entity
   */
  private final Map<String, BasicAttribute> simpleAttributeMap;

  /**
   * The version attribute or {@code null} in case none exists.
   */
  private BasicAttribute versionAttribute;

  /**
   * The embedded classes for this entity
   */
  private final Map<String, EmbeddableClass> embeddedClasses = new HashMap<String, EmbeddableClass>();

  /**
   * A map of all attribute overrides defined in this class. The override name is "normalised", meaning as if specified
   * on class level. If the override is specified on attribute level the attribute name is used as prefix.
   */
  private final Map<String, AttributeOverride> attributeOverrideMap;

  private final Set<String> transientFieldNames = new HashSet<String>();
  private final Set<String> transientMethodNames = new HashSet<String>();

  /**
   * Fully qualified name of a custom tuplizer
   */
  private final String customTuplizer;

  private final EntityBindingContext localBindingContext;

  public ConfiguredClass(
      ClassInfo classInfo,
      AccessType defaultAccessType,
      ConfiguredClass parent,
      AnnotationBindingContext context) {
    this.parent = parent;
    this.classInfo = classInfo;
    this.clazz = context.locateClassByName( classInfo.toString() );
    this.configuredClassType = determineType();
    this.classAccessType = determineClassAccessType( defaultAccessType );
    this.customTuplizer = determineCustomTuplizer();

    this.simpleAttributeMap = new TreeMap<String, BasicAttribute>();
    this.idAttributeMap = new TreeMap<String, BasicAttribute>();
    this.associationAttributeMap = new TreeMap<String, AssociationAttribute>();

    this.localBindingContext = new EntityBindingContext( context, this );

    collectAttributes();
    attributeOverrideMap = Collections.unmodifiableMap( findAttributeOverrides() );
  }

  public String getName() {
    return clazz.getName();
  }

  public Class<?> getConfiguredClass() {
    return clazz;
  }

  public ClassInfo getClassInfo() {
    return classInfo;
  }

  public ConfiguredClass getParent() {
    return parent;
  }

  public EntityBindingContext getLocalBindingContext() {
    return localBindingContext;
  }

  public Iterable<BasicAttribute> getSimpleAttributes() {
    return simpleAttributeMap.values();
  }

  public Iterable<BasicAttribute> getIdAttributes() {
    return idAttributeMap.values();
  }

  public BasicAttribute getVersionAttribute() {
    return versionAttribute;
  }

  public Iterable<AssociationAttribute> getAssociationAttributes() {
    return associationAttributeMap.values();
  }

  public Map<String, EmbeddableClass> getEmbeddedClasses() {
    return embeddedClasses;
  }

  public Map<String, AttributeOverride> getAttributeOverrideMap() {
    return attributeOverrideMap;
  }

  public AccessType getClassAccessType() {
    return classAccessType;
  }

  public String getCustomTuplizer() {
    return customTuplizer;
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append( "ConfiguredClass" );
    sb.append( "{clazz=" ).append( clazz.getSimpleName() );
    sb.append( '}' );
    return sb.toString();
  }

  private ConfiguredClassType determineType() {
    if ( classInfo.annotations().containsKey( JPADotNames.ENTITY ) ) {
      return ConfiguredClassType.ENTITY;
    }
    else if ( classInfo.annotations().containsKey( JPADotNames.MAPPED_SUPERCLASS ) ) {
      return ConfiguredClassType.MAPPED_SUPERCLASS;
    }
    else if ( classInfo.annotations().containsKey( JPADotNames.EMBEDDABLE ) ) {
      return ConfiguredClassType.EMBEDDABLE;
    }
    else {
      return ConfiguredClassType.NON_ENTITY;
    }
  }

  private AccessType determineClassAccessType(AccessType defaultAccessType) {
    // default to the hierarchy access type to start with
    AccessType accessType = defaultAccessType;

    AnnotationInstance accessAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ACCESS );
    if ( accessAnnotation != null && accessAnnotation.target().getClass().equals( ClassInfo.class ) ) {
      accessType = JandexHelper.getEnumValue( accessAnnotation, "value", AccessType.class );
    }

    return accessType;
  }

  /**
   * Find all attributes for this configured class and add them to the corresponding map
   */
  private void collectAttributes() {
    // find transient field and method names
    findTransientFieldAndMethodNames();

    // use the class mate library to generic types
    ResolvedTypeWithMembers resolvedType = localBindingContext.resolveMemberTypes(
        localBindingContext.getResolvedType(
            clazz
        )
    );
    for ( HierarchicType hierarchicType : resolvedType.allTypesAndOverrides() ) {
      if ( hierarchicType.getType().getErasedType().equals( clazz ) ) {
        resolvedType = localBindingContext.resolveMemberTypes( hierarchicType.getType() );
        break;
      }
    }

    if ( resolvedType == null ) {
      throw new AssertionFailure( "Unable to resolve types for " + clazz.getName() );
    }

    Set<String> explicitlyConfiguredMemberNames = createExplicitlyConfiguredAccessProperties( resolvedType );

    if ( AccessType.FIELD.equals( classAccessType ) ) {
      Field fields[] = clazz.getDeclaredFields();
      Field.setAccessible( fields, true );
      for ( Field field : fields ) {
        if ( isPersistentMember( transientFieldNames, explicitlyConfiguredMemberNames, field ) ) {
          createMappedAttribute( field, resolvedType, AccessType.FIELD );
        }
      }
    }
    else {
      Method[] methods = clazz.getDeclaredMethods();
      Method.setAccessible( methods, true );
      for ( Method method : methods ) {
        if ( isPersistentMember( transientMethodNames, explicitlyConfiguredMemberNames, method ) ) {
          createMappedAttribute( method, resolvedType, AccessType.PROPERTY );
        }
      }
    }
  }

  private boolean isPersistentMember(Set<String> transientNames, Set<String> explicitlyConfiguredMemberNames, Member member) {
    if ( !ReflectionHelper.isProperty( member ) ) {
      return false;
    }

    if ( transientNames.contains( member.getName() ) ) {
      return false;
    }

    if ( explicitlyConfiguredMemberNames.contains( ReflectionHelper.getPropertyName( member ) ) ) {
      return false;
    }

    return true;
  }

  /**
   * Creates {@code MappedProperty} instances for the explicitly configured persistent properties
   *
   * @param resolvedMembers the resolved type parameters for this class
   *
   * @return the property names of the explicitly configured attribute names in a set
   */
  private Set<String> createExplicitlyConfiguredAccessProperties(ResolvedTypeWithMembers resolvedMembers) {
    Set<String> explicitAccessPropertyNames = new HashSet<String>();

    List<AnnotationInstance> accessAnnotations = classInfo.annotations().get( JPADotNames.ACCESS );
    if ( accessAnnotations == null ) {
      return explicitAccessPropertyNames;
    }

    // iterate over all @Access annotations defined on the current class
    for ( AnnotationInstance accessAnnotation : accessAnnotations ) {
      // we are only interested at annotations defined on fields and methods
      AnnotationTarget annotationTarget = accessAnnotation.target();
      if ( !( annotationTarget.getClass().equals( MethodInfo.class ) || annotationTarget.getClass()
          .equals( FieldInfo.class ) ) ) {
        continue;
      }

      AccessType accessType = JandexHelper.getEnumValue( accessAnnotation, "value", AccessType.class );

      if ( !isExplicitAttributeAccessAnnotationPlacedCorrectly( annotationTarget, accessType ) ) {
        continue;
      }

      // the placement is correct, get the member
      Member member;
      if ( annotationTarget instanceof MethodInfo ) {
        Method m;
        try {
          m = clazz.getMethod( ( (MethodInfo) annotationTarget ).name() );
        }
        catch ( NoSuchMethodException e ) {
          throw new HibernateException(
              "Unable to load method "
                  + ( (MethodInfo) annotationTarget ).name()
                  + " of class " + clazz.getName()
          );
        }
        member = m;
        accessType = AccessType.PROPERTY;
      }
      else {
        Field f;
        try {
          f = clazz.getField( ( (FieldInfo) annotationTarget ).name() );
        }
        catch ( NoSuchFieldException e ) {
          throw new HibernateException(
              "Unable to load field "
                  + ( (FieldInfo) annotationTarget ).name()
                  + " of class " + clazz.getName()
          );
        }
        member = f;
        accessType = AccessType.FIELD;
      }
      if ( ReflectionHelper.isProperty( member ) ) {
        createMappedAttribute( member, resolvedMembers, accessType );
        explicitAccessPropertyNames.add( ReflectionHelper.getPropertyName( member ) );
      }
    }
    return explicitAccessPropertyNames;
  }

  private boolean isExplicitAttributeAccessAnnotationPlacedCorrectly(AnnotationTarget annotationTarget, AccessType accessType) {
    // when the access type of the class is FIELD
    // overriding access annotations must be placed on properties AND have the access type PROPERTY
    if ( AccessType.FIELD.equals( classAccessType ) ) {
      if ( !( annotationTarget instanceof MethodInfo ) ) {
        LOG.tracef(
            "The access type of class %s is AccessType.FIELD. To override the access for an attribute " +
                "@Access has to be placed on the property (getter)", classInfo.name().toString()
        );
        return false;
      }

      if ( !AccessType.PROPERTY.equals( accessType ) ) {
        LOG.tracef(
            "The access type of class %s is AccessType.FIELD. To override the access for an attribute " +
                "@Access has to be placed on the property (getter) with an access type of AccessType.PROPERTY. " +
                "Using AccessType.FIELD on the property has no effect",
            classInfo.name().toString()
        );
        return false;
      }
    }

    // when the access type of the class is PROPERTY
    // overriding access annotations must be placed on fields and have the access type FIELD
    if ( AccessType.PROPERTY.equals( classAccessType ) ) {
      if ( !( annotationTarget instanceof FieldInfo ) ) {
        LOG.tracef(
            "The access type of class %s is AccessType.PROPERTY. To override the access for a field " +
                "@Access has to be placed on the field ", classInfo.name().toString()
        );
        return false;
      }

      if ( !AccessType.FIELD.equals( accessType ) ) {
        LOG.tracef(
            "The access type of class %s is AccessType.PROPERTY. To override the access for a field " +
                "@Access has to be placed on the field with an access type of AccessType.FIELD. " +
                "Using AccessType.PROPERTY on the field has no effect",
            classInfo.name().toString()
        );
        return false;
      }
    }
    return true;
  }

  private void createMappedAttribute(Member member, ResolvedTypeWithMembers resolvedType, AccessType accessType) {
    final String attributeName = ReflectionHelper.getPropertyName( member );
    ResolvedMember[] resolvedMembers;
    if ( member instanceof Field ) {
      resolvedMembers = resolvedType.getMemberFields();
    }
    else {
      resolvedMembers = resolvedType.getMemberMethods();
    }
    Class<?> attributeType = (Class<?>) findResolvedType( member.getName(), resolvedMembers );
    final Map<DotName, List<AnnotationInstance>> annotations = JandexHelper.getMemberAnnotations(
        classInfo, member.getName()
    );

    AttributeNature attributeNature = determineAttributeNature( annotations );
    String accessTypeString = accessType.toString().toLowerCase();
    switch ( attributeNature ) {
      case BASIC: {
        BasicAttribute attribute = BasicAttribute.createSimpleAttribute(
            attributeName, attributeType, annotations, accessTypeString, getLocalBindingContext()
        );
        if ( attribute.isId() ) {
          idAttributeMap.put( attributeName, attribute );
        }
        else if ( attribute.isVersioned() ) {
          if ( versionAttribute == null ) {
            versionAttribute = attribute;
          }
          else {
            throw new MappingException( "Multiple version attributes", localBindingContext.getOrigin() );
          }
        }
        else {
          simpleAttributeMap.put( attributeName, attribute );
        }
        break;
      }
      case ELEMENT_COLLECTION: {
        throw new NotYetImplementedException( "Element collections must still be implemented." );
      }
      case EMBEDDED_ID: {
        throw new NotYetImplementedException( "Embedded ids must still be implemented." );
      }
      case EMBEDDED: {
        AnnotationInstance targetAnnotation = JandexHelper.getSingleAnnotation(
            getClassInfo(),
            HibernateDotNames.TARGET
        );
        if ( targetAnnotation != null ) {
          attributeType = localBindingContext.locateClassByName(
              JandexHelper.getValue( targetAnnotation, "value", String.class )
          );
        }
        resolveEmbeddable( attributeName, attributeType );
        break;
      }
      // OneToOne, OneToMany, ManyToOne, ManyToMany
      default: {
        AssociationAttribute attribute = AssociationAttribute.createAssociationAttribute(
            attributeName,
            attributeType,
            attributeNature,
            accessTypeString,
            annotations,
            getLocalBindingContext()
        );
        associationAttributeMap.put( attributeName, attribute );
      }
    }
  }

  private void resolveEmbeddable(String attributeName, Class<?> type) {
    ClassInfo embeddableClassInfo = localBindingContext.getClassInfo( type.getName() );
    if ( embeddableClassInfo == null ) {
      String msg = String.format(
          "Attribute '%s#%s' is annotated with @Embedded, but '%s' does not seem to be annotated " +
              "with @Embeddable. Are all annotated classes added to the configuration?",
          getConfiguredClass().getSimpleName(),
          attributeName,
          type.getSimpleName()
      );
      throw new AnnotationException( msg );
    }

    localBindingContext.resolveAllTypes( type.getName() );
    EmbeddableHierarchy hierarchy = EmbeddableHierarchy.createEmbeddableHierarchy(
        localBindingContext.<Object>locateClassByName( embeddableClassInfo.toString() ),
        attributeName,
        classAccessType,
        localBindingContext
    );
    embeddedClasses.put( attributeName, hierarchy.getLeaf() );
  }

  /**
   * Given the annotations defined on a persistent attribute this methods determines the attribute type.
   *
   * @param annotations the annotations defined on the persistent attribute
   *
   * @return an instance of the {@code AttributeType} enum
   */
  private AttributeNature determineAttributeNature(Map<DotName, List<AnnotationInstance>> annotations) {
    EnumMap<AttributeNature, AnnotationInstance> discoveredAttributeTypes =
        new EnumMap<AttributeNature, AnnotationInstance>( AttributeNature.class );

    AnnotationInstance oneToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_ONE );
    if ( oneToOne != null ) {
      discoveredAttributeTypes.put( AttributeNature.ONE_TO_ONE, oneToOne );
    }

    AnnotationInstance oneToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_MANY );
    if ( oneToMany != null ) {
      discoveredAttributeTypes.put( AttributeNature.ONE_TO_MANY, oneToMany );
    }

    AnnotationInstance manyToOne = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_ONE );
    if ( manyToOne != null ) {
      discoveredAttributeTypes.put( AttributeNature.MANY_TO_ONE, manyToOne );
    }

    AnnotationInstance manyToMany = JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_MANY );
    if ( manyToMany != null ) {
      discoveredAttributeTypes.put( AttributeNature.MANY_TO_MANY, manyToMany );
    }

    AnnotationInstance embedded = JandexHelper.getSingleAnnotation( annotations, JPADotNames.EMBEDDED );
    if ( embedded != null ) {
      discoveredAttributeTypes.put( AttributeNature.EMBEDDED, embedded );
    }

    AnnotationInstance embeddedId = JandexHelper.getSingleAnnotation( annotations, JPADotNames.EMBEDDED_ID );
    if ( embeddedId != null ) {
      discoveredAttributeTypes.put( AttributeNature.EMBEDDED_ID, embeddedId );
    }

    AnnotationInstance elementCollection = JandexHelper.getSingleAnnotation(
        annotations,
        JPADotNames.ELEMENT_COLLECTION
    );
    if ( elementCollection != null ) {
      discoveredAttributeTypes.put( AttributeNature.ELEMENT_COLLECTION, elementCollection );
    }

    if ( discoveredAttributeTypes.size() == 0 ) {
      return AttributeNature.BASIC;
    }
    else if ( discoveredAttributeTypes.size() == 1 ) {
      return discoveredAttributeTypes.keySet().iterator().next();
    }
    else {
      throw new AnnotationException( "More than one association type configured for property  " + getName() + " of class " + getName() );
    }
  }

  private Type findResolvedType(String name, ResolvedMember[] resolvedMembers) {
    for ( ResolvedMember resolvedMember : resolvedMembers ) {
      if ( resolvedMember.getName().equals( name ) ) {
        return resolvedMember.getType().getErasedType();
      }
    }
    throw new AssertionFailure(
        String.format(
            "Unable to resolve type of attribute %s of class %s",
            name,
            classInfo.name().toString()
        )
    );
  }

  /**
   * Populates the sets of transient field and method names.
   */
  private void findTransientFieldAndMethodNames() {
    List<AnnotationInstance> transientMembers = classInfo.annotations().get( JPADotNames.TRANSIENT );
    if ( transientMembers == null ) {
      return;
    }

    for ( AnnotationInstance transientMember : transientMembers ) {
      AnnotationTarget target = transientMember.target();
      if ( target instanceof FieldInfo ) {
        transientFieldNames.add( ( (FieldInfo) target ).name() );
      }
      else {
        transientMethodNames.add( ( (MethodInfo) target ).name() );
      }
    }
  }

  private Map<String, AttributeOverride> findAttributeOverrides() {
    Map<String, AttributeOverride> attributeOverrideList = new HashMap<String, AttributeOverride>();

    AnnotationInstance attributeOverrideAnnotation = JandexHelper.getSingleAnnotation(
        classInfo,
        JPADotNames.ATTRIBUTE_OVERRIDE
    );
    if ( attributeOverrideAnnotation != null ) {
      String prefix = createPathPrefix( attributeOverrideAnnotation.target() );
      AttributeOverride override = new AttributeOverride( prefix, attributeOverrideAnnotation );
      attributeOverrideList.put( override.getAttributePath(), override );
    }

    AnnotationInstance attributeOverridesAnnotation = JandexHelper.getSingleAnnotation(
        classInfo,
        JPADotNames.ATTRIBUTE_OVERRIDES
    );
    if ( attributeOverridesAnnotation != null ) {
      AnnotationInstance[] annotationInstances = attributeOverridesAnnotation.value().asNestedArray();
      for ( AnnotationInstance annotationInstance : annotationInstances ) {
        String prefix = createPathPrefix( attributeOverridesAnnotation.target() );
        AttributeOverride override = new AttributeOverride( prefix, annotationInstance );
        attributeOverrideList.put( override.getAttributePath(), override );
      }
    }
    return attributeOverrideList;
  }

  private String createPathPrefix(AnnotationTarget target) {
    String prefix = null;
    if ( target instanceof FieldInfo || target instanceof MethodInfo ) {
      prefix = JandexHelper.getPropertyName( target );
    }
    return prefix;
  }

  private List<AnnotationInstance> findAssociationOverrides() {
    List<AnnotationInstance> associationOverrideList = new ArrayList<AnnotationInstance>();

    AnnotationInstance associationOverrideAnnotation = JandexHelper.getSingleAnnotation(
        classInfo,
        JPADotNames.ASSOCIATION_OVERRIDE
    );
    if ( associationOverrideAnnotation != null ) {
      associationOverrideList.add( associationOverrideAnnotation );
    }

    AnnotationInstance associationOverridesAnnotation = JandexHelper.getSingleAnnotation(
        classInfo,
        JPADotNames.ASSOCIATION_OVERRIDES
    );
    if ( associationOverrideAnnotation != null ) {
      AnnotationInstance[] attributeOverride = associationOverridesAnnotation.value().asNestedArray();
      Collections.addAll( associationOverrideList, attributeOverride );
    }

    return associationOverrideList;
  }

  private String determineCustomTuplizer() {
    final AnnotationInstance tuplizersAnnotation = JandexHelper.getSingleAnnotation(
        classInfo, HibernateDotNames.TUPLIZERS
    );
    if ( tuplizersAnnotation == null ) {
      return null;
    }

    AnnotationInstance[] annotations = JandexHelper.getValue(
        tuplizersAnnotation,
        "value",
        AnnotationInstance[].class
    );

    AnnotationInstance pojoTuplizerAnnotation = null;
    for ( AnnotationInstance tuplizerAnnotation : annotations ) {
      if ( EntityMode.valueOf( tuplizerAnnotation.value( "entityModeType" ).asEnum() ) == EntityMode.POJO ) {
        pojoTuplizerAnnotation = tuplizerAnnotation;
        break;
      }
    }

    String customTuplizer = null;
    if ( pojoTuplizerAnnotation != null ) {
      customTuplizer = pojoTuplizerAnnotation.value( "impl" ).asString();
    }
    return customTuplizer;
  }
}
TOP

Related Classes of org.hibernate.metamodel.source.annotations.entity.ConfiguredClass

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.