org.babyfish.hibernate.cfg.Configuration.java Source code

Java tutorial

Introduction

Here is the source code for org.babyfish.hibernate.cfg.Configuration.java

Source

/*
 * BabyFish, Object Model Framework for Java and JPA.
 * https://github.com/babyfish-ct/babyfish
 *
 * Copyright (c) 2008-2015, Tao Chen
 *
 * 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.
 *
 * Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
 *
 * 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.
 */
package org.babyfish.hibernate.cfg;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Properties;
import java.util.Set;

import org.babyfish.collection.XOrderedMap;
import org.babyfish.collection.XOrderedSet;
import org.babyfish.hibernate.XSessionFactory;
import org.babyfish.hibernate.association.EntityPropertyAccessor;
import org.babyfish.hibernate.collection.type.AbstractMACollectionType;
import org.babyfish.hibernate.collection.type.MACollectionProperties;
import org.babyfish.hibernate.collection.type.MAListType;
import org.babyfish.hibernate.collection.type.MANavigableMapType;
import org.babyfish.hibernate.collection.type.MANavigableSetType;
import org.babyfish.hibernate.collection.type.MAOrderedMapType;
import org.babyfish.hibernate.collection.type.MAOrderedSetType;
import org.babyfish.hibernate.dialect.InstallableDialect;
import org.babyfish.hibernate.event.ObjectModelMergeEventListener;
import org.babyfish.hibernate.hql.XQueryPlanCache;
import org.babyfish.hibernate.internal.SessionFactoryImplWrapper;
import org.babyfish.hibernate.model.loader.HibernateObjectModelScalarLoader;
import org.babyfish.hibernate.model.metadata.HibernateMetadatas;
import org.babyfish.hibernate.model.metadata.HibernateObjectModelMetadata;
import org.babyfish.hibernate.model.spi.HibernateObjectModelFactoryProvider;
import org.babyfish.lang.Arguments;
import org.babyfish.lang.IllegalProgramException;
import org.babyfish.lang.UncheckedException;
import org.babyfish.lang.reflect.PropertyInfo;
import org.babyfish.model.ObjectModel;
import org.babyfish.model.metadata.AssociationProperty;
import org.babyfish.model.metadata.Metadatas;
import org.babyfish.model.spi.ObjectModelFactoryProvider;
import org.babyfish.persistence.instrument.JPAObjectModelInstrument;
import org.babyfish.persistence.model.metadata.JPAAssociationProperty;
import org.babyfish.persistence.model.metadata.JPAObjectModelMetadata;
import org.babyfish.persistence.model.metadata.JPAProperty;
import org.babyfish.persistence.model.metadata.JPAScalarProperty;
import org.babyfish.util.LazyResource;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl;
import org.hibernate.bytecode.instrumentation.internal.FieldInterceptionHelper;
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.Settings;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.query.spi.QueryPlanCache;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.MergeEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.persister.spi.PersisterClassResolver;
import org.hibernate.persister.spi.PersisterFactory;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.internal.AbstractServiceRegistryImpl;
import org.hibernate.service.spi.ServiceInitiator;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.PojoEntityTuplizer;
import org.hibernate.type.AbstractStandardBasicType;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.BigDecimalType;
import org.hibernate.type.BigIntegerType;
import org.hibernate.type.DoubleType;
import org.hibernate.type.FloatType;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.SerializationException;
import org.hibernate.type.StringType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

/**
 * @author Tao Chen
 */
public class Configuration extends org.hibernate.cfg.Configuration {

    private static final long serialVersionUID = 7942896335754607308L;

    private static boolean pathPlanKeyVlidationSuspended;

    private static final Logger sessionFactoryImplLog = LoggerFactory
            .getLogger(org.hibernate.internal.SessionFactoryImpl.class);

    private static final LazyResource<Resource> LAZY_RESOURCE = LazyResource.of(Resource.class);

    private static final Method ABSTACT_SERVICE_RESGISTRY_IMPL_CREATE_SERVICE_BINDING;

    private static final Field QUERY_PLAN_CACHE_FIELD;

    private static final Method CHECK_NAMED_QUERIES_METHOD;

    private static final String INSTRUMENT_EXPECTED_POM_SECTION = "<plugin>\n"
            + "  <artifactId>maven-antrun-plugin</artifactId>\n" + "  <executions>\n" + "    <execution>\n"
            + "      <phase>process-test-classes</phase>\n" + "      <goals>\n" + "        <goal>run</goal>\n"
            + "      </goals>\n" + "    </execution>\n" + "  </executions>\n" + "  <dependencies>\n"
            + "    <dependency>\n" + "      <groupId>org.babyfish</groupId>\n"
            + "      <artifactId>babyfish-hibernate-tool</artifactId>\n"
            + "      <version>${babyfish.version}</version>\n" + "    </dependency>\n" + "  </dependencies>\n"
            + "  <configuration>\n" + "    <tasks>\n"
            + "      <taskdef name=\"instrument\" classname=\"org.babyfish.hibernate.tool.InstrumentTask\">\n"
            + "        <classpath>\n" + "          <path refid=\"maven.runtime.classpath\" />\n"
            + "          <path refid=\"maven.plugin.classpath\" />\n" + "        </classpath>\n"
            + "      </taskdef>\n" + "      <instrument>\n"
            + "        <fileset dir=\"${project.build.outputDirectory}\">\n"
            + "          <include name=\"**/entities/*.class\" />\n" + "        </fileset>\n"
            + "      </instrument>\n" + "    </tasks>\n" + "  </configuration>\n" + "</plugin>";

    public Configuration() {
        super(new org.babyfish.hibernate.cfg.SettingsFactory());
        this.registerXPojoEntityTuplizer();
        this.addNativeFunctions();
    }

    protected Configuration(org.babyfish.hibernate.cfg.SettingsFactory settingsFactory) {
        super(settingsFactory);
        this.registerXPojoEntityTuplizer();
        this.addNativeFunctions();
    }

    @Override
    public Configuration configure() throws HibernateException {
        super.configure();
        return this;
    }

    @Override
    public Configuration configure(String resource) throws HibernateException {
        super.configure(resource);
        return this;
    }

    @Override
    public Configuration configure(URL url) throws HibernateException {
        super.configure(url);
        return this;
    }

    @Override
    public Configuration configure(File configFile) throws HibernateException {
        super.configure(configFile);
        return this;
    }

    @Override
    public Configuration configure(Document document) throws HibernateException {
        super.configure(document);
        return this;
    }

    @Override
    public Configuration addFile(String xmlFile) throws MappingException {
        super.addFile(xmlFile);
        return this;
    }

    @Override
    public Configuration addFile(File xmlFile) throws MappingException {
        super.addFile(xmlFile);
        return this;
    }

    @Override
    public Configuration addCacheableFile(File xmlFile) throws MappingException {
        super.addCacheableFile(xmlFile);
        return this;
    }

    @Override
    public Configuration addCacheableFileStrictly(File xmlFile)
            throws SerializationException, FileNotFoundException {
        super.addCacheableFileStrictly(xmlFile);
        return this;
    }

    @Override
    public Configuration addCacheableFile(String xmlFile) throws MappingException {
        super.addCacheableFile(xmlFile);
        return this;
    }

    @Override
    public Configuration addXML(String xml) throws MappingException {
        super.addXML(xml);
        return this;
    }

    @Override
    public Configuration addURL(URL url) throws MappingException {
        super.addURL(url);
        return this;
    }

    @Override
    public Configuration addDocument(Document doc) throws MappingException {
        super.addDocument(doc);
        return this;
    }

    @Override
    public Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
        super.addInputStream(xmlInputStream);
        return this;
    }

    @Override
    public Configuration addResource(String resourceName, ClassLoader classLoader) throws MappingException {
        super.addResource(resourceName, classLoader);
        return this;
    }

    @Override
    public Configuration addResource(String resourceName) throws MappingException {
        super.addResource(resourceName);
        return this;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Configuration addClass(Class persistentClass) throws MappingException {
        super.addClass(persistentClass);
        return this;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Configuration addAnnotatedClass(Class annotatedClass) {
        super.addAnnotatedClass(annotatedClass);
        return this;
    }

    @Override
    public Configuration addPackage(String packageName) throws MappingException {
        super.addPackage(packageName);
        return this;
    }

    @Override
    public Configuration addJar(File jar) throws MappingException {
        super.addJar(jar);
        return this;
    }

    @Override
    public Configuration addDirectory(File dir) throws MappingException {
        super.addDirectory(dir);
        return this;
    }

    @Override
    public Configuration setInterceptor(Interceptor interceptor) {
        super.setInterceptor(interceptor);
        return this;
    }

    @Override
    public Configuration setProperties(Properties properties) {
        super.setProperties(properties);
        return this;
    }

    @Override
    public Configuration addProperties(Properties extraProperties) {
        super.addProperties(extraProperties);
        return this;
    }

    @Override
    public Configuration setProperty(String propertyName, String value) {
        super.setProperty(propertyName, value);
        return this;
    }

    @Override
    public Configuration setCacheConcurrencyStrategy(String entityName, String concurrencyStrategy) {
        super.setCacheConcurrencyStrategy(entityName, concurrencyStrategy);
        return this;
    }

    @Override
    public Configuration setCacheConcurrencyStrategy(String entityName, String concurrencyStrategy, String region) {
        super.setCacheConcurrencyStrategy(entityName, concurrencyStrategy, region);
        return this;
    }

    @Override
    public Configuration setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy) {
        super.setCollectionCacheConcurrencyStrategy(collectionRole, concurrencyStrategy);
        return this;
    }

    @Override
    public Configuration setNamingStrategy(NamingStrategy namingStrategy) {
        super.setNamingStrategy(namingStrategy);
        return this;
    }

    protected XQueryPlanCache createQueryPlanCache(SessionFactoryImplementor factory) {
        return new XQueryPlanCache(factory);
    }

    @Override
    protected void secondPassCompile() throws MappingException {
        super.secondPassCompile();
        replaceBasicTypesJavaTypeDescriptor();
        processPersistentClasses();
    }

    @Deprecated
    @Override
    public XSessionFactory buildSessionFactory() throws HibernateException {
        return (XSessionFactory) super.buildSessionFactory();
    }

    @Override
    public XSessionFactory buildSessionFactory(ServiceRegistry serviceRegistry) throws HibernateException {
        Arguments.mustBeInstanceOfValue("serviceRegistry",
                Arguments.mustNotBeNull("serviceRegistry", serviceRegistry), StandardServiceRegistryImpl.class);
        replacePersisterClassResolver((AbstractServiceRegistryImpl) serviceRegistry);

        String originalCurrentSessionContext = this.getProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS);
        this.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
        SessionFactoryImpl factory;
        try {
            pathPlanKeyVlidationSuspended = true;
            try {
                factory = (SessionFactoryImpl) super.buildSessionFactory(serviceRegistry);
            } finally {
                pathPlanKeyVlidationSuspended = false;
            }
        } finally {
            if (originalCurrentSessionContext != null) {
                this.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, originalCurrentSessionContext);
            } else {
                this.getProperties().remove(Environment.CURRENT_SESSION_CONTEXT_CLASS);
            }
        }
        if (originalCurrentSessionContext != null) {
            factory.getProperties().setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS,
                    originalCurrentSessionContext);
        } else {
            factory.getProperties().remove(Environment.CURRENT_SESSION_CONTEXT_CLASS);
        }

        Dialect dialect = factory.getDialect();
        if (dialect instanceof InstallableDialect) {
            ((InstallableDialect) dialect).install(factory);
        }

        EventListenerGroup<MergeEventListener> mergeEventListenerGroup = factory.getServiceRegistry()
                .getService(EventListenerRegistry.class).getEventListenerGroup(EventType.MERGE);
        MergeEventListener mergeEventListener = new ObjectModelMergeEventListener(
                mergeEventListenerGroup.listeners());
        mergeEventListenerGroup.clear();
        mergeEventListenerGroup.appendListener(mergeEventListener);

        setQueryPlanceCache(factory, this.createQueryPlanCache(factory));

        for (ClassMetadata classMetadata : factory.getAllClassMetadata().values()) {
            if (Metadatas.getObjectModelFactoryProvider(classMetadata.getMappedClass()) != null) {
                //Validate whether JPA configuration is same with object model configuration
                HibernateMetadatas.of(classMetadata.getMappedClass()).getPersistentClass(factory);
            }
        }

        return SessionFactoryImplWrapper.wrap(factory);
    }

    public static boolean isPathPlanKeyVlidationSuspended() {
        return pathPlanKeyVlidationSuspended;
    }

    private static void replacePersisterClassResolver(AbstractServiceRegistryImpl abstractServiceRegistryImpl) {
        try {
            ABSTACT_SERVICE_RESGISTRY_IMPL_CREATE_SERVICE_BINDING.invoke(abstractServiceRegistryImpl,
                    new StandardServiceInitiator<PersisterClassResolver>() {
                        @Override
                        public Class<PersisterClassResolver> getServiceInitiated() {
                            return PersisterClassResolver.class;
                        }

                        @SuppressWarnings("rawtypes")
                        @Override
                        public PersisterClassResolver initiateService(Map configurationValues,
                                ServiceRegistryImplementor registry) {
                            return new org.babyfish.hibernate.persister.StandardPersisterClassResolver();
                        }
                    });
            ABSTACT_SERVICE_RESGISTRY_IMPL_CREATE_SERVICE_BINDING.invoke(abstractServiceRegistryImpl,
                    new StandardServiceInitiator<PersisterFactory>() {
                        @Override
                        public Class<PersisterFactory> getServiceInitiated() {
                            return PersisterFactory.class;
                        }

                        @SuppressWarnings("rawtypes")
                        @Override
                        public PersisterFactory initiateService(Map configurationValues,
                                ServiceRegistryImplementor registry) {
                            return new org.hibernate.persister.internal.PersisterFactoryImpl();
                        }
                    });
        } catch (IllegalAccessException ex) {
            throw new AssertionError(ex);
        } catch (InvocationTargetException ex) {
            UncheckedException.rethrow(ex.getTargetException());
        }
    }

    @SuppressWarnings("unchecked")
    private void replaceBasicTypesJavaTypeDescriptor() {
        TypeResolver typeResolver = this.getTypeResolver();
        BasicTypeRegistry basicTypeRegistry;
        try {
            Field field = typeResolver.getClass().getDeclaredField("basicTypeRegistry");
            field.setAccessible(true);
            basicTypeRegistry = (BasicTypeRegistry) field.get(typeResolver);
        } catch (NoSuchFieldException | IllegalAccessException ex) {
            throw new AssertionError(ex);
        }

        Map<String, BasicType> registry;
        try {
            Field field = basicTypeRegistry.getClass().getDeclaredField("registry");
            field.setAccessible(true);
            registry = (Map<String, BasicType>) field.get(basicTypeRegistry);
        } catch (NoSuchFieldException | IllegalAccessException ex) {
            throw new AssertionError(ex);
        }

        for (BasicType basicType : registry.values()) {
            if (basicType instanceof AbstractStandardBasicType<?>) {
                WrapperJavaTypeDescriptor.replaceJavaTypeDescriptor((AbstractStandardBasicType<?>) basicType);
            }
        }
    }

    private void processPersistentClasses() {
        Iterator<PersistentClass> classMappings = this.getClassMappings();
        while (classMappings.hasNext()) { //TODO: please iterate its subclasses.
            PersistentClass persistentClass = classMappings.next();
            Class<?> mappedClass = persistentClass.getMappedClass();
            ObjectModelFactoryProvider provider = Metadatas.getObjectModelFactoryProvider(mappedClass);
            if (provider == null) {
                if (mappedClass.isAnnotationPresent(JPAObjectModelInstrument.class)) {
                    throw new IllegalProgramException(LAZY_RESOURCE.get().missInstrument(mappedClass,
                            JPAObjectModelInstrument.class, INSTRUMENT_EXPECTED_POM_SECTION));
                }
            } else {
                if (!(provider instanceof HibernateObjectModelFactoryProvider)) {
                    throw new IllegalProgramException(
                            LAZY_RESOURCE.get().requiredHibernateObjectModelFactoryProvider(mappedClass,
                                    HibernateObjectModelFactoryProvider.class));
                }
                HibernateObjectModelMetadata metadata = HibernateMetadatas.of(mappedClass);
                for (org.babyfish.model.metadata.Property property : metadata.getDeclaredProperties().values()) {
                    JPAProperty jpaProperty = (JPAProperty) property;
                    if (jpaProperty instanceof AssociationProperty) {
                        AssociationProperty associationProperty = (AssociationProperty) jpaProperty;
                        if (associationProperty.getCovarianceProperty() != null) {
                            continue;
                        }
                    }
                    PropertyInfo ownerProperty = jpaProperty.getOwnerProperty();
                    if (ownerProperty == null) {
                        continue;
                    }
                    Property mappingProperty = persistentClass.getProperty(ownerProperty.getName());
                    mappingProperty.setPropertyAccessorName(EntityPropertyAccessor.class.getName());
                    Value value = mappingProperty.getValue();
                    if (property instanceof AssociationProperty) {
                        JPAAssociationProperty jpaAssociationProperty = (JPAAssociationProperty) property;
                        Class<?> standardReturnType = jpaAssociationProperty.getStandardReturnClass();
                        /*
                         * (1) Don't use jpaAssocationProperty.getHibernateProperty().
                         * This is org.hiberante.mapping.Property, that is org.hibernate.tuple.Property
                         * 
                         * (2) Don't invoke property.getType() or property.getValue().getType()
                         * that will cause the creating of original collection-type before the replacement.
                         */
                        if (jpaAssociationProperty.getCovarianceProperty() == null) {
                            if (standardReturnType == NavigableMap.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Map.class,
                                        MANavigableMapType.class);
                            } else if (standardReturnType == XOrderedMap.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Map.class,
                                        MAOrderedMapType.class);
                            } else if (standardReturnType == Map.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Map.class,
                                        MAOrderedMapType.class);
                            } else if (standardReturnType == NavigableSet.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Set.class,
                                        MANavigableSetType.class);
                            } else if (standardReturnType == XOrderedSet.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Set.class,
                                        MAOrderedSetType.class);
                            } else if (standardReturnType == Set.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Set.class,
                                        MAOrderedSetType.class);
                            } else if (standardReturnType == List.class) {
                                if (org.hibernate.mapping.Bag.class
                                        .isAssignableFrom(mappingProperty.getValue().getClass())) {
                                    throw new MappingException(
                                            "In ObjectModel4ORM, Bag proeprty must be declared as java.util.Collection(not java.util.List)");
                                }
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.List.class,
                                        MAListType.class);
                            }
                            if (standardReturnType == Collection.class) {
                                replaceUserCollectionType(mappingProperty, org.hibernate.mapping.Bag.class,
                                        MAOrderedSetType.class);
                            }
                            if (value instanceof org.hibernate.mapping.Collection) {
                                org.hibernate.mapping.Collection collection = (org.hibernate.mapping.Collection) value;
                                collection.setTypeParameters(new MACollectionProperties(jpaAssociationProperty,
                                        collection.getTypeParameters()));
                            }

                            if (jpaAssociationProperty.getOwnerIndexProperty() != null) {
                                persistentClass
                                        .getProperty(jpaAssociationProperty.getOwnerIndexProperty().getName())
                                        .setPropertyAccessorName(EntityPropertyAccessor.class.getName());
                            }
                            if (jpaAssociationProperty.getOwnerKeyProperty() != null) {
                                persistentClass.getProperty(jpaAssociationProperty.getOwnerKeyProperty().getName())
                                        .setPropertyAccessorName(EntityPropertyAccessor.class.getName());
                            }
                        }
                    }
                }
            }
        }
    }

    private void registerXPojoEntityTuplizer() {
        this.getEntityTuplizerFactory().registerDefaultTuplizerClass(EntityMode.POJO, XPojoEntityTuplizer.class);
    }

    @SuppressWarnings("unchecked")
    private void addNativeFunctions() {
        Map<String, SQLFunction> map = this.getSqlFunctions();

        map.put("nativeInteger", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return IntegerType.INSTANCE;
            }
        });
        map.put("nativeLong", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return LongType.INSTANCE;
            }
        });
        map.put("nativeFloat", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return FloatType.INSTANCE;
            }
        });
        map.put("nativeDouble", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return DoubleType.INSTANCE;
            }
        });
        map.put("nativeBigInteger", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return BigIntegerType.INSTANCE;
            }
        });
        map.put("nativeBigDecimal", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return BigDecimalType.INSTANCE;
            }
        });
        map.put("nativeString", new AbstractNativeFunction() {
            @Override
            public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
                return StringType.INSTANCE;
            }
        });
        //TODO: more types, all the derived types of org.hibernate.type.AbstractSingleColumnStandardBasicType
    }

    private static void replaceUserCollectionType(Property mappingProperty,
            Class<? extends org.hibernate.mapping.Collection> hibernateCollectionType,
            Class<? extends AbstractMACollectionType> babyfishCollectionType) {
        /*
         * Don't invoke property.getType() or property.getValue().getType()
         * that will cause the creating of original collection-type before the replacement.
         * that is is slow
         */
        Value value = mappingProperty.getValue();
        if (!(value instanceof org.hibernate.mapping.Collection)) {
            throw new MappingException('"' + mappingProperty.getPersistentClass().getEntityName() + '.'
                    + mappingProperty.getName() + "\" must be mapped as collection.");
        }
        org.hibernate.mapping.Collection collection = (org.hibernate.mapping.Collection) value;
        String typeName = collection.getTypeName();
        if (typeName == null) {
            if (!hibernateCollectionType.isAssignableFrom(value.getClass())) {
                throw new MappingException('"' + mappingProperty.getPersistentClass().getEntityName() + '.'
                        + mappingProperty.getName() + "\" must be mapped collection whose hibernate type is \""
                        + hibernateCollectionType.getName() + "\".");
            }
            collection.setTypeName(babyfishCollectionType.getName());
        } else {
            Class<?> userCollctionType;
            try {
                userCollctionType = ReflectHelper.classForName(typeName);
            } catch (ClassNotFoundException ex) {
                throw new MappingException(
                        '"' + mappingProperty.getPersistentClass().getEntityName() + '.' + mappingProperty.getName()
                                + "\" must be mapped as collection whose attribute \"collection-type\" is \""
                                + typeName + "\", but the there is no java type names\"" + typeName + "\".");
            }
            if (!babyfishCollectionType.isAssignableFrom(userCollctionType)) {
                throw new MappingException(
                        '"' + mappingProperty.getPersistentClass().getEntityName() + '.' + mappingProperty.getName()
                                + "\" must be mapped as collection whose attribut \"collection-type\" is \""
                                + typeName + "\", but the there class \"" + typeName + "\" is not \""
                                + babyfishCollectionType.getName() + "\" or its derived class.");
            }
        }
    }

    // This static internal method will be invoked by the bytec-ode generated by me runtime
    static void setQueryPlanceCache(SessionFactoryImpl factory, QueryPlanCache queryPlanCache) {
        try {
            QUERY_PLAN_CACHE_FIELD.set(factory, queryPlanCache);
        } catch (IllegalAccessException ex) {
            throw new AssertionError();
        }
    }

    //This static internal method will be invoked by the bytec-ode generated by me runtime
    @SuppressWarnings("unchecked")
    static void checkNamedQueries(SessionFactoryImpl factory) {
        Settings settings = factory.getSettings();
        if (settings.isNamedQueryStartupCheckingEnabled()) {
            Map<String, HibernateException> errors;
            try {
                errors = (Map<String, HibernateException>) CHECK_NAMED_QUERIES_METHOD.invoke(factory);
            } catch (IllegalAccessException ex) {
                throw new AssertionError();
            } catch (InvocationTargetException ex) {
                throw UncheckedException.rethrow(ex.getTargetException());
            }
            if (!errors.isEmpty()) {
                boolean addComma = false;
                StringBuilder builder = new StringBuilder("Errors in named queries: ");
                for (Entry<String, HibernateException> entry : errors.entrySet()) {
                    String queryName = entry.getKey();
                    HibernateException e = entry.getValue();
                    if (addComma) {
                        builder.append(", ");
                    } else {
                        addComma = true;
                    }
                    builder.append(queryName);
                    sessionFactoryImplLog.error("Error in named query: " + queryName, e);
                }
                throw new HibernateException(builder.toString());
            }
        }
    }

    private static class XPojoEntityTuplizer extends PojoEntityTuplizer {

        public XPojoEntityTuplizer(EntityMetamodel entityMetamodel, EntityBinding mappedEntity) {
            super(entityMetamodel, mappedEntity);
        }

        public XPojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
            super(entityMetamodel, mappedEntity);
        }

        @Override
        public Object[] getPropertyValues(Object entity) throws HibernateException {
            Object[] arr = super.getPropertyValues(entity);
            FieldInterceptor fieldInterceptor = FieldInterceptionHelper.extractFieldInterceptor(entity);
            if (fieldInterceptor instanceof HibernateObjectModelScalarLoader) {
                HibernateObjectModelScalarLoader hibernateObjectModelScalarLoader = (HibernateObjectModelScalarLoader) fieldInterceptor;
                ObjectModel objectModel = hibernateObjectModelScalarLoader.getObjectModel();
                JPAObjectModelMetadata objectModelMetadata = objectModel.getObjectModelMetadata();
                NonIdentifierAttribute[] attributes = this.getEntityMetamodel().getProperties();
                for (int i = attributes.length - 1; i >= 0; i--) {
                    if (arr[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY) {
                        JPAProperty property = objectModelMetadata.getMappingSources().get(attributes[i].getName());
                        if (property instanceof JPAScalarProperty && !objectModel.isUnloaded(property.getId())) {
                            arr[i] = objectModel.getScalar(property.getId());
                        }
                    }
                }
            }
            return arr;
        }
    }

    private static class WrapperJavaTypeDescriptor implements JavaTypeDescriptor<Object> {

        private static final long serialVersionUID = -8627363999213768175L;

        private JavaTypeDescriptor<Object> raw;

        @SuppressWarnings("unchecked")
        public WrapperJavaTypeDescriptor(JavaTypeDescriptor<?> raw) {
            super();
            this.raw = (JavaTypeDescriptor<Object>) raw;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        static void replaceJavaTypeDescriptor(AbstractStandardBasicType<?> abstractStandardBasicType) {
            JavaTypeDescriptor<?> javaTypeDescriptor = abstractStandardBasicType.getJavaTypeDescriptor();
            if (javaTypeDescriptor != null && !(javaTypeDescriptor instanceof WrapperJavaTypeDescriptor)) {
                abstractStandardBasicType.setJavaTypeDescriptor(
                        (JavaTypeDescriptor) new WrapperJavaTypeDescriptor(javaTypeDescriptor));
            }
        }

        @Override
        public boolean areEqual(Object one, Object another) {
            // Fix the bugs of Hibernate!
            //
            // Hibernate can NOT guarantee that the parameters of this method
            // can't be LazyPropertyInitializer.UNFETCHED_PROPERTY absolutely,
            // so I intercept the JavaTypeDescriptor and add the logic here
            // though it does not looks like a good idea.
            //
            // One of these bugs is reported by me: 
            // Please see "https://hibernate.atlassian.net/browse/HHH-9379"
            if (one == another) {
                return true;
            }
            if (one == LazyPropertyInitializer.UNFETCHED_PROPERTY
                    || another == LazyPropertyInitializer.UNFETCHED_PROPERTY) {
                return false;
            }
            return raw.areEqual(one, another);
        }

        @Override
        public Class<Object> getJavaTypeClass() {
            return raw.getJavaTypeClass();
        }

        @Override
        public MutabilityPlan<Object> getMutabilityPlan() {
            return raw.getMutabilityPlan();
        }

        @Override
        public Comparator<Object> getComparator() {
            return raw.getComparator();
        }

        @Override
        public int extractHashCode(Object value) {
            return raw.extractHashCode(value);
        }

        @Override
        public String extractLoggableRepresentation(Object value) {
            return raw.extractLoggableRepresentation(value);
        }

        @Override
        public String toString(Object value) {
            return raw.toString(value);
        }

        @Override
        public Object fromString(String string) {
            return raw.fromString(string);
        }

        @Override
        public <X> X unwrap(Object value, Class<X> type, WrapperOptions options) {
            return raw.unwrap(value, type, options);
        }

        @Override
        public <X> Object wrap(X value, WrapperOptions options) {
            return raw.wrap(value, options);
        }
    }

    private static abstract class AbstractNativeFunction implements SQLFunction {

        @Override
        public boolean hasArguments() {
            return true;
        }

        @Override
        public boolean hasParenthesesIfNoArguments() {
            return true;
        }

        @SuppressWarnings("rawtypes")
        @Override
        public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory)
                throws QueryException {
            return "1";
        }

    }

    private interface Resource {

        String requiredHibernateObjectModelFactoryProvider(Class<?> entityClass,
                Class<HibernateObjectModelFactoryProvider> hibernateObjectModelFactoryProvider);

        String missInstrument(Class<?> entityClass,
                Class<JPAObjectModelInstrument> jpaObjectModelInstrumentConstant,
                String instrumentExpectedPOMSection);
    }

    static {
        Method abstractServiceRegistryImplCreateServiceBinding;
        try {
            abstractServiceRegistryImplCreateServiceBinding = AbstractServiceRegistryImpl.class
                    .getDeclaredMethod("createServiceBinding", ServiceInitiator.class);
        } catch (NoSuchMethodException ex) {
            throw new AssertionError(ex);
        }
        abstractServiceRegistryImplCreateServiceBinding.setAccessible(true);

        Method checkNamedQueriesMethod;
        try {
            checkNamedQueriesMethod = SessionFactoryImpl.class.getDeclaredMethod("checkNamedQueries");
        } catch (NoSuchMethodException ex) {
            throw new AssertionError(ex);
        }
        checkNamedQueriesMethod.setAccessible(true);

        Field queryPlanCacheField;
        try {
            queryPlanCacheField = SessionFactoryImpl.class.getDeclaredField("queryPlanCache");
        } catch (NoSuchFieldException ex) {
            throw new AssertionError(ex);
        }
        queryPlanCacheField.setAccessible(true);

        ABSTACT_SERVICE_RESGISTRY_IMPL_CREATE_SERVICE_BINDING = abstractServiceRegistryImplCreateServiceBinding;
        CHECK_NAMED_QUERIES_METHOD = checkNamedQueriesMethod;
        QUERY_PLAN_CACHE_FIELD = queryPlanCacheField;
    }

}