/** Overrides the default behaviour to including binding of Grails domain classes. */
  @Override
  protected void secondPassCompile() throws MappingException {
    if (configLocked) {
      return;
    }

    // set the class loader to load Groovy classes
    if (grailsApplication != null) {
      Thread.currentThread().setContextClassLoader(grailsApplication.getClassLoader());
    }

    configureDomainBinder(grailsApplication, domainClasses);

    for (GrailsDomainClass domainClass : domainClasses) {
      if (!GrailsHibernateUtil.usesDatasource(domainClass, dataSourceName)) {
        continue;
      }
      final Mappings mappings = super.createMappings();
      Mapping m = binder.getMapping(domainClass);
      mappings.setAutoImport(m == null || m.getAutoImport());
      binder.bindClass(domainClass, mappings, sessionFactoryBeanName);
    }

    super.secondPassCompile();
    configLocked = true;
  }
  /** Overrides the default behaviour to including binding of Grails domain classes. */
  @Override
  protected void secondPassCompile() throws MappingException {
    final Thread currentThread = Thread.currentThread();
    final ClassLoader originalContextLoader = currentThread.getContextClassLoader();
    if (!configLocked) {
      if (LOG.isDebugEnabled())
        LOG.debug(
            "[GrailsAnnotationConfiguration] ["
                + domainClasses.size()
                + "] Grails domain classes to bind to persistence runtime");

      // do Grails class configuration
      configureDomainBinder(binder, grailsApplication, domainClasses);

      for (GrailsDomainClass domainClass : domainClasses) {

        final String fullClassName = domainClass.getFullName();

        String hibernateConfig = fullClassName.replace('.', '/') + ".hbm.xml";
        final ClassLoader loader = originalContextLoader;
        // don't configure Hibernate mapped classes
        if (loader.getResource(hibernateConfig) != null) continue;

        final Mappings mappings = super.createMappings();
        if (!GrailsHibernateUtil.usesDatasource(domainClass, dataSourceName)) {
          continue;
        }

        LOG.debug(
            "[GrailsAnnotationConfiguration] Binding persistent class [" + fullClassName + "]");

        Mapping m = binder.getMapping(domainClass);
        mappings.setAutoImport(m == null || m.getAutoImport());
        binder.bindClass(domainClass, mappings, sessionFactoryBeanName);
      }
    }

    try {
      currentThread.setContextClassLoader(grailsApplication.getClassLoader());
      super.secondPassCompile();
      createSubclassForeignKeys();
    } finally {
      currentThread.setContextClassLoader(originalContextLoader);
    }

    configLocked = true;
  }
 private Iterator getSubPropertyIterator(PersistentClass pc, String reducedName) {
   Value value = pc.getRecursiveProperty(reducedName).getValue();
   Iterator parentPropIter;
   if (value instanceof Component) {
     Component comp = (Component) value;
     parentPropIter = comp.getPropertyIterator();
   } else if (value instanceof ToOne) {
     ToOne toOne = (ToOne) value;
     PersistentClass referencedPc = mappings.getClass(toOne.getReferencedEntityName());
     if (toOne.getReferencedPropertyName() != null) {
       try {
         parentPropIter =
             ((Component)
                     referencedPc
                         .getRecursiveProperty(toOne.getReferencedPropertyName())
                         .getValue())
                 .getPropertyIterator();
       } catch (ClassCastException e) {
         throw new MappingException(
             "dotted notation reference neither a component nor a many/one to one", e);
       }
     } else {
       try {
         if (referencedPc.getIdentifierMapper() == null) {
           parentPropIter =
               ((Component) referencedPc.getIdentifierProperty().getValue()).getPropertyIterator();
         } else {
           parentPropIter = referencedPc.getIdentifierMapper().getPropertyIterator();
         }
       } catch (ClassCastException e) {
         throw new MappingException(
             "dotted notation reference neither a component nor a many/one to one", e);
       }
     }
   } else {
     throw new MappingException(
         "dotted notation reference neither a component nor a many/one to one");
   }
   return parentPropIter;
 }
  @Override
  @SuppressWarnings("unchecked")
  protected SessionFactory buildSessionFactory() throws Exception {
    // Create Configuration instance.
    Configuration config = newConfiguration();

    DataSource dataSource = getDataSource();
    if (dataSource != null) {
      // Make given DataSource available for SessionFactory configuration.
      configTimeDataSourceHolder.set(dataSource);
    }
    if (this.jtaTransactionManager != null) {
      // Make Spring-provided JTA TransactionManager available.
      configTimeTransactionManagerHolder.set(this.jtaTransactionManager);
    }
    if (this.cacheRegionFactory != null) {
      // Make Spring-provided Hibernate RegionFactory available.
      configTimeRegionFactoryHolder.set(this.cacheRegionFactory);
    }
    if (this.lobHandler != null) {
      // Make given LobHandler available for SessionFactory configuration.
      // Do early because because mapping resource might refer to custom types.
      configTimeLobHandlerHolder.set(this.lobHandler);
    }

    // Analogous to Hibernate EntityManager's Ejb3Configuration:
    // Hibernate doesn't allow setting the bean ClassLoader explicitly,
    // so we need to expose it as thread context ClassLoader accordingly.
    Thread currentThread = Thread.currentThread();
    ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
    boolean overrideClassLoader =
        (this.beanClassLoader != null && !this.beanClassLoader.equals(threadContextClassLoader));
    if (overrideClassLoader) {
      currentThread.setContextClassLoader(this.beanClassLoader);
    }

    try {
      if (isExposeTransactionAwareSessionFactory()) {
        // Set Hibernate 3.1+ CurrentSessionContext implementation,
        // providing the Spring-managed Session as current Session.
        // Can be overridden by a custom value for the corresponding Hibernate property.
        config.setProperty(
            Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
      }

      if (this.jtaTransactionManager != null) {
        // Set Spring-provided JTA TransactionManager as Hibernate property.
        config.setProperty(Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName());
        config.setProperty(
            Environment.TRANSACTION_MANAGER_STRATEGY,
            LocalTransactionManagerLookup.class.getName());
      } else {
        // Makes the Hibernate Session aware of the presence of a Spring-managed transaction.
        // Also sets connection release mode to ON_CLOSE by default.
        config.setProperty(
            Environment.TRANSACTION_STRATEGY, SpringTransactionFactory.class.getName());
      }

      if (this.entityInterceptor != null) {
        // Set given entity interceptor at SessionFactory level.
        config.setInterceptor(this.entityInterceptor);
      }

      if (this.namingStrategy != null) {
        // Pass given naming strategy to Hibernate Configuration.
        config.setNamingStrategy(this.namingStrategy);
      }

      if (this.typeDefinitions != null) {
        // Register specified Hibernate type definitions.
        Mappings mappings = config.createMappings();
        for (TypeDefinitionBean typeDef : this.typeDefinitions) {
          mappings.addTypeDef(
              typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());
        }
      }

      if (this.filterDefinitions != null) {
        // Register specified Hibernate FilterDefinitions.
        for (FilterDefinition filterDef : this.filterDefinitions) {
          config.addFilterDefinition(filterDef);
        }
      }

      if (this.configLocations != null) {
        for (Resource resource : this.configLocations) {
          // Load Hibernate configuration from given location.
          config.configure(resource.getURL());
        }
      }

      if (this.hibernateProperties != null) {
        // Add given Hibernate properties to Configuration.
        config.addProperties(this.hibernateProperties);
      }

      if (dataSource != null) {
        Class<?> providerClass = LocalDataSourceConnectionProvider.class;
        if (isUseTransactionAwareDataSource()
            || dataSource instanceof TransactionAwareDataSourceProxy) {
          providerClass = TransactionAwareDataSourceConnectionProvider.class;
        } else if (config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) {
          providerClass = LocalJtaDataSourceConnectionProvider.class;
        }
        // Set Spring-provided DataSource as Hibernate ConnectionProvider.
        config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName());
      }

      if (this.cacheRegionFactory != null) {
        // Expose Spring-provided Hibernate RegionFactory.
        config.setProperty(
            Environment.CACHE_REGION_FACTORY, LocalRegionFactoryProxy.class.getName());
      }

      if (this.mappingResources != null) {
        // Register given Hibernate mapping definitions, contained in resource files.
        for (String mapping : this.mappingResources) {
          Resource resource = new ClassPathResource(mapping.trim(), this.beanClassLoader);
          config.addInputStream(resource.getInputStream());
        }
      }

      if (this.mappingLocations != null) {
        // Register given Hibernate mapping definitions, contained in resource files.
        for (Resource resource : this.mappingLocations) {
          config.addInputStream(resource.getInputStream());
        }
      }

      if (this.cacheableMappingLocations != null) {
        // Register given cacheable Hibernate mapping definitions, read from the file system.
        for (Resource resource : this.cacheableMappingLocations) {
          config.addCacheableFile(resource.getFile());
        }
      }

      if (this.mappingJarLocations != null) {
        // Register given Hibernate mapping definitions, contained in jar files.
        for (Resource resource : this.mappingJarLocations) {
          config.addJar(resource.getFile());
        }
      }

      if (this.mappingDirectoryLocations != null) {
        // Register all Hibernate mapping definitions in the given directories.
        for (Resource resource : this.mappingDirectoryLocations) {
          File file = resource.getFile();
          if (!file.isDirectory()) {
            throw new IllegalArgumentException(
                "Mapping directory location [" + resource + "] does not denote a directory");
          }
          config.addDirectory(file);
        }
      }

      // Tell Hibernate to eagerly compile the mappings that we registered,
      // for availability of the mapping information in further processing.
      postProcessMappings(config);
      config.buildMappings();

      if (this.entityCacheStrategies != null) {
        // Register cache strategies for mapped entities.
        for (Enumeration<?> classNames = this.entityCacheStrategies.propertyNames();
            classNames.hasMoreElements(); ) {
          String className = (String) classNames.nextElement();
          String[] strategyAndRegion =
              StringUtils.commaDelimitedListToStringArray(
                  this.entityCacheStrategies.getProperty(className));
          if (strategyAndRegion.length > 1) {
            config.setCacheConcurrencyStrategy(
                className, strategyAndRegion[0], strategyAndRegion[1]);
          } else if (strategyAndRegion.length > 0) {
            config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);
          }
        }
      }

      if (this.collectionCacheStrategies != null) {
        // Register cache strategies for mapped collections.
        for (Enumeration<?> collRoles = this.collectionCacheStrategies.propertyNames();
            collRoles.hasMoreElements(); ) {
          String collRole = (String) collRoles.nextElement();
          String[] strategyAndRegion =
              StringUtils.commaDelimitedListToStringArray(
                  this.collectionCacheStrategies.getProperty(collRole));
          if (strategyAndRegion.length > 1) {
            config.setCollectionCacheConcurrencyStrategy(
                collRole, strategyAndRegion[0], strategyAndRegion[1]);
          } else if (strategyAndRegion.length > 0) {
            config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]);
          }
        }
      }

      if (this.eventListeners != null) {
        // Register specified Hibernate event listeners.
        for (Map.Entry<String, Object> entry : this.eventListeners.entrySet()) {
          String listenerType = entry.getKey();
          Object listenerObject = entry.getValue();
          if (listenerObject instanceof Collection) {
            Collection<Object> listeners = (Collection<Object>) listenerObject;
            EventListeners listenerRegistry = config.getEventListeners();
            Object[] listenerArray =
                (Object[])
                    Array.newInstance(
                        listenerRegistry.getListenerClassFor(listenerType), listeners.size());
            listenerArray = listeners.toArray(listenerArray);
            config.setListeners(listenerType, listenerArray);
          } else {
            config.setListener(listenerType, listenerObject);
          }
        }
      }

      // Perform custom post-processing in subclasses.
      postProcessConfiguration(config);

      // Build SessionFactory instance.
      logger.info("Building new Hibernate SessionFactory");
      this.configuration = config;
      return newSessionFactory(config);
    } finally {
      if (dataSource != null) {
        configTimeDataSourceHolder.remove();
      }
      if (this.jtaTransactionManager != null) {
        configTimeTransactionManagerHolder.remove();
      }
      if (this.cacheRegionFactory != null) {
        configTimeRegionFactoryHolder.remove();
      }
      if (this.lobHandler != null) {
        configTimeLobHandlerHolder.remove();
      }
      if (overrideClassLoader) {
        // Reset original thread context ClassLoader.
        currentThread.setContextClassLoader(threadContextClassLoader);
      }
    }
  }
  public void doSecondPass(Map persistentClasses) throws MappingException {
    // TODO add parameters checkings
    if (ann == null) return;
    ResultSetMappingDefinition definition = new ResultSetMappingDefinition(ann.name());
    LOG.debugf("Binding result set mapping: %s", definition.getName());

    int entityAliasIndex = 0;

    for (EntityResult entity : ann.entities()) {
      // TODO parameterize lock mode?
      List<FieldResult> properties = new ArrayList<FieldResult>();
      List<String> propertyNames = new ArrayList<String>();
      for (FieldResult field : entity.fields()) {
        // use an ArrayList cause we might have several columns per root property
        String name = field.name();
        if (name.indexOf('.') == -1) {
          // regular property
          properties.add(field);
          propertyNames.add(name);
        } else {
          /**
           * Reorder properties 1. get the parent property 2. list all the properties following the
           * expected one in the parent property 3. calculate the lowest index and insert the
           * property
           */
          PersistentClass pc = mappings.getClass(entity.entityClass().getName());
          if (pc == null) {
            throw new MappingException(
                "Entity not found "
                    + entity.entityClass().getName()
                    + " in SqlResultsetMapping "
                    + ann.name());
          }
          int dotIndex = name.lastIndexOf('.');
          String reducedName = name.substring(0, dotIndex);
          Iterator parentPropIter = getSubPropertyIterator(pc, reducedName);
          List followers = getFollowers(parentPropIter, reducedName, name);

          int index = propertyNames.size();
          int followersSize = followers.size();
          for (int loop = 0; loop < followersSize; loop++) {
            String follower = (String) followers.get(loop);
            int currentIndex = getIndexOfFirstMatchingProperty(propertyNames, follower);
            index = currentIndex != -1 && currentIndex < index ? currentIndex : index;
          }
          propertyNames.add(index, name);
          properties.add(index, field);
        }
      }

      Set<String> uniqueReturnProperty = new HashSet<String>();
      Map<String, ArrayList<String>> propertyResultsTmp = new HashMap<String, ArrayList<String>>();
      for (Object property : properties) {
        final FieldResult propertyresult = (FieldResult) property;
        final String name = propertyresult.name();
        if ("class".equals(name)) {
          throw new MappingException(
              "class is not a valid property name to use in a @FieldResult, use @Entity(discriminatorColumn) instead");
        }

        if (uniqueReturnProperty.contains(name)) {
          throw new MappingException(
              "duplicate @FieldResult for property "
                  + name
                  + " on @Entity "
                  + entity.entityClass().getName()
                  + " in "
                  + ann.name());
        }
        uniqueReturnProperty.add(name);

        final String quotingNormalizedColumnName =
            mappings.getObjectNameNormalizer().normalizeIdentifierQuoting(propertyresult.column());

        String key = StringHelper.root(name);
        ArrayList<String> intermediateResults = propertyResultsTmp.get(key);
        if (intermediateResults == null) {
          intermediateResults = new ArrayList<String>();
          propertyResultsTmp.put(key, intermediateResults);
        }
        intermediateResults.add(quotingNormalizedColumnName);
      }

      Map<String, String[]> propertyResults = new HashMap<String, String[]>();
      for (Map.Entry<String, ArrayList<String>> entry : propertyResultsTmp.entrySet()) {
        propertyResults.put(
            entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]));
      }

      if (!BinderHelper.isEmptyAnnotationValue(entity.discriminatorColumn())) {
        final String quotingNormalizedName =
            mappings
                .getObjectNameNormalizer()
                .normalizeIdentifierQuoting(entity.discriminatorColumn());
        propertyResults.put("class", new String[] {quotingNormalizedName});
      }

      if (propertyResults.isEmpty()) {
        propertyResults = java.util.Collections.emptyMap();
      }

      NativeSQLQueryRootReturn result =
          new NativeSQLQueryRootReturn(
              "alias" + entityAliasIndex++,
              entity.entityClass().getName(),
              propertyResults,
              LockMode.READ);
      definition.addQueryReturn(result);
    }

    for (ColumnResult column : ann.columns()) {
      definition.addQueryReturn(
          new NativeSQLQueryScalarReturn(
              mappings.getObjectNameNormalizer().normalizeIdentifierQuoting(column.name()), null));
    }

    for (ConstructorResult constructorResult : ann.classes()) {
      List<NativeSQLQueryScalarReturn> columnReturns = new ArrayList<NativeSQLQueryScalarReturn>();
      for (ColumnResult columnResult : constructorResult.columns()) {
        columnReturns.add(
            new NativeSQLQueryScalarReturn(
                mappings.getObjectNameNormalizer().normalizeIdentifierQuoting(columnResult.name()),
                null));
      }
      definition.addQueryReturn(
          new NativeSQLQueryConstructorReturn(constructorResult.targetClass(), columnReturns));
    }

    if (isDefault) {
      mappings.addDefaultResultSetMapping(definition);
    } else {
      mappings.addResultSetMapping(definition);
    }
  }