/* * Copyright 2014 - 2017 Blazebit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.blazebit.persistence.impl; import com.blazebit.persistence.CriteriaBuilder; import com.blazebit.persistence.CriteriaBuilderFactory; import com.blazebit.persistence.DeleteCriteriaBuilder; import com.blazebit.persistence.InsertCriteriaBuilder; import com.blazebit.persistence.LeafOngoingFinalSetOperationCriteriaBuilder; import com.blazebit.persistence.StartOngoingSetOperationCriteriaBuilder; import com.blazebit.persistence.UpdateCriteriaBuilder; import com.blazebit.persistence.impl.expression.ExpressionCache; import com.blazebit.persistence.impl.expression.ExpressionFactory; import com.blazebit.persistence.impl.expression.ExpressionFactoryImpl; import com.blazebit.persistence.impl.expression.MacroConfiguration; import com.blazebit.persistence.impl.expression.SimpleCachingExpressionFactory; import com.blazebit.persistence.impl.expression.SubqueryExpressionFactory; import com.blazebit.persistence.spi.ConfigurationSource; import com.blazebit.persistence.spi.DbmsDialect; import com.blazebit.persistence.spi.EntityManagerFactoryIntegrator; import com.blazebit.persistence.spi.ExtendedQuerySupport; import com.blazebit.persistence.spi.JpaProvider; import com.blazebit.persistence.spi.JpaProviderFactory; import com.blazebit.persistence.spi.JpqlFunction; import com.blazebit.persistence.spi.JpqlFunctionGroup; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * * @author Christian Beikov * @since 1.0 */ public class CriteriaBuilderFactoryImpl implements CriteriaBuilderFactory { private final EntityMetamodelImpl metamodel; private final AssociationParameterTransformerFactory transientEntityParameterTransformerFactory; private final ExtendedQuerySupport extendedQuerySupport; private final Set<String> aggregateFunctions; private final Map<Class<?>, String> treatFunctions; private final ExpressionCache expressionCache; private final ExpressionFactory expressionFactory; private final ExpressionFactory subqueryExpressionFactory; private final QueryConfiguration queryConfiguration; private final MacroConfiguration macroConfiguration; private final String configuredDbms; private final DbmsDialect configuredDbmsDialect; private final Map<String, JpqlFunction> configuredRegisteredFunctions; private final JpaProviderFactory configuredJpaProviderFactory; public CriteriaBuilderFactoryImpl(CriteriaBuilderConfigurationImpl config, EntityManagerFactory entityManagerFactory) { this.queryConfiguration = new ImmutableQueryConfiguration((Map<String, String>) (Map<?, ?>) config.getProperties()); final boolean compatibleMode = queryConfiguration.isCompatibleModeEnabled(); final boolean optimize = queryConfiguration.isExpressionOptimizationEnabled(); this.metamodel = new EntityMetamodelImpl(entityManagerFactory, config.getExtendedQuerySupport()); this.transientEntityParameterTransformerFactory = new TransientEntityAssociationParameterTransformerFactory(metamodel, new AssociationToIdParameterTransformer(entityManagerFactory.getPersistenceUnitUtil())); this.extendedQuerySupport = config.getExtendedQuerySupport(); this.aggregateFunctions = resolveAggregateFunctions(config.getFunctions()); this.treatFunctions = resolveTreatTypes(config.getTreatTypes()); ExpressionFactory originalExpressionFactory = new ExpressionFactoryImpl(aggregateFunctions, metamodel.getEntityTypes(), metamodel.getEnumTypes(), !compatibleMode, optimize); this.expressionCache = createCache(queryConfiguration.getExpressionCacheClass()); ExpressionFactory cachingExpressionFactory = new SimpleCachingExpressionFactory(originalExpressionFactory, expressionCache); ExpressionFactory cachingSubqueryExpressionFactory = new SimpleCachingExpressionFactory(new SubqueryExpressionFactory(aggregateFunctions, metamodel.getEntityTypes(), metamodel.getEnumTypes(), !compatibleMode, optimize, originalExpressionFactory)); this.macroConfiguration = MacroConfiguration.of(JpqlMacroAdapter.createMacros(config.getMacros(), cachingExpressionFactory)); JpqlMacroStorage macroStorage = new JpqlMacroStorage(null, macroConfiguration); this.expressionFactory = new JpqlMacroAwareExpressionFactory(cachingExpressionFactory, macroStorage); this.subqueryExpressionFactory = new JpqlMacroAwareExpressionFactory(cachingSubqueryExpressionFactory, macroStorage); List<EntityManagerFactoryIntegrator> integrators = config.getEntityManagerIntegrators(); if (integrators.size() < 1) { throw new IllegalArgumentException("No EntityManagerFactoryIntegrator was found on the classpath! Please check if an integration for your JPA provider is visible on the classpath!"); } if (integrators.size() > 1) { throw new IllegalArgumentException("Multiple EntityManagerFactoryIntegrator were found on the classpath! Please remove the wrong integrations from the classpath!"); } EntityManagerFactoryIntegrator integrator = integrators.get(0); EntityManagerFactory emf = integrator.registerFunctions(entityManagerFactory, config.getFunctions()); Map<String, JpqlFunction> registeredFunctions = new HashMap<>(integrator.getRegisteredFunctions(emf)); String dbms = integrator.getDbms(emf); Map<String, DbmsDialect> dbmsDialects = config.getDbmsDialects(); DbmsDialect dialect = dbmsDialects.get(dbms); // Use the default dialect if (dialect == null) { dialect = dbmsDialects.get(null); } this.configuredDbms = dbms; this.configuredDbmsDialect = dialect; this.configuredRegisteredFunctions = registeredFunctions; this.configuredJpaProviderFactory = integrator.getJpaProviderFactory(emf); } private ExpressionCache createCache(String className) { try { return (ExpressionCache) Class.forName(className).newInstance(); } catch (Exception ex) { throw new IllegalArgumentException("Could not instantiate expression cache: " + className, ex); } } private static Set<String> resolveAggregateFunctions(Map<String, JpqlFunctionGroup> functions) { Set<String> aggregateFunctions = new HashSet<String>(); for (Map.Entry<String, JpqlFunctionGroup> entry : functions.entrySet()) { if (entry.getValue().isAggregate()) { aggregateFunctions.add(entry.getKey().toLowerCase()); } } return aggregateFunctions; } private static Map<Class<?>, String> resolveTreatTypes(Map<String, Class<?>> treatTypes) { Map<Class<?>, String> types = new HashMap<Class<?>, String>(treatTypes.size()); for (Map.Entry<String, Class<?>> entry : treatTypes.entrySet()) { types.put(entry.getValue(), "TREAT_" + entry.getKey().toUpperCase()); } return Collections.unmodifiableMap(types); } public JpaProvider createJpaProvider(EntityManager em) { return configuredJpaProviderFactory.createJpaProvider(em); } public QueryConfiguration getQueryConfiguration() { return queryConfiguration; } public EntityMetamodelImpl getMetamodel() { return metamodel; } public AssociationParameterTransformerFactory getTransientEntityParameterTransformerFactory() { return transientEntityParameterTransformerFactory; } public MacroConfiguration getMacroConfiguration() { return macroConfiguration; } public ExtendedQuerySupport getExtendedQuerySupport() { return extendedQuerySupport; } public Set<String> getAggregateFunctions() { return aggregateFunctions; } public Map<Class<?>, String> getTreatFunctions() { return treatFunctions; } public ExpressionCache getExpressionCache() { return expressionCache; } public ExpressionFactory getExpressionFactory() { return expressionFactory; } public ExpressionFactory getSubqueryExpressionFactory() { return subqueryExpressionFactory; } @Override public Map<String, JpqlFunction> getRegisteredFunctions() { return Collections.unmodifiableMap(configuredRegisteredFunctions); } @Override public Map<String, String> getProperties() { return queryConfiguration.getProperties(); } @Override public String getProperty(String propertyName) { return queryConfiguration.getProperty(propertyName); } public MainQuery createMainQuery(EntityManager entityManager) { return MainQuery.create(this, entityManager, configuredDbms, configuredDbmsDialect, configuredRegisteredFunctions); } @Override @SuppressWarnings("unchecked") public <T> StartOngoingSetOperationCriteriaBuilder<T, LeafOngoingFinalSetOperationCriteriaBuilder<T>> startSet(EntityManager entityManager, Class<T> resultClass) { MainQuery mainQuery = createMainQuery(entityManager); FinalSetOperationCriteriaBuilderImpl<T> parentFinalSetOperationBuilder = new FinalSetOperationCriteriaBuilderImpl<T>(mainQuery, true, resultClass, null, false, null); OngoingFinalSetOperationCriteriaBuilderImpl<T> subFinalSetOperationBuilder = new OngoingFinalSetOperationCriteriaBuilderImpl<T>(mainQuery, false, resultClass, null, true, parentFinalSetOperationBuilder.getSubListener()); LeafOngoingSetOperationCriteriaBuilderImpl<T> leafCb = new LeafOngoingSetOperationCriteriaBuilderImpl<T>(mainQuery, false, resultClass, parentFinalSetOperationBuilder.getSubListener(), parentFinalSetOperationBuilder); StartOngoingSetOperationCriteriaBuilderImpl<T, LeafOngoingFinalSetOperationCriteriaBuilder<T>> cb = new StartOngoingSetOperationCriteriaBuilderImpl<T, LeafOngoingFinalSetOperationCriteriaBuilder<T>>(mainQuery, false, resultClass, subFinalSetOperationBuilder.getSubListener(), subFinalSetOperationBuilder, leafCb); // TODO: This is such an ugly hack, but I don't know how else to fix this generics issue for now subFinalSetOperationBuilder.setEndSetResult((T) leafCb); subFinalSetOperationBuilder.setOperationManager.setStartQueryBuilder(cb); parentFinalSetOperationBuilder.setOperationManager.setStartQueryBuilder(subFinalSetOperationBuilder); subFinalSetOperationBuilder.getSubListener().onBuilderStarted(cb); parentFinalSetOperationBuilder.getSubListener().onBuilderStarted(leafCb); return cb; } @Override public <T> CriteriaBuilder<T> create(EntityManager entityManager, Class<T> resultClass) { return create(entityManager, resultClass, null); } @Override public <T> CriteriaBuilder<T> create(EntityManager entityManager, Class<T> resultClass, String alias) { MainQuery mainQuery = createMainQuery(entityManager); CriteriaBuilderImpl<T> cb = new CriteriaBuilderImpl<T>(mainQuery, true, resultClass, alias); return cb; } @Override public <T> DeleteCriteriaBuilder<T> delete(EntityManager entityManager, Class<T> deleteClass) { return delete(entityManager, deleteClass, null); } @Override public <T> DeleteCriteriaBuilder<T> delete(EntityManager entityManager, Class<T> deleteClass, String alias) { MainQuery mainQuery = createMainQuery(entityManager); DeleteCriteriaBuilderImpl<T> cb = new DeleteCriteriaBuilderImpl<T>(mainQuery, deleteClass, alias); return cb; } @Override public <T> UpdateCriteriaBuilder<T> update(EntityManager entityManager, Class<T> updateClass) { return update(entityManager, updateClass, null); } @Override public <T> UpdateCriteriaBuilder<T> update(EntityManager entityManager, Class<T> updateClass, String alias) { MainQuery mainQuery = createMainQuery(entityManager); UpdateCriteriaBuilderImpl<T> cb = new UpdateCriteriaBuilderImpl<T>(mainQuery, updateClass, alias); return cb; } @Override public <T> InsertCriteriaBuilder<T> insert(EntityManager entityManager, Class<T> insertClass) { MainQuery mainQuery = createMainQuery(entityManager); InsertCriteriaBuilderImpl<T> cb = new InsertCriteriaBuilderImpl<T>(mainQuery, insertClass); return cb; } @Override @SuppressWarnings("unchecked") public <T> T getService(Class<T> serviceClass) { if (SubqueryExpressionFactory.class.equals(serviceClass)) { return (T) subqueryExpressionFactory; } else if (ConfigurationSource.class.equals(serviceClass)) { return (T) this; } else if (ExpressionFactory.class.isAssignableFrom(serviceClass)) { return (T) expressionFactory; } else if (DbmsDialect.class.equals(serviceClass)) { return (T) configuredDbmsDialect; } else if (ExtendedQuerySupport.class.equals(serviceClass)) { return (T) extendedQuerySupport; } else if (JpaProviderFactory.class.equals(serviceClass)) { return (T) configuredJpaProviderFactory; } else if (ExpressionCache.class.equals(serviceClass)) { return (T) expressionCache; } else if (EntityMetamodel.class.equals(serviceClass)) { return (T) metamodel; } return null; } }