All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blazebit.persistence.impl.CriteriaBuilderFactoryImpl Maven / Gradle / Ivy

/*
 * Copyright 2014 - 2020 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.parser.expression.ExpressionCache;
import com.blazebit.persistence.parser.expression.ExpressionFactory;
import com.blazebit.persistence.parser.expression.ExpressionFactoryImpl;
import com.blazebit.persistence.parser.expression.MacroConfiguration;
import com.blazebit.persistence.parser.expression.SimpleCachingExpressionFactory;
import com.blazebit.persistence.parser.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 com.blazebit.persistence.spi.PackageOpener;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.Metamodel;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Christian Beikov
 * @since 1.0.0
 */
public class CriteriaBuilderFactoryImpl implements CriteriaBuilderFactory {

    private final PackageOpener packageOpener;
    private final EntityManagerFactory entityManagerFactory;
    private final EntityMetamodelImpl metamodel;
    private final AssociationParameterTransformerFactory transientEntityParameterTransformerFactory;
    private final ExtendedQuerySupport extendedQuerySupport;
    private final Map functions;
    private final Map, String> namedTypes;
    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 configuredRegisteredFunctions;
    private final JpaProviderFactory configuredJpaProviderFactory;
    private final JpaProvider jpaProvider;

    public CriteriaBuilderFactoryImpl(CriteriaBuilderConfigurationImpl config, EntityManagerFactory entityManagerFactory) {
        List 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 registeredFunctions = new HashMap<>(integrator.getRegisteredFunctions(emf));
        String dbms = integrator.getDbms(emf);
        Map dbmsDialects = config.getDbmsDialects();
        DbmsDialect dialect = dbmsDialects.get(dbms);

        // Use the default dialect
        if (dialect == null) {
            dialect = dbmsDialects.get(null);
        }

        this.packageOpener = config.getPackageOpener();
        this.configuredDbms = dbms;
        this.configuredDbmsDialect = dialect;
        this.configuredRegisteredFunctions = registeredFunctions;
        this.configuredJpaProviderFactory = integrator.getJpaProviderFactory(emf);

        this.queryConfiguration = new ImmutableQueryConfiguration((Map) (Map) config.getProperties());
        final boolean compatibleMode = queryConfiguration.isCompatibleModeEnabled();
        final boolean optimize = queryConfiguration.isExpressionOptimizationEnabled();

        this.entityManagerFactory = entityManagerFactory;
        this.metamodel = new EntityMetamodelImpl(entityManagerFactory, configuredJpaProviderFactory);
        this.jpaProvider = new CachingJpaProvider(metamodel);

        this.transientEntityParameterTransformerFactory = new TransientEntityAssociationParameterTransformerFactory(metamodel, new AssociationToIdParameterTransformer(jpaProvider));
        this.extendedQuerySupport = config.getExtendedQuerySupport();
        this.functions = resolveFunctions(config.getFunctions(), configuredRegisteredFunctions);
        this.namedTypes = resolveNamedTypes(config.getNamedTypes());

        ExpressionFactory originalExpressionFactory = new ExpressionFactoryImpl(functions, metamodel.getEntityTypes(), metamodel.getEnumTypes(), metamodel.getEnumTypesForLiterals(), !compatibleMode, optimize);
        this.expressionCache = createCache(queryConfiguration.getExpressionCacheClass());
        ExpressionFactory cachingExpressionFactory = new SimpleCachingExpressionFactory(originalExpressionFactory, expressionCache);
        ExpressionFactory cachingSubqueryExpressionFactory = new SimpleCachingExpressionFactory(new SubqueryExpressionFactory(functions, metamodel.getEntityTypes(), metamodel.getEnumTypes(), metamodel.getEnumTypesForLiterals(), !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);
    }

    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 Map resolveFunctions(Map functions, Map configuredFunctions) {
        Map map = new HashMap<>();
        for (Map.Entry entry : functions.entrySet()) {
            map.put(entry.getKey().toLowerCase(), entry.getValue().isAggregate());
        }
        // add standard JPQL aggregate functions
        map.put("sum", true);
        map.put("min", true);
        map.put("max", true);
        map.put("avg", true);
        map.put("count", true);

        for (Map.Entry entry : configuredFunctions.entrySet()) {
            if (!map.containsKey(entry.getKey())) {
                map.put(entry.getKey(), false);
            }
        }

        return map;
    }

    private static Map, String> resolveNamedTypes(Map> namedTypes) {
        Map, String> types = new HashMap, String>(namedTypes.size());
        for (Map.Entry> entry : namedTypes.entrySet()) {
            types.put(entry.getValue(), entry.getKey());
        }
        return Collections.unmodifiableMap(types);
    }

    public JpaProvider getJpaProvider() {
        return jpaProvider;
    }

    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 Map getFunctions() {
        return functions;
    }

    public Map, String> getNamedTypes() {
        return namedTypes;
    }

    public ExpressionCache getExpressionCache() {
        return expressionCache;
    }

    public ExpressionFactory getExpressionFactory() {
        return expressionFactory;
    }

    public ExpressionFactory getSubqueryExpressionFactory() {
        return subqueryExpressionFactory;
    }

    @Override
    public Map getRegisteredFunctions() {
        return Collections.unmodifiableMap(configuredRegisteredFunctions);
    }

    @Override
    public Map 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  StartOngoingSetOperationCriteriaBuilder> startSet(EntityManager entityManager, Class resultClass) {
        MainQuery mainQuery = createMainQuery(entityManager);
        FinalSetOperationCriteriaBuilderImpl parentFinalSetOperationBuilder = new FinalSetOperationCriteriaBuilderImpl(mainQuery, null, true, resultClass, null, false, null);
        OngoingFinalSetOperationCriteriaBuilderImpl subFinalSetOperationBuilder = new OngoingFinalSetOperationCriteriaBuilderImpl(mainQuery, null, false, resultClass, null, true, parentFinalSetOperationBuilder.getSubListener());
        
        LeafOngoingSetOperationCriteriaBuilderImpl leafCb = new LeafOngoingSetOperationCriteriaBuilderImpl(mainQuery, null, false, resultClass, parentFinalSetOperationBuilder.getSubListener(), parentFinalSetOperationBuilder);
        StartOngoingSetOperationCriteriaBuilderImpl> cb = new StartOngoingSetOperationCriteriaBuilderImpl>(mainQuery, null, 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  CriteriaBuilder create(EntityManager entityManager, Class resultClass) {
        return create(entityManager, resultClass, null);
    }

    @Override
    public  CriteriaBuilder create(EntityManager entityManager, Class resultClass, String alias) {
        MainQuery mainQuery = createMainQuery(entityManager);
        CriteriaBuilderImpl cb = new CriteriaBuilderImpl(mainQuery, true, resultClass, alias);
        return cb;
    }

    @Override
    public  DeleteCriteriaBuilder delete(EntityManager entityManager, Class deleteClass) {
        return delete(entityManager, deleteClass, null);
    }

    @Override
    public  DeleteCriteriaBuilder delete(EntityManager entityManager, Class deleteClass, String alias) {
        MainQuery mainQuery = createMainQuery(entityManager);
        DeleteCriteriaBuilderImpl cb = new DeleteCriteriaBuilderImpl(mainQuery, deleteClass, alias);
        return cb;
    }

    @Override
    public  DeleteCriteriaBuilder deleteCollection(EntityManager entityManager, Class deleteOwnerClass, String collectionName) {
        return deleteCollection(entityManager, deleteOwnerClass, null, collectionName);
    }

    @Override
    public  DeleteCriteriaBuilder deleteCollection(EntityManager entityManager, Class deleteOwnerClass, String alias, String collectionName) {
        MainQuery mainQuery = createMainQuery(entityManager);
        DeleteCollectionCriteriaBuilderImpl cb = new DeleteCollectionCriteriaBuilderImpl(mainQuery, deleteOwnerClass, alias, collectionName);
        return cb;
    }

    @Override
    public  UpdateCriteriaBuilder update(EntityManager entityManager, Class updateClass) {
        return update(entityManager, updateClass, null);
    }

    @Override
    public  UpdateCriteriaBuilder update(EntityManager entityManager, Class updateClass, String alias) {
        MainQuery mainQuery = createMainQuery(entityManager);
        UpdateCriteriaBuilderImpl cb = new UpdateCriteriaBuilderImpl(mainQuery, updateClass, alias);
        return cb;
    }

    @Override
    public  UpdateCriteriaBuilder updateCollection(EntityManager entityManager, Class updateOwnerClass, String collectionName) {
        return updateCollection(entityManager, updateOwnerClass, null, collectionName);
    }

    @Override
    public  UpdateCriteriaBuilder updateCollection(EntityManager entityManager, Class updateOwnerClass, String alias, String collectionName) {
        MainQuery mainQuery = createMainQuery(entityManager);
        UpdateCollectionCriteriaBuilderImpl cb = new UpdateCollectionCriteriaBuilderImpl(mainQuery, updateOwnerClass, alias, collectionName);
        return cb;
    }

    @Override
    public  InsertCriteriaBuilder insert(EntityManager entityManager, Class insertClass) {
        MainQuery mainQuery = createMainQuery(entityManager);
        InsertCriteriaBuilderImpl cb = new InsertCriteriaBuilderImpl(mainQuery, insertClass);
        return cb;
    }

    @Override
    public  InsertCriteriaBuilder insertCollection(EntityManager entityManager, Class insertOwnerClass, String collectionName) {
        MainQuery mainQuery = createMainQuery(entityManager);
        InsertCollectionCriteriaBuilderImpl cb = new InsertCollectionCriteriaBuilderImpl(mainQuery, insertOwnerClass, collectionName);
        return cb;
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T getService(Class 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 (JpaProvider.class.equals(serviceClass)) {
            return (T) jpaProvider;
        } else if (ExpressionCache.class.equals(serviceClass)) {
            return (T) expressionCache;
        } else if (Metamodel.class.isAssignableFrom(serviceClass)) {
            return (T) metamodel;
        } else if (EntityManagerFactory.class.equals(serviceClass)) {
            return (T) entityManagerFactory;
        } else if (PackageOpener.class.equals(serviceClass)) {
            if (CallerChecker.isCallerTrusted()) {
                return (T) packageOpener;
            }
        }

        return null;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy