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

org.omnifaces.persistence.Provider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021 OmniFaces
 *
 * 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
 *
 *     https://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 org.omnifaces.persistence;

import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.MANY_TO_ONE;
import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_MANY;
import static jakarta.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_ONE;
import static org.omnifaces.utils.Collections.unmodifiableSet;
import static org.omnifaces.utils.Lang.isOneOf;
import static org.omnifaces.utils.reflect.Reflections.findClass;
import static org.omnifaces.utils.reflect.Reflections.findMethod;
import static org.omnifaces.utils.reflect.Reflections.invokeMethod;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;

import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.metamodel.Attribute;

import org.omnifaces.persistence.model.BaseEntity;
import org.omnifaces.persistence.service.BaseEntityService;

/**
 * Enumeration of all supported JPA providers.
 */
public enum Provider {

	HIBERNATE {

		@Override
		public String getDialectName(EntityManagerFactory entityManagerFactory) {
			var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory);

			if (HIBERNATE_SESSION_FACTORY.get().isInstance(unwrappedEntityManagerFactory)) {
				// 5.2+ has merged hibernate-entitymanager into hibernate-core, and made EntityManagerFactory impl an instance of SessionFactory, and removed getDialect() shortcut method.
				return invokeMethod(invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getJdbcServices"), "getJdbcEnvironment"), "getDialect").getClass().getSimpleName();
			}
			else {
				return invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getSessionFactory"), "getDialect").getClass().getSimpleName();
			}
		}

		@Override
		public boolean isAggregation(Expression expression) {
		    if (HIBERNATE_6_0_0_AGGREGATE_FUNCTION.isPresent()) {
                return HIBERNATE_6_0_0_AGGREGATE_FUNCTION.get().isInstance(expression);
		    }
		    else {
		        return HIBERNATE_BASIC_FUNCTION_EXPRESSION.get().isInstance(expression) && (boolean) invokeMethod(expression, "isAggregation")
	                || HIBERNATE_COMPARISON_PREDICATE.get().isInstance(expression) && (isAggregation(invokeMethod(expression, "getLeftHandOperand")) || isAggregation(invokeMethod(expression, "getRightHandOperand")));
		    }
		}

		@Override
		public  & Serializable, E extends BaseEntity> boolean isProxy(E entity) {
			return HIBERNATE_PROXY.get().isInstance(entity);
		}

		@Override
		public  & Serializable, E extends BaseEntity> boolean isProxyUninitialized(E entity) {
			return invokeOnProxy(entity, "isUninitialized", super::isProxyUninitialized);
		}

		@Override
		public  & Serializable, E extends BaseEntity> E dereferenceProxy(E entity) {
			return invokeOnProxy(entity, "getImplementation", super::dereferenceProxy);
		}

		@Override
		public  & Serializable, E extends BaseEntity> Class getEntityType(E entity) {
			return invokeOnProxy(entity, "getPersistentClass", super::getEntityType);
		}

		@Override
		public  & Serializable, E extends BaseEntity> I getIdentifier(E entity) {
			return invokeOnProxy(entity, "getIdentifier", super::getIdentifier);
		}

		@SuppressWarnings("unchecked")
		private  & Serializable, E extends BaseEntity> T invokeOnProxy(E entity, String methodName, Function fallback) {
			return isProxy(entity) ? (T) invokeMethod(invokeMethod(entity, "getHibernateLazyInitializer"), methodName) : fallback.apply(entity);
		}
	},

	ECLIPSELINK {

		@Override
		public String getDialectName(EntityManagerFactory entityManagerFactory) {
			var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory);
			return invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getDatabaseSession"), "getDatasourcePlatform").getClass().getSimpleName();
		}

		@Override
		public boolean isAggregation(Expression expression) {
			return ECLIPSELINK_FUNCTION_EXPRESSION_IMPL.get().isInstance(expression) && AGGREGATE_FUNCTIONS.contains(invokeMethod(expression, "getOperation"));
		}
	},

	OPENJPA {

		@Override
		public String getDialectName(EntityManagerFactory entityManagerFactory) {
			var unwrappedEntityManagerFactory = unwrapEntityManagerFactoryIfNecessary(entityManagerFactory);
			return invokeMethod(invokeMethod(unwrappedEntityManagerFactory, "getConfiguration"), "getDBDictionaryInstance").getClass().getSimpleName();
		}

		@Override
		public boolean isAggregation(Expression expression) {
			// We could also invoke toValue() on it and then isAggregate(), but that requires ExpressionFactory and CriteriaQueryImpl arguments which are not trivial to get here.
			return AGGREGATE_FUNCTIONS.contains(expression.getClass().getSimpleName().toUpperCase());
		}

		@Override
		public boolean isElementCollection(Attribute attribute) {
			// For some reason OpenJPA returns PersistentAttributeType.ONE_TO_MANY on an @ElementCollection.
			return ((Field) attribute.getJavaMember()).getAnnotation(ElementCollection.class) != null;
		}

		@Override
		public boolean isOneToMany(Attribute attribute) {
			// For some reason OpenJPA returns PersistentAttributeType.ONE_TO_MANY on an @ElementCollection.
			return !isElementCollection(attribute) && super.isOneToMany(attribute);
		}
	},

	UNKNOWN;

	public static final String QUERY_HINT_HIBERNATE_CACHEABLE = "org.hibernate.cacheable"; // true | false
	public static final String QUERY_HINT_HIBERNATE_CACHE_REGION = "org.hibernate.cacheRegion"; // 2nd level cache region ID
	public static final String QUERY_HINT_ECLIPSELINK_MAINTAIN_CACHE = "eclipselink.maintain-cache"; // true | false
	public static final String QUERY_HINT_ECLIPSELINK_REFRESH = "eclipselink.refresh"; // true | false

	private static final Optional> HIBERNATE_PROXY = findClass("org.hibernate.proxy.HibernateProxy");
	private static final Optional> HIBERNATE_SESSION_FACTORY = findClass("org.hibernate.SessionFactory");
	private static final Optional> HIBERNATE_3_5_0_BASIC_FUNCTION_EXPRESSION = findClass("org.hibernate.ejb.criteria.expression.function.BasicFunctionExpression");
	private static final Optional> HIBERNATE_4_3_0_BASIC_FUNCTION_EXPRESSION = findClass("org.hibernate.jpa.criteria.expression.function.BasicFunctionExpression");
	private static final Optional> HIBERNATE_5_2_0_BASIC_FUNCTION_EXPRESSION = findClass("org.hibernate.query.criteria.internal.expression.function.BasicFunctionExpression");
	private static final Optional> HIBERNATE_BASIC_FUNCTION_EXPRESSION = Stream.of(HIBERNATE_5_2_0_BASIC_FUNCTION_EXPRESSION, HIBERNATE_4_3_0_BASIC_FUNCTION_EXPRESSION, HIBERNATE_3_5_0_BASIC_FUNCTION_EXPRESSION).filter(Optional::isPresent).findFirst().orElse(Optional.empty());
    private static final Optional> HIBERNATE_6_0_0_AGGREGATE_FUNCTION = findClass("org.hibernate.query.sqm.function.SelfRenderingSqmAggregateFunction");
	private static final Optional> HIBERNATE_3_5_0_COMPARISON_PREDICATE = findClass("org.hibernate.ejb.criteria.predicate.ComparisonPredicate");
	private static final Optional> HIBERNATE_4_3_0_COMPARISON_PREDICATE = findClass("org.hibernate.jpa.criteria.predicate.ComparisonPredicate");
	private static final Optional> HIBERNATE_5_2_0_COMPARISON_PREDICATE = findClass("org.hibernate.query.criteria.internal.predicate.ComparisonPredicate");
	private static final Optional> HIBERNATE_COMPARISON_PREDICATE = Stream.of(HIBERNATE_5_2_0_COMPARISON_PREDICATE, HIBERNATE_4_3_0_COMPARISON_PREDICATE, HIBERNATE_3_5_0_COMPARISON_PREDICATE).filter(Optional::isPresent).findFirst().orElse(Optional.empty());
	private static final Optional> ECLIPSELINK_FUNCTION_EXPRESSION_IMPL = findClass("org.eclipse.persistence.internal.jpa.querydef.FunctionExpressionImpl");
	private static final Set AGGREGATE_FUNCTIONS = unmodifiableSet("MIN", "MAX", "SUM", "AVG", "COUNT");

	private static Object unwrapEntityManagerFactoryIfNecessary(EntityManagerFactory entityManagerFactory) {
		var packageName = entityManagerFactory.getClass().getPackage().getName();

		if (packageName.startsWith("org.apache.openejb.")) {
			var getDelegate = findMethod(entityManagerFactory, "getDelegate");
			return getDelegate.isPresent() ? invokeMethod(entityManagerFactory, getDelegate.get()) : entityManagerFactory;
		}

		return entityManagerFactory;
	}

	public static Provider of(EntityManager entityManager) {
		var packageName = entityManager.getDelegate().getClass().getPackage().getName();

		if (packageName.startsWith("org.hibernate.")) {
			return HIBERNATE;
		}
		else if (packageName.startsWith("org.eclipse.persistence.")) {
			return ECLIPSELINK;
		}
		else if (packageName.startsWith("org.apache.openjpa.")) {
			return OPENJPA;
		}
		else {
			return UNKNOWN;
		}
	}

	public static boolean is(Provider provider) {
		return BaseEntityService.getCurrentInstance().getProvider() == provider;
	}

	public String getDialectName(EntityManagerFactory entityManagerFactory) {
		throw new UnsupportedOperationException(String.valueOf(entityManagerFactory));
	}

	public boolean isAggregation(Expression expression) {
		throw new UnsupportedOperationException(String.valueOf(expression));
	}

	public boolean isElementCollection(Attribute attribute) {
		return attribute.getPersistentAttributeType() == ELEMENT_COLLECTION;
	}

	public boolean isOneToMany(Attribute attribute) {
		return attribute.getPersistentAttributeType() == ONE_TO_MANY;
	}

	public boolean isManyOrOneToOne(Attribute attribute) {
		return isOneOf(attribute.getPersistentAttributeType(), MANY_TO_ONE, ONE_TO_ONE);
	}

	public  & Serializable, E extends BaseEntity> boolean isProxy(E entity) {
		return false;
	}

	public  & Serializable, E extends BaseEntity> boolean isProxyUninitialized(E entity) {
		return false;
	}

	public  & Serializable, E extends BaseEntity> E dereferenceProxy(E entity) {
		return entity;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public  & Serializable, E extends BaseEntity> Class getEntityType(E entity) {
		if (entity == null) {
			return null;
		}

		Class entityType = entity.getClass();

		while (BaseEntity.class.isAssignableFrom(entityType) && entityType.getAnnotation(Entity.class) == null)
		{
			entityType = (Class) entityType.getSuperclass();
		}

		return (Class) entityType;
	}

	public  & Serializable, E extends BaseEntity> I getIdentifier(E entity) {
		return entity == null ? null : entity.getId();
	}

	public  & Serializable, E extends BaseEntity> String getTableName(E entity) {
		if (entity == null) {
			return null;
		}

		Class entityType = getEntityType(entity);
		var table = entityType.getAnnotation(Table.class);
		return table != null ? table.name() : entityType.getSimpleName().toUpperCase();
	}

}