/* * 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.spi; import com.blazebit.persistence.JoinType; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.ManagedType; /** * A JPA provider implementation provides information about which features are supported by a JPA implementation. * * @author Christian Beikov * @since 1.2.0 */ public interface JpaProvider { /** * Whether JPA 2.1 specification is supported. * * @return True if JPA 2.1 is supported, false otherwise */ public boolean supportsJpa21(); /** * Whether Entity Joins are supported. * * @return True if Entity Joins are supported, false otherwise */ public boolean supportsEntityJoin(); /** * Whether Insert statements are supported. * * @return True if Insert statements are supported, false otherwise */ public boolean supportsInsertStatement(); /** * Whether brackets are needed around a list parameter. * * @return True if brackets are needed, false otherwise */ public boolean needsBracketsForListParamter(); /** * Returns whether key restricted left joins should be rewritten to subquery joins. * This is part of the workaround for https://hibernate.atlassian.net/browse/HHH-9329 which makes key restricted left joins wrong. * * @return true if joins should be rewritten to subquery joins, false otherwise */ public boolean needsJoinSubqueryRewrite(); /** * The JPQL expression to use for the given literal boolean value. * This expression is used in contexts where an expression is assumed. * * @param value The boolean value * @return The JPQL expression to use to reflect the boolean value */ public String getBooleanExpression(boolean value); /** * The JPQL conditional expression to use for the given literal boolean value. * This expression is used in contexts where a predicate is assumed. * * @param value The boolean value * @return The JPQL conditional expression to use to reflect the boolean value */ public String getBooleanConditionalExpression(boolean value); /** * The JPQL expression to use for the NULL literal. * * @return The JPQL expression to use to reflect the NULL value */ public String getNullExpression(); /** * The name of the clause that has the same semantics as the ON clause which was introduced in JPA 2.1. * * @return The name of the ON clause */ public String getOnClause(); /** * Normally returns <code>VALUE</code>, but since Hibernate does weird things when using that, it returns <code>null</code>. * Returning null results in omitting <code>VALUE</code> in the final query that is passed to the JPA provider. * * @return The value function */ public String getCollectionValueFunction(); /** * Whether dereferencing a VALUE function expression is supported by the JPA provider. * * @return True if dereferencing is supported, false otherwise */ public boolean supportsCollectionValueDereference(); /** * The default result type of a scalar query. * * @return The default result type */ public Class<?> getDefaultQueryResultType(); /** * Returns the start of a JPQL representation of a function invocation for the given function name and argument count. * Normally this returns <code>FUNCTION('<i>functionName</i>',</code> but implementations may also allow to render the function name directly. * * @param functionName The function name * @param argumentCount The number of the arguments that the function is invoked with * @return The start of the JPQL represenation for the function invocation */ public String getCustomFunctionInvocation(String functionName, int argumentCount); /** * The given escape character for a LIKE predicate as string. * * @param character The escape character * @return The string representation */ public String escapeCharacter(char character); /** * Whether the query language supports the null precedence clause. * * @return True if the null precedence clause is supported, false otherwise */ public boolean supportsNullPrecedenceExpression(); /** * Renders the null precedence into the given string builder. * * @param sb The builder to which the null precedence should be appended to * @param expression The order by expression which might be a select alias * @param resolvedExpression The resolved expression for a possible select alias or the expression * @param order The order as string (<code>ASC</code> or <code>DESC</code>) * @param nulls The null precedence as string (<code>NULLS FIRST</code> or <code>NULLS LAST</code>) */ public void renderNullPrecedence(StringBuilder sb, String expression, String resolvedExpression, String order, String nulls); /** * Whether treating a from/root alias is supported. * For example <code>SELECT TREAT(alias AS Subtype).property FROM ..</code> * * @return True if treating a from alias is supported, false otherwise */ public boolean supportsRootTreat(); /** * Whether a treat join is supported. * For example <code>SELECT ... FROM .. JOIN TREAT(alias.relation AS Subtype)</code> * * @return True if a treat join is supported, false otherwise */ public boolean supportsTreatJoin(); /** * Whether a correlation path with a treat expression is supported. * For example <code>SELECT (SELECT .. FROM TREAT(parent AS Subtype).relation) FROM ..</code> * * @return True if a treat in correlation expressions is supported, false otherwise */ public boolean supportsTreatCorrelation(); /** * Whether a root treat in a join is supported. * For example <code>SELECT ... FROM .. JOIN TREAT(alias AS Subtype).relation</code> * * @return True if a root treat in a join is supported, false otherwise */ public boolean supportsRootTreatJoin(); /** * Whether a root treat in a treat join is supported. * For example <code>SELECT ... FROM .. JOIN TREAT(TREAT(alias AS Subtype).relation AS Subtype)</code> * * @return True if a root treat in a treat join is supported, false otherwise */ public boolean supportsRootTreatTreatJoin(); /** * Whether properties accessed of a from node are implicitly resolved to properties of a subtype of the from node. * * @return True if subtype property resolving is supported, false otherwise */ public boolean supportsSubtypePropertyResolving(); /** * Whether relations of a from node in joins are implicitly resolved to the relations of a subtype of the from node. * * @return True if subtype relation resolving is supported, false otherwise */ public boolean supportsSubtypeRelationResolving(); /** * Whether the <code>COUNT(*)</code> syntax is supported. * * @return True if <code>COUNT(*)</code> syntax is supported, false otherwise */ public boolean supportsCountStar(); /** * Whether the join columns for the given attribute are in a foreign table. * * @param ownerType The owner of the attribute * @param attributeName The attribute name to check * @return True if join columns are in a foreign table, false otherwise */ public boolean isForeignJoinColumn(ManagedType<?> ownerType, String attributeName); /** * Whether columns for the given attribute are shared between multiple subtypes * or shared by occupying the same slot in the resulting SQL. * * @param ownerType The owner of the attribute * @param attributeName The attribute name to check * @return True if columns of the attribute are shared, false otherwise */ public boolean isColumnShared(ManagedType<?> ownerType, String attributeName); /** * Whether the association defined by owner type and attribute name, when treat joined requires a type filter to be applied via an ON * clause to correctly filter subtypes. This is for JPA providers that don't correctly filter the types. * * Hibernate for example does not automatically add the type constraint to treat joins of a type that is uses * the table per class inheritance strategy. * * @param ownerType The type for which to check the treat filter requirement * @param attributeName The attribute name for which to check the treat filter requirement or null * @param joinType The join type used for the treat join * @return True if a treat filter is required, false otherwise */ public ConstraintType requiresTreatFilter(ManagedType<?> ownerType, String attributeName, JoinType joinType); /** * Whether the given attribute is a collection that uses a join table. * * @param attribute The attribute to check * @return True if uses a join table, false otherwise */ public boolean isJoinTable(Attribute<?, ?> attribute); /** * Whether the given attribute is a non-indexed and non-ordered collection a.k.a. a bag. * * @param attribute The attribute to check * @return True if it is a bag, false otherwise */ public boolean isBag(Attribute<?, ?> attribute); /** * Indicates if the provider supports expressions like * * doc.owner.id * * without generating an extra join. * * @return true if supported, else false */ public boolean supportsSingleValuedAssociationIdExpressions(); /** * Indicates if the provider supports the use of foreign associations in the ON clause. * This is the case when implicit joins in the ON clause are specially treated regarding SQL rendering. * If an expression like <code>alias.association.property</code> results in a subquery or table group join * when used in the ON clause, the JPA provider supports this feature. Normally this silently fails with invalid SQL. * By returning false, subqueries will be generated for such associations instead. * * The value is not yet used but will be in a future version. Also see: https://github.com/Blazebit/blaze-persistence/issues/402 * * @return true if supported, else false */ public boolean supportsForeignAssociationInOnClause(); /** * Indicates if the provider supports the use of transient entity objects as parameters. * * @return true if supported, else false */ public boolean supportsTransientEntityAsParameter(); /** * Indicates if the provider needs associations in the ON clause to use their id. * If needed, an expression like <code>alias.association</code> in the ON clause is rewritten to * <code>alias.association.id</code>. * * @return true if required, else false */ public boolean needsAssociationToIdRewriteInOnClause(); /** * Indicates if the provider needs associations in the ON clause to use their id. * If needed, an expression like <code>alias.association</code> in the ON clause is rewritten to * <code>alias.association.id</code> which relies on a <i>broken</i> type check in older Hibernate versions. * * @return true if required, else false */ public boolean needsBrokenAssociationToIdRewriteInOnClause(); /** * Indicates if the provider does <i>column sharing</i> for same named columns in inheritance mappings * and thus requires the use of a CASE WHEN expression for restricting casted accesses like e.g. <code>TREAT(alias AS Subtype).property</code> * to retain cast semantics. * * @return true if required, else false */ public boolean needsTypeConstraintForColumnSharing(); /** * The possible locations of a constraint. * * @author Christian Beikov * @since 1.2.0 */ public static enum ConstraintType { /** * No constraint. */ NONE, /** * Constraint in the ON clause. */ ON, /** * Constraint in the WHERE clause. */ WHERE; } }