You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2821 lines
96 KiB
2821 lines
96 KiB
/* |
|
* Hibernate, Relational Persistence for Idiomatic Java |
|
* |
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. |
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. |
|
*/ |
|
package com.fr.third.org.hibernate.dialect; |
|
|
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.sql.Blob; |
|
import java.sql.CallableStatement; |
|
import java.sql.Clob; |
|
import java.sql.DatabaseMetaData; |
|
import java.sql.NClob; |
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.sql.Types; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.Iterator; |
|
import java.util.List; |
|
import java.util.Locale; |
|
import java.util.Map; |
|
import java.util.Properties; |
|
import java.util.Set; |
|
import java.util.regex.Pattern; |
|
|
|
import com.fr.third.org.hibernate.HibernateException; |
|
import com.fr.third.org.hibernate.LockMode; |
|
import com.fr.third.org.hibernate.LockOptions; |
|
import com.fr.third.org.hibernate.MappingException; |
|
import com.fr.third.org.hibernate.NullPrecedence; |
|
import com.fr.third.org.hibernate.ScrollMode; |
|
import com.fr.third.org.hibernate.boot.model.TypeContributions; |
|
import com.fr.third.org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; |
|
import com.fr.third.org.hibernate.boot.model.relational.Sequence; |
|
import com.fr.third.org.hibernate.cfg.Environment; |
|
import com.fr.third.org.hibernate.dialect.function.CastFunction; |
|
import com.fr.third.org.hibernate.dialect.function.SQLFunction; |
|
import com.fr.third.org.hibernate.dialect.function.SQLFunctionTemplate; |
|
import com.fr.third.org.hibernate.dialect.function.StandardAnsiSqlAggregationFunctions; |
|
import com.fr.third.org.hibernate.dialect.function.StandardSQLFunction; |
|
import com.fr.third.org.hibernate.dialect.identity.IdentityColumnSupport; |
|
import com.fr.third.org.hibernate.dialect.identity.IdentityColumnSupportImpl; |
|
import com.fr.third.org.hibernate.dialect.lock.LockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.OptimisticLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.lock.SelectLockingStrategy; |
|
import com.fr.third.org.hibernate.dialect.pagination.LegacyLimitHandler; |
|
import com.fr.third.org.hibernate.dialect.pagination.LimitHandler; |
|
import com.fr.third.org.hibernate.dialect.unique.DefaultUniqueDelegate; |
|
import com.fr.third.org.hibernate.dialect.unique.UniqueDelegate; |
|
import com.fr.third.org.hibernate.engine.jdbc.LobCreator; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.spi.AnsiSqlKeywords; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.spi.IdentifierHelper; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; |
|
import com.fr.third.org.hibernate.engine.jdbc.env.spi.SchemaNameResolver; |
|
import com.fr.third.org.hibernate.engine.jdbc.spi.JdbcServices; |
|
import com.fr.third.org.hibernate.engine.spi.SessionImplementor; |
|
import com.fr.third.org.hibernate.exception.spi.ConversionContext; |
|
import com.fr.third.org.hibernate.exception.spi.SQLExceptionConversionDelegate; |
|
import com.fr.third.org.hibernate.exception.spi.SQLExceptionConverter; |
|
import com.fr.third.org.hibernate.exception.spi.ViolatedConstraintNameExtracter; |
|
import com.fr.third.org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; |
|
import com.fr.third.org.hibernate.hql.spi.id.persistent.PersistentTableBulkIdStrategy; |
|
import com.fr.third.org.hibernate.id.IdentityGenerator; |
|
import com.fr.third.org.hibernate.id.enhanced.SequenceStyleGenerator; |
|
import com.fr.third.org.hibernate.internal.CoreMessageLogger; |
|
import com.fr.third.org.hibernate.internal.util.ReflectHelper; |
|
import com.fr.third.org.hibernate.internal.util.StringHelper; |
|
import com.fr.third.org.hibernate.internal.util.collections.ArrayHelper; |
|
import com.fr.third.org.hibernate.internal.util.io.StreamCopier; |
|
import com.fr.third.org.hibernate.loader.BatchLoadSizingStrategy; |
|
import com.fr.third.org.hibernate.mapping.Column; |
|
import com.fr.third.org.hibernate.mapping.Constraint; |
|
import com.fr.third.org.hibernate.mapping.ForeignKey; |
|
import com.fr.third.org.hibernate.mapping.Index; |
|
import com.fr.third.org.hibernate.mapping.Table; |
|
import com.fr.third.org.hibernate.persister.entity.Lockable; |
|
import com.fr.third.org.hibernate.procedure.internal.StandardCallableStatementSupport; |
|
import com.fr.third.org.hibernate.procedure.spi.CallableStatementSupport; |
|
import com.fr.third.org.hibernate.service.ServiceRegistry; |
|
import com.fr.third.org.hibernate.sql.ANSICaseFragment; |
|
import com.fr.third.org.hibernate.sql.ANSIJoinFragment; |
|
import com.fr.third.org.hibernate.sql.CaseFragment; |
|
import com.fr.third.org.hibernate.sql.ForUpdateFragment; |
|
import com.fr.third.org.hibernate.sql.JoinFragment; |
|
import com.fr.third.org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; |
|
import com.fr.third.org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; |
|
import com.fr.third.org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardAuxiliaryDatabaseObjectExporter; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardForeignKeyExporter; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardIndexExporter; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardSequenceExporter; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardTableExporter; |
|
import com.fr.third.org.hibernate.tool.schema.internal.StandardUniqueKeyExporter; |
|
import com.fr.third.org.hibernate.tool.schema.spi.Exporter; |
|
import com.fr.third.org.hibernate.type.StandardBasicTypes; |
|
import com.fr.third.org.hibernate.type.descriptor.sql.ClobTypeDescriptor; |
|
import com.fr.third.org.hibernate.type.descriptor.sql.SqlTypeDescriptor; |
|
|
|
import com.fr.third.org.jboss.logging.Logger; |
|
|
|
/** |
|
* Represents a dialect of SQL implemented by a particular RDBMS. Subclasses implement Hibernate compatibility |
|
* with different systems. Subclasses should provide a public default constructor that register a set of type |
|
* mappings and default Hibernate properties. Subclasses should be immutable. |
|
* |
|
* @author Gavin King, David Channon |
|
*/ |
|
@SuppressWarnings("deprecation") |
|
public abstract class Dialect implements ConversionContext { |
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger( |
|
CoreMessageLogger.class, |
|
Dialect.class.getName() |
|
); |
|
|
|
/** |
|
* Defines a default batch size constant |
|
*/ |
|
public static final String DEFAULT_BATCH_SIZE = "15"; |
|
|
|
/** |
|
* Defines a "no batching" batch size constant |
|
*/ |
|
public static final String NO_BATCH = "0"; |
|
|
|
/** |
|
* Characters used as opening for quoting SQL identifiers |
|
*/ |
|
public static final String QUOTE = "`\"["; |
|
|
|
/** |
|
* Characters used as closing for quoting SQL identifiers |
|
*/ |
|
public static final String CLOSED_QUOTE = "`\"]"; |
|
|
|
private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile("\\*/"); |
|
private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile("/\\*"); |
|
|
|
private final TypeNames typeNames = new TypeNames(); |
|
private final TypeNames hibernateTypeNames = new TypeNames(); |
|
|
|
private final Properties properties = new Properties(); |
|
private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>(); |
|
private final Set<String> sqlKeywords = new HashSet<String>(); |
|
|
|
private final UniqueDelegate uniqueDelegate; |
|
|
|
|
|
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
protected Dialect() { |
|
LOG.usingDialect( this ); |
|
StandardAnsiSqlAggregationFunctions.primeFunctionMap( sqlFunctions ); |
|
|
|
// standard sql92 functions (can be overridden by subclasses) |
|
registerFunction( "substring", new SQLFunctionTemplate( StandardBasicTypes.STRING, "substring(?1, ?2, ?3)" ) ); |
|
registerFunction( "locate", new SQLFunctionTemplate( StandardBasicTypes.INTEGER, "locate(?1, ?2, ?3)" ) ); |
|
registerFunction( "trim", new SQLFunctionTemplate( StandardBasicTypes.STRING, "trim(?1 ?2 ?3 ?4)" ) ); |
|
registerFunction( "length", new StandardSQLFunction( "length", StandardBasicTypes.INTEGER ) ); |
|
registerFunction( "bit_length", new StandardSQLFunction( "bit_length", StandardBasicTypes.INTEGER ) ); |
|
registerFunction( "coalesce", new StandardSQLFunction( "coalesce" ) ); |
|
registerFunction( "nullif", new StandardSQLFunction( "nullif" ) ); |
|
registerFunction( "abs", new StandardSQLFunction( "abs" ) ); |
|
registerFunction( "mod", new StandardSQLFunction( "mod", StandardBasicTypes.INTEGER) ); |
|
registerFunction( "sqrt", new StandardSQLFunction( "sqrt", StandardBasicTypes.DOUBLE) ); |
|
registerFunction( "upper", new StandardSQLFunction("upper") ); |
|
registerFunction( "lower", new StandardSQLFunction("lower") ); |
|
registerFunction( "cast", new CastFunction() ); |
|
registerFunction( "extract", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(?1 ?2 ?3)") ); |
|
|
|
//map second/minute/hour/day/month/year to ANSI extract(), override on subclasses |
|
registerFunction( "second", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(second from ?1)") ); |
|
registerFunction( "minute", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(minute from ?1)") ); |
|
registerFunction( "hour", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(hour from ?1)") ); |
|
registerFunction( "day", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(day from ?1)") ); |
|
registerFunction( "month", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(month from ?1)") ); |
|
registerFunction( "year", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(year from ?1)") ); |
|
|
|
registerFunction( "str", new SQLFunctionTemplate(StandardBasicTypes.STRING, "cast(?1 as char)") ); |
|
|
|
registerColumnType( Types.BIT, "bit" ); |
|
registerColumnType( Types.BOOLEAN, "boolean" ); |
|
registerColumnType( Types.TINYINT, "tinyint" ); |
|
registerColumnType( Types.SMALLINT, "smallint" ); |
|
registerColumnType( Types.INTEGER, "integer" ); |
|
registerColumnType( Types.BIGINT, "bigint" ); |
|
registerColumnType( Types.FLOAT, "float($p)" ); |
|
registerColumnType( Types.DOUBLE, "double precision" ); |
|
registerColumnType( Types.NUMERIC, "numeric($p,$s)" ); |
|
registerColumnType( Types.REAL, "real" ); |
|
|
|
registerColumnType( Types.DATE, "date" ); |
|
registerColumnType( Types.TIME, "time" ); |
|
registerColumnType( Types.TIMESTAMP, "timestamp" ); |
|
|
|
registerColumnType( Types.VARBINARY, "bit varying($l)" ); |
|
registerColumnType( Types.LONGVARBINARY, "bit varying($l)" ); |
|
registerColumnType( Types.BLOB, "blob" ); |
|
|
|
registerColumnType( Types.CHAR, "char($l)" ); |
|
registerColumnType( Types.VARCHAR, "varchar($l)" ); |
|
registerColumnType( Types.LONGVARCHAR, "varchar($l)" ); |
|
registerColumnType( Types.CLOB, "clob" ); |
|
|
|
registerColumnType( Types.NCHAR, "nchar($l)" ); |
|
registerColumnType( Types.NVARCHAR, "nvarchar($l)" ); |
|
registerColumnType( Types.LONGNVARCHAR, "nvarchar($l)" ); |
|
registerColumnType( Types.NCLOB, "nclob" ); |
|
|
|
// register hibernate types for default use in scalar sqlquery type auto detection |
|
registerHibernateType( Types.BIGINT, StandardBasicTypes.BIG_INTEGER.getName() ); |
|
registerHibernateType( Types.BINARY, StandardBasicTypes.BINARY.getName() ); |
|
registerHibernateType( Types.BIT, StandardBasicTypes.BOOLEAN.getName() ); |
|
registerHibernateType( Types.BOOLEAN, StandardBasicTypes.BOOLEAN.getName() ); |
|
registerHibernateType( Types.CHAR, StandardBasicTypes.CHARACTER.getName() ); |
|
registerHibernateType( Types.CHAR, 1, StandardBasicTypes.CHARACTER.getName() ); |
|
registerHibernateType( Types.CHAR, 255, StandardBasicTypes.STRING.getName() ); |
|
registerHibernateType( Types.DATE, StandardBasicTypes.DATE.getName() ); |
|
registerHibernateType( Types.DOUBLE, StandardBasicTypes.DOUBLE.getName() ); |
|
registerHibernateType( Types.FLOAT, StandardBasicTypes.FLOAT.getName() ); |
|
registerHibernateType( Types.INTEGER, StandardBasicTypes.INTEGER.getName() ); |
|
registerHibernateType( Types.SMALLINT, StandardBasicTypes.SHORT.getName() ); |
|
registerHibernateType( Types.TINYINT, StandardBasicTypes.BYTE.getName() ); |
|
registerHibernateType( Types.TIME, StandardBasicTypes.TIME.getName() ); |
|
registerHibernateType( Types.TIMESTAMP, StandardBasicTypes.TIMESTAMP.getName() ); |
|
registerHibernateType( Types.VARCHAR, StandardBasicTypes.STRING.getName() ); |
|
registerHibernateType( Types.NVARCHAR, StandardBasicTypes.NSTRING.getName() ); |
|
registerHibernateType( Types.VARBINARY, StandardBasicTypes.BINARY.getName() ); |
|
registerHibernateType( Types.LONGVARCHAR, StandardBasicTypes.TEXT.getName() ); |
|
registerHibernateType( Types.LONGVARBINARY, StandardBasicTypes.IMAGE.getName() ); |
|
registerHibernateType( Types.NUMERIC, StandardBasicTypes.BIG_DECIMAL.getName() ); |
|
registerHibernateType( Types.DECIMAL, StandardBasicTypes.BIG_DECIMAL.getName() ); |
|
registerHibernateType( Types.BLOB, StandardBasicTypes.BLOB.getName() ); |
|
registerHibernateType( Types.CLOB, StandardBasicTypes.CLOB.getName() ); |
|
registerHibernateType( Types.REAL, StandardBasicTypes.FLOAT.getName() ); |
|
|
|
uniqueDelegate = new DefaultUniqueDelegate( this ); |
|
} |
|
|
|
/** |
|
* Get an instance of the dialect specified by the current <tt>System</tt> properties. |
|
* |
|
* @return The specified Dialect |
|
* @throws HibernateException If no dialect was specified, or if it could not be instantiated. |
|
*/ |
|
public static Dialect getDialect() throws HibernateException { |
|
return instantiateDialect( Environment.getProperties().getProperty( Environment.DIALECT ) ); |
|
} |
|
|
|
|
|
/** |
|
* Get an instance of the dialect specified by the given properties or by |
|
* the current <tt>System</tt> properties. |
|
* |
|
* @param props The properties to use for finding the dialect class to use. |
|
* @return The specified Dialect |
|
* @throws HibernateException If no dialect was specified, or if it could not be instantiated. |
|
*/ |
|
public static Dialect getDialect(Properties props) throws HibernateException { |
|
final String dialectName = props.getProperty( Environment.DIALECT ); |
|
if ( dialectName == null ) { |
|
return getDialect(); |
|
} |
|
return instantiateDialect( dialectName ); |
|
} |
|
|
|
private static Dialect instantiateDialect(String dialectName) throws HibernateException { |
|
if ( dialectName == null ) { |
|
throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." ); |
|
} |
|
try { |
|
return (Dialect) ReflectHelper.classForName( dialectName ).newInstance(); |
|
} |
|
catch ( ClassNotFoundException cnfe ) { |
|
throw new HibernateException( "Dialect class not found: " + dialectName ); |
|
} |
|
catch ( Exception e ) { |
|
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e ); |
|
} |
|
} |
|
|
|
/** |
|
* Retrieve a set of default Hibernate properties for this database. |
|
* |
|
* @return a set of Hibernate properties |
|
*/ |
|
public final Properties getDefaultProperties() { |
|
return properties; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return getClass().getName(); |
|
} |
|
|
|
|
|
// database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Allows the Dialect to contribute additional types |
|
* |
|
* @param typeContributions Callback to contribute the types |
|
* @param serviceRegistry The service registry |
|
*/ |
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { |
|
// by default, nothing to do |
|
} |
|
|
|
/** |
|
* Get the name of the database type associated with the given |
|
* {@link java.sql.Types} typecode. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @return the database type name |
|
* @throws HibernateException If no mapping was specified for that type. |
|
*/ |
|
public String getTypeName(int code) throws HibernateException { |
|
final String result = typeNames.get( code ); |
|
if ( result == null ) { |
|
throw new HibernateException( "No default type mapping for (java.sql.Types) " + code ); |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Get the name of the database type associated with the given |
|
* {@link java.sql.Types} typecode with the given storage specification |
|
* parameters. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param length The datatype length |
|
* @param precision The datatype precision |
|
* @param scale The datatype scale |
|
* @return the database type name |
|
* @throws HibernateException If no mapping was specified for that type. |
|
*/ |
|
public String getTypeName(int code, long length, int precision, int scale) throws HibernateException { |
|
final String result = typeNames.get( code, length, precision, scale ); |
|
if ( result == null ) { |
|
throw new HibernateException( |
|
String.format( "No type mapping for java.sql.Types code: %s, length: %s", code, length ) |
|
); |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Get the name of the database type appropriate for casting operations |
|
* (via the CAST() SQL function) for the given {@link java.sql.Types} typecode. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @return The database type name |
|
*/ |
|
public String getCastTypeName(int code) { |
|
return getTypeName( code, Column.DEFAULT_LENGTH, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE ); |
|
} |
|
|
|
/** |
|
* Return an expression casting the value to the specified type |
|
* |
|
* @param value The value to cast |
|
* @param jdbcTypeCode The JDBC type code to cast to |
|
* @param length The type length |
|
* @param precision The type precision |
|
* @param scale The type scale |
|
* |
|
* @return The cast expression |
|
*/ |
|
public String cast(String value, int jdbcTypeCode, int length, int precision, int scale) { |
|
if ( jdbcTypeCode == Types.CHAR ) { |
|
return "cast(" + value + " as char(" + length + "))"; |
|
} |
|
else { |
|
return "cast(" + value + "as " + getTypeName( jdbcTypeCode, length, precision, scale ) + ")"; |
|
} |
|
} |
|
|
|
/** |
|
* Return an expression casting the value to the specified type. Simply calls |
|
* {@link #cast(String, int, int, int, int)} passing {@link Column#DEFAULT_PRECISION} and |
|
* {@link Column#DEFAULT_SCALE} as the precision/scale. |
|
* |
|
* @param value The value to cast |
|
* @param jdbcTypeCode The JDBC type code to cast to |
|
* @param length The type length |
|
* |
|
* @return The cast expression |
|
*/ |
|
public String cast(String value, int jdbcTypeCode, int length) { |
|
return cast( value, jdbcTypeCode, length, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE ); |
|
} |
|
|
|
/** |
|
* Return an expression casting the value to the specified type. Simply calls |
|
* {@link #cast(String, int, int, int, int)} passing {@link Column#DEFAULT_LENGTH} as the length |
|
* |
|
* @param value The value to cast |
|
* @param jdbcTypeCode The JDBC type code to cast to |
|
* @param precision The type precision |
|
* @param scale The type scale |
|
* |
|
* @return The cast expression |
|
*/ |
|
public String cast(String value, int jdbcTypeCode, int precision, int scale) { |
|
return cast( value, jdbcTypeCode, Column.DEFAULT_LENGTH, precision, scale ); |
|
} |
|
|
|
/** |
|
* Subclasses register a type name for the given type code and maximum |
|
* column length. <tt>$l</tt> in the type name with be replaced by the |
|
* column length (if appropriate). |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param capacity The maximum length of database type |
|
* @param name The database type name |
|
*/ |
|
protected void registerColumnType(int code, long capacity, String name) { |
|
typeNames.put( code, capacity, name ); |
|
} |
|
|
|
/** |
|
* Subclasses register a type name for the given type code. <tt>$l</tt> in |
|
* the type name with be replaced by the column length (if appropriate). |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param name The database type name |
|
*/ |
|
protected void registerColumnType(int code, String name) { |
|
typeNames.put( code, name ); |
|
} |
|
|
|
/** |
|
* Allows the dialect to override a {@link SqlTypeDescriptor}. |
|
* <p/> |
|
* If the passed {@code sqlTypeDescriptor} allows itself to be remapped (per |
|
* {@link com.fr.third.org.hibernate.type.descriptor.sql.SqlTypeDescriptor#canBeRemapped()}), then this method uses |
|
* {@link #getSqlTypeDescriptorOverride} to get an optional override based on the SQL code returned by |
|
* {@link SqlTypeDescriptor#getSqlType()}. |
|
* <p/> |
|
* If this dialect does not provide an override or if the {@code sqlTypeDescriptor} doe not allow itself to be |
|
* remapped, then this method simply returns the original passed {@code sqlTypeDescriptor} |
|
* |
|
* @param sqlTypeDescriptor The {@link SqlTypeDescriptor} to override |
|
* @return The {@link SqlTypeDescriptor} that should be used for this dialect; |
|
* if there is no override, then original {@code sqlTypeDescriptor} is returned. |
|
* @throws IllegalArgumentException if {@code sqlTypeDescriptor} is null. |
|
* |
|
* @see #getSqlTypeDescriptorOverride |
|
*/ |
|
public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { |
|
if ( sqlTypeDescriptor == null ) { |
|
throw new IllegalArgumentException( "sqlTypeDescriptor is null" ); |
|
} |
|
if ( ! sqlTypeDescriptor.canBeRemapped() ) { |
|
return sqlTypeDescriptor; |
|
} |
|
|
|
final SqlTypeDescriptor overridden = getSqlTypeDescriptorOverride( sqlTypeDescriptor.getSqlType() ); |
|
return overridden == null ? sqlTypeDescriptor : overridden; |
|
} |
|
|
|
/** |
|
* Returns the {@link SqlTypeDescriptor} that should be used to handle the given JDBC type code. Returns |
|
* {@code null} if there is no override. |
|
* |
|
* @param sqlCode A {@link Types} constant indicating the SQL column type |
|
* @return The {@link SqlTypeDescriptor} to use as an override, or {@code null} if there is no override. |
|
*/ |
|
protected SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) { |
|
SqlTypeDescriptor descriptor; |
|
switch ( sqlCode ) { |
|
case Types.CLOB: { |
|
descriptor = useInputStreamToInsertBlob() ? ClobTypeDescriptor.STREAM_BINDING : null; |
|
break; |
|
} |
|
default: { |
|
descriptor = null; |
|
break; |
|
} |
|
} |
|
return descriptor; |
|
} |
|
|
|
/** |
|
* The legacy behavior of Hibernate. LOBs are not processed by merge |
|
*/ |
|
@SuppressWarnings( {"UnusedDeclaration"}) |
|
protected static final LobMergeStrategy LEGACY_LOB_MERGE_STRATEGY = new LobMergeStrategy() { |
|
@Override |
|
public Blob mergeBlob(Blob original, Blob target, SessionImplementor session) { |
|
return target; |
|
} |
|
|
|
@Override |
|
public Clob mergeClob(Clob original, Clob target, SessionImplementor session) { |
|
return target; |
|
} |
|
|
|
@Override |
|
public NClob mergeNClob(NClob original, NClob target, SessionImplementor session) { |
|
return target; |
|
} |
|
}; |
|
|
|
/** |
|
* Merge strategy based on transferring contents based on streams. |
|
*/ |
|
@SuppressWarnings( {"UnusedDeclaration"}) |
|
protected static final LobMergeStrategy STREAM_XFER_LOB_MERGE_STRATEGY = new LobMergeStrategy() { |
|
@Override |
|
public Blob mergeBlob(Blob original, Blob target, SessionImplementor session) { |
|
if ( original != target ) { |
|
try { |
|
// the BLOB just read during the load phase of merge |
|
final OutputStream connectedStream = target.setBinaryStream( 1L ); |
|
// the BLOB from the detached state |
|
final InputStream detachedStream = original.getBinaryStream(); |
|
StreamCopier.copy( detachedStream, connectedStream ); |
|
return target; |
|
} |
|
catch (SQLException e ) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge BLOB data" ); |
|
} |
|
} |
|
else { |
|
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeBlob( original, target, session ); |
|
} |
|
} |
|
|
|
@Override |
|
public Clob mergeClob(Clob original, Clob target, SessionImplementor session) { |
|
if ( original != target ) { |
|
try { |
|
// the CLOB just read during the load phase of merge |
|
final OutputStream connectedStream = target.setAsciiStream( 1L ); |
|
// the CLOB from the detached state |
|
final InputStream detachedStream = original.getAsciiStream(); |
|
StreamCopier.copy( detachedStream, connectedStream ); |
|
return target; |
|
} |
|
catch (SQLException e ) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge CLOB data" ); |
|
} |
|
} |
|
else { |
|
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeClob( original, target, session ); |
|
} |
|
} |
|
|
|
@Override |
|
public NClob mergeNClob(NClob original, NClob target, SessionImplementor session) { |
|
if ( original != target ) { |
|
try { |
|
// the NCLOB just read during the load phase of merge |
|
final OutputStream connectedStream = target.setAsciiStream( 1L ); |
|
// the NCLOB from the detached state |
|
final InputStream detachedStream = original.getAsciiStream(); |
|
StreamCopier.copy( detachedStream, connectedStream ); |
|
return target; |
|
} |
|
catch (SQLException e ) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge NCLOB data" ); |
|
} |
|
} |
|
else { |
|
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeNClob( original, target, session ); |
|
} |
|
} |
|
}; |
|
|
|
/** |
|
* Merge strategy based on creating a new LOB locator. |
|
*/ |
|
protected static final LobMergeStrategy NEW_LOCATOR_LOB_MERGE_STRATEGY = new LobMergeStrategy() { |
|
@Override |
|
public Blob mergeBlob(Blob original, Blob target, SessionImplementor session) { |
|
if ( original == null && target == null ) { |
|
return null; |
|
} |
|
try { |
|
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( |
|
session |
|
); |
|
return original == null |
|
? lobCreator.createBlob( ArrayHelper.EMPTY_BYTE_ARRAY ) |
|
: lobCreator.createBlob( original.getBinaryStream(), original.length() ); |
|
} |
|
catch (SQLException e) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge BLOB data" ); |
|
} |
|
} |
|
|
|
@Override |
|
public Clob mergeClob(Clob original, Clob target, SessionImplementor session) { |
|
if ( original == null && target == null ) { |
|
return null; |
|
} |
|
try { |
|
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session ); |
|
return original == null |
|
? lobCreator.createClob( "" ) |
|
: lobCreator.createClob( original.getCharacterStream(), original.length() ); |
|
} |
|
catch (SQLException e) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge CLOB data" ); |
|
} |
|
} |
|
|
|
@Override |
|
public NClob mergeNClob(NClob original, NClob target, SessionImplementor session) { |
|
if ( original == null && target == null ) { |
|
return null; |
|
} |
|
try { |
|
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session ); |
|
return original == null |
|
? lobCreator.createNClob( "" ) |
|
: lobCreator.createNClob( original.getCharacterStream(), original.length() ); |
|
} |
|
catch (SQLException e) { |
|
throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge NCLOB data" ); |
|
} |
|
} |
|
}; |
|
|
|
public LobMergeStrategy getLobMergeStrategy() { |
|
return NEW_LOCATOR_LOB_MERGE_STRATEGY; |
|
} |
|
|
|
|
|
// hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Get the name of the Hibernate {@link com.fr.third.org.hibernate.type.Type} associated with the given |
|
* {@link java.sql.Types} type code. |
|
* |
|
* @param code The {@link java.sql.Types} type code |
|
* @return The Hibernate {@link com.fr.third.org.hibernate.type.Type} name. |
|
* @throws HibernateException If no mapping was specified for that type. |
|
*/ |
|
@SuppressWarnings( {"UnusedDeclaration"}) |
|
public String getHibernateTypeName(int code) throws HibernateException { |
|
final String result = hibernateTypeNames.get( code ); |
|
if ( result == null ) { |
|
throw new HibernateException( "No Hibernate type mapping for java.sql.Types code: " + code ); |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Get the name of the Hibernate {@link com.fr.third.org.hibernate.type.Type} associated |
|
* with the given {@link java.sql.Types} typecode with the given storage |
|
* specification parameters. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param length The datatype length |
|
* @param precision The datatype precision |
|
* @param scale The datatype scale |
|
* @return The Hibernate {@link com.fr.third.org.hibernate.type.Type} name. |
|
* @throws HibernateException If no mapping was specified for that type. |
|
*/ |
|
public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException { |
|
final String result = hibernateTypeNames.get( code, length, precision, scale ); |
|
if ( result == null ) { |
|
throw new HibernateException( |
|
String.format( |
|
"No Hibernate type mapping for type [code=%s, length=%s]", |
|
code, |
|
length |
|
) |
|
); |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Registers a Hibernate {@link com.fr.third.org.hibernate.type.Type} name for the given |
|
* {@link java.sql.Types} type code and maximum column length. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param capacity The maximum length of database type |
|
* @param name The Hibernate {@link com.fr.third.org.hibernate.type.Type} name |
|
*/ |
|
protected void registerHibernateType(int code, long capacity, String name) { |
|
hibernateTypeNames.put( code, capacity, name); |
|
} |
|
|
|
/** |
|
* Registers a Hibernate {@link com.fr.third.org.hibernate.type.Type} name for the given |
|
* {@link java.sql.Types} type code. |
|
* |
|
* @param code The {@link java.sql.Types} typecode |
|
* @param name The Hibernate {@link com.fr.third.org.hibernate.type.Type} name |
|
*/ |
|
protected void registerHibernateType(int code, String name) { |
|
hibernateTypeNames.put( code, name); |
|
} |
|
|
|
|
|
// function support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
protected void registerFunction(String name, SQLFunction function) { |
|
// HHH-7721: SQLFunctionRegistry expects all lowercase. Enforce, |
|
// just in case a user's customer dialect uses mixed cases. |
|
sqlFunctions.put( name.toLowerCase( Locale.ROOT ), function ); |
|
} |
|
|
|
/** |
|
* Retrieves a map of the dialect's registered functions |
|
* (functionName => {@link com.fr.third.org.hibernate.dialect.function.SQLFunction}). |
|
* |
|
* @return The map of registered functions. |
|
*/ |
|
public final Map<String, SQLFunction> getFunctions() { |
|
return sqlFunctions; |
|
} |
|
|
|
|
|
// native identifier generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* The class (which implements {@link com.fr.third.org.hibernate.id.IdentifierGenerator}) |
|
* which acts as this dialects native generation strategy. |
|
* <p/> |
|
* Comes into play whenever the user specifies the native generator. |
|
* |
|
* @return The native generator class. |
|
* @deprecated use {@link #getNativeIdentifierGeneratorStrategy()} instead |
|
*/ |
|
@Deprecated |
|
public Class getNativeIdentifierGeneratorClass() { |
|
if ( getIdentityColumnSupport().supportsIdentityColumns() ) { |
|
return IdentityGenerator.class; |
|
} |
|
else { |
|
return SequenceStyleGenerator.class; |
|
} |
|
} |
|
|
|
/** |
|
* Resolves the native generation strategy associated to this dialect. |
|
* <p/> |
|
* Comes into play whenever the user specifies the native generator. |
|
* |
|
* @return The native generator strategy. |
|
*/ |
|
public String getNativeIdentifierGeneratorStrategy() { |
|
if ( getIdentityColumnSupport().supportsIdentityColumns() ) { |
|
return "identity"; |
|
} |
|
else { |
|
return "sequence"; |
|
} |
|
} |
|
|
|
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Get the appropriate {@link IdentityColumnSupport} |
|
* |
|
* @return the IdentityColumnSupport |
|
* @since 5.1 |
|
*/ |
|
public IdentityColumnSupport getIdentityColumnSupport(){ |
|
return new IdentityColumnSupportImpl(); |
|
} |
|
|
|
// SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Does this dialect support sequences? |
|
* |
|
* @return True if sequences supported; false otherwise. |
|
*/ |
|
public boolean supportsSequences() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect support "pooled" sequences. Not aware of a better |
|
* name for this. Essentially can we specify the initial and increment values? |
|
* |
|
* @return True if such "pooled" sequences are supported; false otherwise. |
|
* @see #getCreateSequenceStrings(String, int, int) |
|
* @see #getCreateSequenceString(String, int, int) |
|
*/ |
|
public boolean supportsPooledSequences() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Generate the appropriate select statement to to retrieve the next value |
|
* of a sequence. |
|
* <p/> |
|
* This should be a "stand alone" select statement. |
|
* |
|
* @param sequenceName the name of the sequence |
|
* @return String The "nextval" select string. |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
public String getSequenceNextValString(String sequenceName) throws MappingException { |
|
throw new MappingException( getClass().getName() + " does not support sequences" ); |
|
} |
|
|
|
/** |
|
* Generate the select expression fragment that will retrieve the next |
|
* value of a sequence as part of another (typically DML) statement. |
|
* <p/> |
|
* This differs from {@link #getSequenceNextValString(String)} in that this |
|
* should return an expression usable within another statement. |
|
* |
|
* @param sequenceName the name of the sequence |
|
* @return The "nextval" fragment. |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
public String getSelectSequenceNextValString(String sequenceName) throws MappingException { |
|
throw new MappingException( getClass().getName() + " does not support sequences" ); |
|
} |
|
|
|
/** |
|
* The multiline script used to create a sequence. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @return The sequence creation commands |
|
* @throws MappingException If sequences are not supported. |
|
* @deprecated Use {@link #getCreateSequenceString(String, int, int)} instead |
|
*/ |
|
@Deprecated |
|
public String[] getCreateSequenceStrings(String sequenceName) throws MappingException { |
|
return new String[] { getCreateSequenceString( sequenceName ) }; |
|
} |
|
|
|
/** |
|
* An optional multi-line form for databases which {@link #supportsPooledSequences()}. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @param initialValue The initial value to apply to 'create sequence' statement |
|
* @param incrementSize The increment value to apply to 'create sequence' statement |
|
* @return The sequence creation commands |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
public String[] getCreateSequenceStrings(String sequenceName, int initialValue, int incrementSize) throws MappingException { |
|
return new String[] { getCreateSequenceString( sequenceName, initialValue, incrementSize ) }; |
|
} |
|
|
|
/** |
|
* Typically dialects which support sequences can create a sequence |
|
* with a single command. This is convenience form of |
|
* {@link #getCreateSequenceStrings} to help facilitate that. |
|
* <p/> |
|
* Dialects which support sequences and can create a sequence in a |
|
* single command need *only* override this method. Dialects |
|
* which support sequences but require multiple commands to create |
|
* a sequence should instead override {@link #getCreateSequenceStrings}. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @return The sequence creation command |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
protected String getCreateSequenceString(String sequenceName) throws MappingException { |
|
throw new MappingException( getClass().getName() + " does not support sequences" ); |
|
} |
|
|
|
/** |
|
* Overloaded form of {@link #getCreateSequenceString(String)}, additionally |
|
* taking the initial value and increment size to be applied to the sequence |
|
* definition. |
|
* </p> |
|
* The default definition is to suffix {@link #getCreateSequenceString(String)} |
|
* with the string: " start with {initialValue} increment by {incrementSize}" where |
|
* {initialValue} and {incrementSize} are replacement placeholders. Generally |
|
* dialects should only need to override this method if different key phrases |
|
* are used to apply the allocation information. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @param initialValue The initial value to apply to 'create sequence' statement |
|
* @param incrementSize The increment value to apply to 'create sequence' statement |
|
* @return The sequence creation command |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
protected String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize) throws MappingException { |
|
if ( supportsPooledSequences() ) { |
|
return getCreateSequenceString( sequenceName ) + " start with " + initialValue + " increment by " + incrementSize; |
|
} |
|
throw new MappingException( getClass().getName() + " does not support pooled sequences" ); |
|
} |
|
|
|
/** |
|
* The multiline script used to drop a sequence. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @return The sequence drop commands |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
public String[] getDropSequenceStrings(String sequenceName) throws MappingException { |
|
return new String[]{getDropSequenceString( sequenceName )}; |
|
} |
|
|
|
/** |
|
* Typically dialects which support sequences can drop a sequence |
|
* with a single command. This is convenience form of |
|
* {@link #getDropSequenceStrings} to help facilitate that. |
|
* <p/> |
|
* Dialects which support sequences and can drop a sequence in a |
|
* single command need *only* override this method. Dialects |
|
* which support sequences but require multiple commands to drop |
|
* a sequence should instead override {@link #getDropSequenceStrings}. |
|
* |
|
* @param sequenceName The name of the sequence |
|
* @return The sequence drop commands |
|
* @throws MappingException If sequences are not supported. |
|
*/ |
|
protected String getDropSequenceString(String sequenceName) throws MappingException { |
|
throw new MappingException( getClass().getName() + " does not support sequences" ); |
|
} |
|
|
|
/** |
|
* Get the select command used retrieve the names of all sequences. |
|
* |
|
* @return The select command; or null if sequences are not supported. |
|
* @see com.fr.third.org.hibernate.tool.hbm2ddl.SchemaUpdate |
|
*/ |
|
public String getQuerySequencesString() { |
|
return null; |
|
} |
|
|
|
public SequenceInformationExtractor getSequenceInformationExtractor() { |
|
if ( getQuerySequencesString() == null ) { |
|
return SequenceInformationExtractorNoOpImpl.INSTANCE; |
|
} |
|
else { |
|
return SequenceInformationExtractorLegacyImpl.INSTANCE; |
|
} |
|
} |
|
|
|
|
|
// GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Get the command used to select a GUID from the underlying database. |
|
* <p/> |
|
* Optional operation. |
|
* |
|
* @return The appropriate command. |
|
*/ |
|
public String getSelectGUIDString() { |
|
throw new UnsupportedOperationException( getClass().getName() + " does not support GUIDs" ); |
|
} |
|
|
|
|
|
// limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Returns the delegate managing LIMIT clause. |
|
* |
|
* @return LIMIT clause delegate. |
|
*/ |
|
public LimitHandler getLimitHandler() { |
|
return new LegacyLimitHandler( this ); |
|
} |
|
|
|
/** |
|
* Does this dialect support some form of limiting query results |
|
* via a SQL clause? |
|
* |
|
* @return True if this dialect supports some form of LIMIT. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsLimit() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect's LIMIT support (if any) additionally |
|
* support specifying an offset? |
|
* |
|
* @return True if the dialect supports an offset within the limit support. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsLimitOffset() { |
|
return supportsLimit(); |
|
} |
|
|
|
/** |
|
* Does this dialect support bind variables (i.e., prepared statement |
|
* parameters) for its limit/offset? |
|
* |
|
* @return True if bind variables can be used; false otherwise. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsVariableLimit() { |
|
return supportsLimit(); |
|
} |
|
|
|
/** |
|
* ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit. |
|
* Does this dialect require us to bind the parameters in reverse order? |
|
* |
|
* @return true if the correct order is limit, offset |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean bindLimitParametersInReverseOrder() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does the <tt>LIMIT</tt> clause come at the start of the |
|
* <tt>SELECT</tt> statement, rather than at the end? |
|
* |
|
* @return true if limit parameters should come before other parameters |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean bindLimitParametersFirst() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does the <tt>LIMIT</tt> clause take a "maximum" row number instead |
|
* of a total number of returned rows? |
|
* <p/> |
|
* This is easiest understood via an example. Consider you have a table |
|
* with 20 rows, but you only want to retrieve rows number 11 through 20. |
|
* Generally, a limit with offset would say that the offset = 11 and the |
|
* limit = 10 (we only want 10 rows at a time); this is specifying the |
|
* total number of returned rows. Some dialects require that we instead |
|
* specify offset = 11 and limit = 20, where 20 is the "last" row we want |
|
* relative to offset (i.e. total number of rows = 20 - 11 = 9) |
|
* <p/> |
|
* So essentially, is limit relative from offset? Or is limit absolute? |
|
* |
|
* @return True if limit is relative from offset; false otherwise. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean useMaxForLimit() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Generally, if there is no limit applied to a Hibernate query we do not apply any limits |
|
* to the SQL query. This option forces that the limit be written to the SQL query. |
|
* |
|
* @return True to force limit into SQL query even if none specified in Hibernate query; false otherwise. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean forceLimitUsage() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Given a limit and an offset, apply the limit clause to the query. |
|
* |
|
* @param query The query to which to apply the limit. |
|
* @param offset The offset of the limit |
|
* @param limit The limit of the limit ;) |
|
* @return The modified query statement with the limit applied. |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public String getLimitString(String query, int offset, int limit) { |
|
return getLimitString( query, ( offset > 0 || forceLimitUsage() ) ); |
|
} |
|
|
|
/** |
|
* Apply s limit clause to the query. |
|
* <p/> |
|
* Typically dialects utilize {@link #supportsVariableLimit() variable} |
|
* limit clauses when they support limits. Thus, when building the |
|
* select command we do not actually need to know the limit or the offest |
|
* since we will just be using placeholders. |
|
* <p/> |
|
* Here we do still pass along whether or not an offset was specified |
|
* so that dialects not supporting offsets can generate proper exceptions. |
|
* In general, dialects will override one or the other of this method and |
|
* {@link #getLimitString(String, int, int)}. |
|
* |
|
* @param query The query to which to apply the limit. |
|
* @param hasOffset Is the query requesting an offset? |
|
* @return the modified SQL |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
protected String getLimitString(String query, boolean hasOffset) { |
|
throw new UnsupportedOperationException( "Paged queries not supported by " + getClass().getName()); |
|
} |
|
|
|
/** |
|
* Hibernate APIs explicitly state that setFirstResult() should be a zero-based offset. Here we allow the |
|
* Dialect a chance to convert that value based on what the underlying db or driver will expect. |
|
* <p/> |
|
* NOTE: what gets passed into {@link #getLimitString(String,int,int)} is the zero-based offset. Dialects which |
|
* do not {@link #supportsVariableLimit} should take care to perform any needed first-row-conversion calls prior |
|
* to injecting the limit values into the SQL string. |
|
* |
|
* @param zeroBasedFirstResult The user-supplied, zero-based first row offset. |
|
* @return The corresponding db/dialect specific offset. |
|
* @see com.fr.third.org.hibernate.Query#setFirstResult |
|
* @see com.fr.third.org.hibernate.Criteria#setFirstResult |
|
* @deprecated {@link #getLimitHandler()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public int convertToFirstRowValue(int zeroBasedFirstResult) { |
|
return zeroBasedFirstResult; |
|
} |
|
|
|
|
|
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Informational metadata about whether this dialect is known to support |
|
* specifying timeouts for requested lock acquisitions. |
|
* |
|
* @return True is this dialect supports specifying lock timeouts. |
|
*/ |
|
public boolean supportsLockTimeouts() { |
|
return true; |
|
|
|
} |
|
|
|
/** |
|
* If this dialect supports specifying lock timeouts, are those timeouts |
|
* rendered into the <tt>SQL</tt> string as parameters. The implication |
|
* is that Hibernate will need to bind the timeout value as a parameter |
|
* in the {@link java.sql.PreparedStatement}. If true, the param position |
|
* is always handled as the last parameter; if the dialect specifies the |
|
* lock timeout elsewhere in the <tt>SQL</tt> statement then the timeout |
|
* value should be directly rendered into the statement and this method |
|
* should return false. |
|
* |
|
* @return True if the lock timeout is rendered into the <tt>SQL</tt> |
|
* string as a parameter; false otherwise. |
|
*/ |
|
public boolean isLockTimeoutParameterized() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Get a strategy instance which knows how to acquire a database-level lock |
|
* of the specified mode for this dialect. |
|
* |
|
* @param lockable The persister for the entity to be locked. |
|
* @param lockMode The type of lock to be acquired. |
|
* @return The appropriate locking strategy. |
|
* @since 3.2 |
|
*/ |
|
public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) { |
|
switch ( lockMode ) { |
|
case PESSIMISTIC_FORCE_INCREMENT: |
|
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode ); |
|
case PESSIMISTIC_WRITE: |
|
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode ); |
|
case PESSIMISTIC_READ: |
|
return new PessimisticReadSelectLockingStrategy( lockable, lockMode ); |
|
case OPTIMISTIC: |
|
return new OptimisticLockingStrategy( lockable, lockMode ); |
|
case OPTIMISTIC_FORCE_INCREMENT: |
|
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode ); |
|
default: |
|
return new SelectLockingStrategy( lockable, lockMode ); |
|
} |
|
} |
|
|
|
/** |
|
* Given LockOptions (lockMode, timeout), determine the appropriate for update fragment to use. |
|
* |
|
* @param lockOptions contains the lock mode to apply. |
|
* @return The appropriate for update fragment. |
|
*/ |
|
public String getForUpdateString(LockOptions lockOptions) { |
|
final LockMode lockMode = lockOptions.getLockMode(); |
|
return getForUpdateString( lockMode, lockOptions.getTimeOut() ); |
|
} |
|
|
|
@SuppressWarnings( {"deprecation"}) |
|
private String getForUpdateString(LockMode lockMode, int timeout){ |
|
switch ( lockMode ) { |
|
case UPGRADE: |
|
return getForUpdateString(); |
|
case PESSIMISTIC_READ: |
|
return getReadLockString( timeout ); |
|
case PESSIMISTIC_WRITE: |
|
return getWriteLockString( timeout ); |
|
case UPGRADE_NOWAIT: |
|
case FORCE: |
|
case PESSIMISTIC_FORCE_INCREMENT: |
|
return getForUpdateNowaitString(); |
|
case UPGRADE_SKIPLOCKED: |
|
return getForUpdateSkipLockedString(); |
|
default: |
|
return ""; |
|
} |
|
} |
|
|
|
/** |
|
* Given a lock mode, determine the appropriate for update fragment to use. |
|
* |
|
* @param lockMode The lock mode to apply. |
|
* @return The appropriate for update fragment. |
|
*/ |
|
public String getForUpdateString(LockMode lockMode) { |
|
return getForUpdateString( lockMode, LockOptions.WAIT_FOREVER ); |
|
} |
|
|
|
/** |
|
* Get the string to append to SELECT statements to acquire locks |
|
* for this dialect. |
|
* |
|
* @return The appropriate <tt>FOR UPDATE</tt> clause string. |
|
*/ |
|
public String getForUpdateString() { |
|
return " for update"; |
|
} |
|
|
|
/** |
|
* Get the string to append to SELECT statements to acquire WRITE locks |
|
* for this dialect. Location of the of the returned string is treated |
|
* the same as getForUpdateString. |
|
* |
|
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. |
|
* @return The appropriate <tt>LOCK</tt> clause string. |
|
*/ |
|
public String getWriteLockString(int timeout) { |
|
return getForUpdateString(); |
|
} |
|
|
|
/** |
|
* Get the string to append to SELECT statements to acquire WRITE locks |
|
* for this dialect. Location of the of the returned string is treated |
|
* the same as getForUpdateString. |
|
* |
|
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. |
|
* @return The appropriate <tt>LOCK</tt> clause string. |
|
*/ |
|
public String getReadLockString(int timeout) { |
|
return getForUpdateString(); |
|
} |
|
|
|
|
|
/** |
|
* Is <tt>FOR UPDATE OF</tt> syntax supported? |
|
* |
|
* @return True if the database supports <tt>FOR UPDATE OF</tt> syntax; |
|
* false otherwise. |
|
*/ |
|
public boolean forUpdateOfColumns() { |
|
// by default we report no support |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect support <tt>FOR UPDATE</tt> in conjunction with |
|
* outer joined rows? |
|
* |
|
* @return True if outer joined rows can be locked via <tt>FOR UPDATE</tt>. |
|
*/ |
|
public boolean supportsOuterJoinForUpdate() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this |
|
* dialect given the aliases of the columns to be write locked. |
|
* |
|
* @param aliases The columns to be write locked. |
|
* @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string. |
|
*/ |
|
public String getForUpdateString(String aliases) { |
|
// by default we simply return the getForUpdateString() result since |
|
// the default is to say no support for "FOR UPDATE OF ..." |
|
return getForUpdateString(); |
|
} |
|
|
|
/** |
|
* Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this |
|
* dialect given the aliases of the columns to be write locked. |
|
* |
|
* @param aliases The columns to be write locked. |
|
* @param lockOptions the lock options to apply |
|
* @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string. |
|
*/ |
|
@SuppressWarnings({"unchecked", "UnusedParameters"}) |
|
public String getForUpdateString(String aliases, LockOptions lockOptions) { |
|
LockMode lockMode = lockOptions.getLockMode(); |
|
final Iterator<Map.Entry<String, LockMode>> itr = lockOptions.getAliasLockIterator(); |
|
while ( itr.hasNext() ) { |
|
// seek the highest lock mode |
|
final Map.Entry<String, LockMode>entry = itr.next(); |
|
final LockMode lm = entry.getValue(); |
|
if ( lm.greaterThan( lockMode ) ) { |
|
lockMode = lm; |
|
} |
|
} |
|
lockOptions.setLockMode( lockMode ); |
|
return getForUpdateString( lockOptions ); |
|
} |
|
|
|
/** |
|
* Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect. |
|
* |
|
* @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string. |
|
*/ |
|
public String getForUpdateNowaitString() { |
|
// by default we report no support for NOWAIT lock semantics |
|
return getForUpdateString(); |
|
} |
|
|
|
/** |
|
* Retrieves the <tt>FOR UPDATE SKIP LOCKED</tt> syntax specific to this dialect. |
|
* |
|
* @return The appropriate <tt>FOR UPDATE SKIP LOCKED</tt> clause string. |
|
*/ |
|
public String getForUpdateSkipLockedString() { |
|
// by default we report no support for SKIP_LOCKED lock semantics |
|
return getForUpdateString(); |
|
} |
|
|
|
/** |
|
* Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate |
|
* for this dialect given the aliases of the columns to be write locked. |
|
* |
|
* @param aliases The columns to be write locked. |
|
* @return The appropriate <tt>FOR UPDATE OF colunm_list NOWAIT</tt> clause string. |
|
*/ |
|
public String getForUpdateNowaitString(String aliases) { |
|
return getForUpdateString( aliases ); |
|
} |
|
|
|
/** |
|
* Get the <tt>FOR UPDATE OF column_list SKIP LOCKED</tt> fragment appropriate |
|
* for this dialect given the aliases of the columns to be write locked. |
|
* |
|
* @param aliases The columns to be write locked. |
|
* @return The appropriate <tt>FOR UPDATE colunm_list SKIP LOCKED</tt> clause string. |
|
*/ |
|
public String getForUpdateSkipLockedString(String aliases) { |
|
return getForUpdateString( aliases ); |
|
} |
|
|
|
/** |
|
* Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>, |
|
* whereby a "lock hint" is appends to the table name in the from clause. |
|
* <p/> |
|
* contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a> |
|
* |
|
* @param mode The lock mode to apply |
|
* @param tableName The name of the table to which to apply the lock hint. |
|
* @return The table with any required lock hints. |
|
* @deprecated use {@code appendLockHint(LockOptions,String)} instead |
|
*/ |
|
@Deprecated |
|
public String appendLockHint(LockMode mode, String tableName) { |
|
return appendLockHint( new LockOptions( mode ), tableName ); |
|
} |
|
/** |
|
* Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>, |
|
* whereby a "lock hint" is appends to the table name in the from clause. |
|
* <p/> |
|
* contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a> |
|
* |
|
* @param lockOptions The lock options to apply |
|
* @param tableName The name of the table to which to apply the lock hint. |
|
* @return The table with any required lock hints. |
|
*/ |
|
public String appendLockHint(LockOptions lockOptions, String tableName){ |
|
return tableName; |
|
} |
|
|
|
/** |
|
* Modifies the given SQL by applying the appropriate updates for the specified |
|
* lock modes and key columns. |
|
* <p/> |
|
* The behavior here is that of an ANSI SQL <tt>SELECT FOR UPDATE</tt>. This |
|
* method is really intended to allow dialects which do not support |
|
* <tt>SELECT FOR UPDATE</tt> to achieve this in their own fashion. |
|
* |
|
* @param sql the SQL string to modify |
|
* @param aliasedLockOptions lock options indexed by aliased table names. |
|
* @param keyColumnNames a map of key columns indexed by aliased table names. |
|
* @return the modified SQL string. |
|
*/ |
|
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) { |
|
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString(); |
|
} |
|
|
|
|
|
// table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Command used to create a table. |
|
* |
|
* @return The command used to create a table. |
|
*/ |
|
public String getCreateTableString() { |
|
return "create table"; |
|
} |
|
|
|
/** |
|
* Slight variation on {@link #getCreateTableString}. Here, we have the |
|
* command used to create a table when there is no primary key and |
|
* duplicate rows are expected. |
|
* <p/> |
|
* Most databases do not care about the distinction; originally added for |
|
* Teradata support which does care. |
|
* |
|
* @return The command used to create a multiset table. |
|
*/ |
|
public String getCreateMultisetTableString() { |
|
return getCreateTableString(); |
|
} |
|
|
|
public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() { |
|
return new PersistentTableBulkIdStrategy(); |
|
} |
|
|
|
// callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Registers a parameter (either OUT, or the new REF_CURSOR param type available in Java 8) capable of |
|
* returning {@link java.sql.ResultSet} *by position*. Pre-Java 8, registering such ResultSet-returning |
|
* parameters varied greatly across database and drivers; hence its inclusion as part of the Dialect contract. |
|
* |
|
* @param statement The callable statement. |
|
* @param position The bind position at which to register the output param. |
|
* |
|
* @return The number of (contiguous) bind positions used. |
|
* |
|
* @throws SQLException Indicates problems registering the param. |
|
*/ |
|
public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException { |
|
throw new UnsupportedOperationException( |
|
getClass().getName() + |
|
" does not support resultsets via stored procedures" |
|
); |
|
} |
|
|
|
/** |
|
* Registers a parameter (either OUT, or the new REF_CURSOR param type available in Java 8) capable of |
|
* returning {@link java.sql.ResultSet} *by name*. Pre-Java 8, registering such ResultSet-returning |
|
* parameters varied greatly across database and drivers; hence its inclusion as part of the Dialect contract. |
|
* |
|
* @param statement The callable statement. |
|
* @param name The parameter name (for drivers which support named parameters). |
|
* |
|
* @return The number of (contiguous) bind positions used. |
|
* |
|
* @throws SQLException Indicates problems registering the param. |
|
*/ |
|
@SuppressWarnings("UnusedParameters") |
|
public int registerResultSetOutParameter(CallableStatement statement, String name) throws SQLException { |
|
throw new UnsupportedOperationException( |
|
getClass().getName() + |
|
" does not support resultsets via stored procedures" |
|
); |
|
} |
|
|
|
/** |
|
* Given a callable statement previously processed by {@link #registerResultSetOutParameter}, |
|
* extract the {@link java.sql.ResultSet} from the OUT parameter. |
|
* |
|
* @param statement The callable statement. |
|
* @return The extracted result set. |
|
* @throws SQLException Indicates problems extracting the result set. |
|
*/ |
|
public ResultSet getResultSet(CallableStatement statement) throws SQLException { |
|
throw new UnsupportedOperationException( |
|
getClass().getName() + " does not support resultsets via stored procedures" |
|
); |
|
} |
|
|
|
/** |
|
* Given a callable statement previously processed by {@link #registerResultSetOutParameter}, |
|
* extract the {@link java.sql.ResultSet}. |
|
* |
|
* @param statement The callable statement. |
|
* @param position The bind position at which to register the output param. |
|
* |
|
* @return The extracted result set. |
|
* |
|
* @throws SQLException Indicates problems extracting the result set. |
|
*/ |
|
@SuppressWarnings("UnusedParameters") |
|
public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { |
|
throw new UnsupportedOperationException( |
|
getClass().getName() + " does not support resultsets via stored procedures" |
|
); |
|
} |
|
|
|
/** |
|
* Given a callable statement previously processed by {@link #registerResultSetOutParameter}, |
|
* extract the {@link java.sql.ResultSet} from the OUT parameter. |
|
* |
|
* @param statement The callable statement. |
|
* @param name The parameter name (for drivers which support named parameters). |
|
* |
|
* @return The extracted result set. |
|
* |
|
* @throws SQLException Indicates problems extracting the result set. |
|
*/ |
|
@SuppressWarnings("UnusedParameters") |
|
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { |
|
throw new UnsupportedOperationException( |
|
getClass().getName() + " does not support resultsets via stored procedures" |
|
); |
|
} |
|
|
|
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Does this dialect support a way to retrieve the database's current |
|
* timestamp value? |
|
* |
|
* @return True if the current timestamp can be retrieved; false otherwise. |
|
*/ |
|
public boolean supportsCurrentTimestampSelection() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Should the value returned by {@link #getCurrentTimestampSelectString} |
|
* be treated as callable. Typically this indicates that JDBC escape |
|
* syntax is being used... |
|
* |
|
* @return True if the {@link #getCurrentTimestampSelectString} return |
|
* is callable; false otherwise. |
|
*/ |
|
public boolean isCurrentTimestampSelectStringCallable() { |
|
throw new UnsupportedOperationException( "Database not known to define a current timestamp function" ); |
|
} |
|
|
|
/** |
|
* Retrieve the command used to retrieve the current timestamp from the |
|
* database. |
|
* |
|
* @return The command. |
|
*/ |
|
public String getCurrentTimestampSelectString() { |
|
throw new UnsupportedOperationException( "Database not known to define a current timestamp function" ); |
|
} |
|
|
|
/** |
|
* The name of the database-specific SQL function for retrieving the |
|
* current timestamp. |
|
* |
|
* @return The function name. |
|
*/ |
|
public String getCurrentTimestampSQLFunctionName() { |
|
// the standard SQL function name is current_timestamp... |
|
return "current_timestamp"; |
|
} |
|
|
|
|
|
// SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Build an instance of the SQLExceptionConverter preferred by this dialect for |
|
* converting SQLExceptions into Hibernate's JDBCException hierarchy. |
|
* <p/> |
|
* The preferred method is to not override this method; if possible, |
|
* {@link #buildSQLExceptionConversionDelegate()} should be overridden |
|
* instead. |
|
* |
|
* If this method is not overridden, the default SQLExceptionConverter |
|
* implementation executes 3 SQLException converter delegates: |
|
* <ol> |
|
* <li>a "static" delegate based on the JDBC 4 defined SQLException hierarchy;</li> |
|
* <li>the vendor-specific delegate returned by {@link #buildSQLExceptionConversionDelegate()}; |
|
* (it is strongly recommended that specific Dialect implementations |
|
* override {@link #buildSQLExceptionConversionDelegate()})</li> |
|
* <li>a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, |
|
* depending on java.sql.DatabaseMetaData#getSQLStateType</li> |
|
* </ol> |
|
* <p/> |
|
* If this method is overridden, it is strongly recommended that the |
|
* returned {@link SQLExceptionConverter} interpret SQL errors based on |
|
* vendor-specific error codes rather than the SQLState since the |
|
* interpretation is more accurate when using vendor-specific ErrorCodes. |
|
* |
|
* @return The Dialect's preferred SQLExceptionConverter, or null to |
|
* indicate that the default {@link SQLExceptionConverter} should be used. |
|
* |
|
* @see {@link #buildSQLExceptionConversionDelegate()} |
|
* @deprecated {@link #buildSQLExceptionConversionDelegate()} should be |
|
* overridden instead. |
|
*/ |
|
@Deprecated |
|
public SQLExceptionConverter buildSQLExceptionConverter() { |
|
return null; |
|
} |
|
|
|
/** |
|
* Build an instance of a {@link SQLExceptionConversionDelegate} for |
|
* interpreting dialect-specific error or SQLState codes. |
|
* <p/> |
|
* When {@link #buildSQLExceptionConverter} returns null, the default |
|
* {@link SQLExceptionConverter} is used to interpret SQLState and |
|
* error codes. If this method is overridden to return a non-null value, |
|
* the default {@link SQLExceptionConverter} will use the returned |
|
* {@link SQLExceptionConversionDelegate} in addition to the following |
|
* standard delegates: |
|
* <ol> |
|
* <li>a "static" delegate based on the JDBC 4 defined SQLException hierarchy;</li> |
|
* <li>a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, |
|
* depending on java.sql.DatabaseMetaData#getSQLStateType</li> |
|
* </ol> |
|
* <p/> |
|
* It is strongly recommended that specific Dialect implementations override this |
|
* method, since interpretation of a SQL error is much more accurate when based on |
|
* the a vendor-specific ErrorCode rather than the SQLState. |
|
* <p/> |
|
* Specific Dialects may override to return whatever is most appropriate for that vendor. |
|
* |
|
* @return The SQLExceptionConversionDelegate for this dialect |
|
*/ |
|
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { |
|
return null; |
|
} |
|
|
|
private static final ViolatedConstraintNameExtracter EXTRACTER = new ViolatedConstraintNameExtracter() { |
|
public String extractConstraintName(SQLException sqle) { |
|
return null; |
|
} |
|
}; |
|
|
|
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { |
|
return EXTRACTER; |
|
} |
|
|
|
|
|
// union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Given a {@link java.sql.Types} type code, determine an appropriate |
|
* null value to use in a select clause. |
|
* <p/> |
|
* One thing to consider here is that certain databases might |
|
* require proper casting for the nulls here since the select here |
|
* will be part of a UNION/UNION ALL. |
|
* |
|
* @param sqlType The {@link java.sql.Types} type code. |
|
* @return The appropriate select clause value fragment. |
|
*/ |
|
public String getSelectClauseNullString(int sqlType) { |
|
return "null"; |
|
} |
|
|
|
/** |
|
* Does this dialect support UNION ALL, which is generally a faster |
|
* variant of UNION? |
|
* |
|
* @return True if UNION ALL is supported; false otherwise. |
|
*/ |
|
public boolean supportsUnionAll() { |
|
return false; |
|
} |
|
|
|
|
|
// miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
|
|
/** |
|
* Create a {@link com.fr.third.org.hibernate.sql.JoinFragment} strategy responsible |
|
* for handling this dialect's variations in how joins are handled. |
|
* |
|
* @return This dialect's {@link com.fr.third.org.hibernate.sql.JoinFragment} strategy. |
|
*/ |
|
public JoinFragment createOuterJoinFragment() { |
|
return new ANSIJoinFragment(); |
|
} |
|
|
|
/** |
|
* Create a {@link com.fr.third.org.hibernate.sql.CaseFragment} strategy responsible |
|
* for handling this dialect's variations in how CASE statements are |
|
* handled. |
|
* |
|
* @return This dialect's {@link com.fr.third.org.hibernate.sql.CaseFragment} strategy. |
|
*/ |
|
public CaseFragment createCaseFragment() { |
|
return new ANSICaseFragment(); |
|
} |
|
|
|
/** |
|
* The fragment used to insert a row without specifying any column values. |
|
* This is not possible on some databases. |
|
* |
|
* @return The appropriate empty values clause. |
|
*/ |
|
public String getNoColumnsInsertString() { |
|
return "values ( )"; |
|
} |
|
|
|
/** |
|
* The name of the SQL function that transforms a string to |
|
* lowercase |
|
* |
|
* @return The dialect-specific lowercase function. |
|
*/ |
|
public String getLowercaseFunction() { |
|
return "lower"; |
|
} |
|
|
|
/** |
|
* The name of the SQL function that can do case insensitive <b>like</b> comparison. |
|
* |
|
* @return The dialect-specific "case insensitive" like function. |
|
*/ |
|
public String getCaseInsensitiveLike(){ |
|
return "like"; |
|
} |
|
|
|
/** |
|
* Does this dialect support case insensitive LIKE restrictions? |
|
* |
|
* @return {@code true} if the underlying database supports case insensitive like comparison, |
|
* {@code false} otherwise. The default is {@code false}. |
|
*/ |
|
public boolean supportsCaseInsensitiveLike(){ |
|
return false; |
|
} |
|
|
|
/** |
|
* Meant as a means for end users to affect the select strings being sent |
|
* to the database and perhaps manipulate them in some fashion. |
|
* <p/> |
|
* The recommend approach is to instead use |
|
* {@link com.fr.third.org.hibernate.Interceptor#onPrepareStatement(String)}. |
|
* |
|
* @param select The select command |
|
* @return The mutated select command, or the same as was passed in. |
|
*/ |
|
public String transformSelectString(String select) { |
|
return select; |
|
} |
|
|
|
/** |
|
* What is the maximum length Hibernate can use for generated aliases? |
|
* <p/> |
|
* The maximum here should account for the fact that Hibernate often needs to append "uniqueing" information |
|
* to the end of generated aliases. That "uniqueing" information will be added to the end of a identifier |
|
* generated to the length specified here; so be sure to leave some room (generally speaking 5 positions will |
|
* suffice). |
|
* |
|
* @return The maximum length. |
|
*/ |
|
public int getMaxAliasLength() { |
|
return 10; |
|
} |
|
|
|
/** |
|
* The SQL literal value to which this database maps boolean values. |
|
* |
|
* @param bool The boolean value |
|
* @return The appropriate SQL literal. |
|
*/ |
|
public String toBooleanValueString(boolean bool) { |
|
return bool ? "1" : "0"; |
|
} |
|
|
|
|
|
// keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
protected void registerKeyword(String word) { |
|
sqlKeywords.add( word ); |
|
} |
|
|
|
/** |
|
* @deprecated These are only ever used (if at all) from the code that handles identifier quoting. So |
|
* see {@link #buildIdentifierHelper} instead |
|
*/ |
|
@Deprecated |
|
public Set<String> getKeywords() { |
|
return sqlKeywords; |
|
} |
|
|
|
/** |
|
* Build the IdentifierHelper indicated by this Dialect for handling identifier conversions. |
|
* Returning {@code null} is allowed and indicates that Hibernate should fallback to building a |
|
* "standard" helper. In the fallback path, any changes made to the IdentifierHelperBuilder |
|
* during this call will still be incorporated into the built IdentifierHelper. |
|
* <p/> |
|
* The incoming builder will have the following set:<ul> |
|
* <li>{@link IdentifierHelperBuilder#isGloballyQuoteIdentifiers()}</li> |
|
* <li>{@link IdentifierHelperBuilder#getUnquotedCaseStrategy()} - initialized to UPPER</li> |
|
* <li>{@link IdentifierHelperBuilder#getQuotedCaseStrategy()} - initialized to MIXED</li> |
|
* </ul> |
|
* <p/> |
|
* By default Hibernate will do the following:<ul> |
|
* <li>Call {@link IdentifierHelperBuilder#applyIdentifierCasing(DatabaseMetaData)} |
|
* <li>Call {@link IdentifierHelperBuilder#applyReservedWords(DatabaseMetaData)} |
|
* <li>Applies {@link AnsiSqlKeywords#sql2003()} as reserved words</li> |
|
* <li>Applies the {#link #sqlKeywords} collected here as reserved words</li> |
|
* <li>Applies the Dialect's NameQualifierSupport, if it defines one</li> |
|
* </ul> |
|
* |
|
* @param builder A semi-configured IdentifierHelper builder. |
|
* @param dbMetaData Access to the metadata returned from the driver if needed and if available. WARNING: may be {@code null} |
|
* |
|
* @return The IdentifierHelper instance to use, or {@code null} to indicate Hibernate should use its fallback path |
|
* |
|
* @throws SQLException Accessing the DatabaseMetaData can throw it. Just re-throw and Hibernate will handle. |
|
* |
|
* @see #getNameQualifierSupport() |
|
*/ |
|
public IdentifierHelper buildIdentifierHelper( |
|
IdentifierHelperBuilder builder, |
|
DatabaseMetaData dbMetaData) throws SQLException { |
|
builder.applyIdentifierCasing( dbMetaData ); |
|
|
|
builder.applyReservedWords( dbMetaData ); |
|
builder.applyReservedWords( AnsiSqlKeywords.INSTANCE.sql2003() ); |
|
builder.applyReservedWords( sqlKeywords ); |
|
|
|
builder.setNameQualifierSupport( getNameQualifierSupport() ); |
|
|
|
return builder.build(); |
|
} |
|
|
|
|
|
// identifier quoting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* The character specific to this dialect used to begin a quoted identifier. |
|
* |
|
* @return The dialect's specific open quote character. |
|
*/ |
|
public char openQuote() { |
|
return '"'; |
|
} |
|
|
|
/** |
|
* The character specific to this dialect used to close a quoted identifier. |
|
* |
|
* @return The dialect's specific close quote character. |
|
*/ |
|
public char closeQuote() { |
|
return '"'; |
|
} |
|
|
|
/** |
|
* Apply dialect-specific quoting. |
|
* <p/> |
|
* By default, the incoming value is checked to see if its first character |
|
* is the back-tick (`). If so, the dialect specific quoting is applied. |
|
* |
|
* @param name The value to be quoted. |
|
* @return The quoted (or unmodified, if not starting with back-tick) value. |
|
* @see #openQuote() |
|
* @see #closeQuote() |
|
*/ |
|
public final String quote(String name) { |
|
if ( name == null ) { |
|
return null; |
|
} |
|
|
|
if ( name.charAt( 0 ) == '`' ) { |
|
return openQuote() + name.substring( 1, name.length() - 1 ) + closeQuote(); |
|
} |
|
else { |
|
return name; |
|
} |
|
} |
|
|
|
|
|
// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
private StandardTableExporter tableExporter = new StandardTableExporter( this ); |
|
private StandardSequenceExporter sequenceExporter = new StandardSequenceExporter( this ); |
|
private StandardIndexExporter indexExporter = new StandardIndexExporter( this ); |
|
private StandardForeignKeyExporter foreignKeyExporter = new StandardForeignKeyExporter( this ); |
|
private StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this ); |
|
private StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this ); |
|
|
|
public Exporter<Table> getTableExporter() { |
|
return tableExporter; |
|
} |
|
|
|
public Exporter<Sequence> getSequenceExporter() { |
|
return sequenceExporter; |
|
} |
|
|
|
public Exporter<Index> getIndexExporter() { |
|
return indexExporter; |
|
} |
|
|
|
public Exporter<ForeignKey> getForeignKeyExporter() { |
|
return foreignKeyExporter; |
|
} |
|
|
|
public Exporter<Constraint> getUniqueKeyExporter() { |
|
return uniqueKeyExporter; |
|
} |
|
|
|
public Exporter<AuxiliaryDatabaseObject> getAuxiliaryDatabaseObjectExporter() { |
|
return auxiliaryObjectExporter; |
|
} |
|
|
|
/** |
|
* Does this dialect support catalog creation? |
|
* |
|
* @return True if the dialect supports catalog creation; false otherwise. |
|
*/ |
|
public boolean canCreateCatalog() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Get the SQL command used to create the named catalog |
|
* |
|
* @param catalogName The name of the catalog to be created. |
|
* |
|
* @return The creation commands |
|
*/ |
|
public String[] getCreateCatalogCommand(String catalogName) { |
|
throw new UnsupportedOperationException( "No create catalog syntax supported by " + getClass().getName() ); |
|
} |
|
|
|
/** |
|
* Get the SQL command used to drop the named catalog |
|
* |
|
* @param catalogName The name of the catalog to be dropped. |
|
* |
|
* @return The drop commands |
|
*/ |
|
public String[] getDropCatalogCommand(String catalogName) { |
|
throw new UnsupportedOperationException( "No drop catalog syntax supported by " + getClass().getName() ); |
|
} |
|
|
|
/** |
|
* Does this dialect support schema creation? |
|
* |
|
* @return True if the dialect supports schema creation; false otherwise. |
|
*/ |
|
public boolean canCreateSchema() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Get the SQL command used to create the named schema |
|
* |
|
* @param schemaName The name of the schema to be created. |
|
* |
|
* @return The creation commands |
|
*/ |
|
public String[] getCreateSchemaCommand(String schemaName) { |
|
return new String[] {"create schema " + schemaName}; |
|
} |
|
|
|
/** |
|
* Get the SQL command used to drop the named schema |
|
* |
|
* @param schemaName The name of the schema to be dropped. |
|
* |
|
* @return The drop commands |
|
*/ |
|
public String[] getDropSchemaCommand(String schemaName) { |
|
return new String[] {"drop schema " + schemaName}; |
|
} |
|
|
|
/** |
|
* Get the SQL command used to retrieve the current schema name. Works in conjunction |
|
* with {@link #getSchemaNameResolver()}, unless the return from there does not need this |
|
* information. E.g., a custom impl might make use of the Java 1.7 addition of |
|
* the {@link java.sql.Connection#getSchema()} method |
|
* |
|
* @return The current schema retrieval SQL |
|
*/ |
|
public String getCurrentSchemaCommand() { |
|
return null; |
|
} |
|
|
|
/** |
|
* Get the strategy for determining the schema name of a Connection |
|
* |
|
* @return The schema name resolver strategy |
|
*/ |
|
public SchemaNameResolver getSchemaNameResolver() { |
|
return DefaultSchemaNameResolver.INSTANCE; |
|
} |
|
|
|
/** |
|
* Does this dialect support the <tt>ALTER TABLE</tt> syntax? |
|
* |
|
* @return True if we support altering of tables; false otherwise. |
|
*/ |
|
public boolean hasAlterTable() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Do we need to drop constraints before dropping tables in this dialect? |
|
* |
|
* @return True if constraints must be dropped prior to dropping |
|
* the table; false otherwise. |
|
*/ |
|
public boolean dropConstraints() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Do we need to qualify index names with the schema name? |
|
* |
|
* @return boolean |
|
*/ |
|
public boolean qualifyIndexName() { |
|
return true; |
|
} |
|
|
|
/** |
|
* The syntax used to add a column to a table (optional). |
|
* |
|
* @return The "add column" fragment. |
|
*/ |
|
public String getAddColumnString() { |
|
throw new UnsupportedOperationException( "No add column syntax supported by " + getClass().getName() ); |
|
} |
|
|
|
/** |
|
* The syntax for the suffix used to add a column to a table (optional). |
|
* |
|
* @return The suffix "add column" fragment. |
|
*/ |
|
public String getAddColumnSuffixString() { |
|
return ""; |
|
} |
|
|
|
public String getDropForeignKeyString() { |
|
return " drop constraint "; |
|
} |
|
|
|
public String getTableTypeString() { |
|
// grrr... for differentiation of mysql storage engines |
|
return ""; |
|
} |
|
|
|
/** |
|
* The syntax used to add a foreign key constraint to a table. |
|
* |
|
* @param constraintName The FK constraint name. |
|
* @param foreignKey The names of the columns comprising the FK |
|
* @param referencedTable The table referenced by the FK |
|
* @param primaryKey The explicit columns in the referencedTable referenced |
|
* by this FK. |
|
* @param referencesPrimaryKey if false, constraint should be |
|
* explicit about which column names the constraint refers to |
|
* |
|
* @return the "add FK" fragment |
|
*/ |
|
public String getAddForeignKeyConstraintString( |
|
String constraintName, |
|
String[] foreignKey, |
|
String referencedTable, |
|
String[] primaryKey, |
|
boolean referencesPrimaryKey) { |
|
final StringBuilder res = new StringBuilder( 30 ); |
|
|
|
res.append( " add constraint " ) |
|
.append( quote( constraintName ) ) |
|
.append( " foreign key (" ) |
|
.append( StringHelper.join( ", ", foreignKey ) ) |
|
.append( ") references " ) |
|
.append( referencedTable ); |
|
|
|
if ( !referencesPrimaryKey ) { |
|
res.append( " (" ) |
|
.append( StringHelper.join( ", ", primaryKey ) ) |
|
.append( ')' ); |
|
} |
|
|
|
return res.toString(); |
|
} |
|
|
|
public String getAddForeignKeyConstraintString( |
|
String constraintName, |
|
String foreignKeyDefinition) { |
|
return new StringBuilder( 30 ) |
|
.append( " add constraint " ) |
|
.append( quote( constraintName ) ) |
|
.append( " " ) |
|
.append( foreignKeyDefinition ) |
|
.toString(); |
|
} |
|
|
|
/** |
|
* The syntax used to add a primary key constraint to a table. |
|
* |
|
* @param constraintName The name of the PK constraint. |
|
* @return The "add PK" fragment |
|
*/ |
|
public String getAddPrimaryKeyConstraintString(String constraintName) { |
|
return " add constraint " + constraintName + " primary key "; |
|
} |
|
|
|
|
|
public String getDropPrimaryKeyConstraintString(String constraintName) { |
|
return " drop constraint " + constraintName; |
|
} |
|
|
|
/** |
|
* Does the database/driver have bug in deleting rows that refer to other rows being deleted in the same query? |
|
* |
|
* @return {@code true} if the database/driver has this bug |
|
*/ |
|
public boolean hasSelfReferentialForeignKeyBug() { |
|
return false; |
|
} |
|
|
|
/** |
|
* The keyword used to specify a nullable column. |
|
* |
|
* @return String |
|
*/ |
|
public String getNullColumnString() { |
|
return ""; |
|
} |
|
|
|
/** |
|
* Does this dialect/database support commenting on tables, columns, etc? |
|
* |
|
* @return {@code true} if commenting is supported |
|
*/ |
|
public boolean supportsCommentOn() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Get the comment into a form supported for table definition. |
|
* |
|
* @param comment The comment to apply |
|
* |
|
* @return The comment fragment |
|
*/ |
|
public String getTableComment(String comment) { |
|
return ""; |
|
} |
|
|
|
/** |
|
* Get the comment into a form supported for column definition. |
|
* |
|
* @param comment The comment to apply |
|
* |
|
* @return The comment fragment |
|
*/ |
|
public String getColumnComment(String comment) { |
|
return ""; |
|
} |
|
|
|
/** |
|
* For dropping a table, can the phrase "if exists" be applied before the table name? |
|
* <p/> |
|
* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsAfterTableName} should return true |
|
* |
|
* @return {@code true} if the "if exists" can be applied before the table name |
|
*/ |
|
public boolean supportsIfExistsBeforeTableName() { |
|
return false; |
|
} |
|
|
|
/** |
|
* For dropping a table, can the phrase "if exists" be applied after the table name? |
|
* <p/> |
|
* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsBeforeTableName} should return true |
|
* |
|
* @return {@code true} if the "if exists" can be applied after the table name |
|
*/ |
|
public boolean supportsIfExistsAfterTableName() { |
|
return false; |
|
} |
|
|
|
/** |
|
* For dropping a constraint with an "alter table", can the phrase "if exists" be applied before the constraint name? |
|
* <p/> |
|
* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsAfterConstraintName} should return true |
|
* |
|
* @return {@code true} if the "if exists" can be applied before the constraint name |
|
*/ |
|
public boolean supportsIfExistsBeforeConstraintName() { |
|
return false; |
|
} |
|
|
|
/** |
|
* For dropping a constraint with an "alter table", can the phrase "if exists" be applied after the constraint name? |
|
* <p/> |
|
* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsBeforeConstraintName} should return true |
|
* |
|
* @return {@code true} if the "if exists" can be applied after the constraint name |
|
*/ |
|
public boolean supportsIfExistsAfterConstraintName() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Generate a DROP TABLE statement |
|
* |
|
* @param tableName The name of the table to drop |
|
* |
|
* @return The DROP TABLE command |
|
*/ |
|
public String getDropTableString(String tableName) { |
|
final StringBuilder buf = new StringBuilder( "drop table " ); |
|
if ( supportsIfExistsBeforeTableName() ) { |
|
buf.append( "if exists " ); |
|
} |
|
buf.append( tableName ).append( getCascadeConstraintsString() ); |
|
if ( supportsIfExistsAfterTableName() ) { |
|
buf.append( " if exists" ); |
|
} |
|
return buf.toString(); |
|
} |
|
|
|
/** |
|
* Does this dialect support column-level check constraints? |
|
* |
|
* @return True if column-level CHECK constraints are supported; false |
|
* otherwise. |
|
*/ |
|
public boolean supportsColumnCheck() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support table-level check constraints? |
|
* |
|
* @return True if table-level CHECK constraints are supported; false |
|
* otherwise. |
|
*/ |
|
public boolean supportsTableCheck() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support cascaded delete on foreign key definitions? |
|
* |
|
* @return {@code true} indicates that the dialect does support cascaded delete on foreign keys. |
|
*/ |
|
public boolean supportsCascadeDelete() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Completely optional cascading drop clause |
|
* |
|
* @return String |
|
*/ |
|
public String getCascadeConstraintsString() { |
|
return ""; |
|
} |
|
|
|
/** |
|
* Returns the separator to use for defining cross joins when translating HQL queries. |
|
* <p/> |
|
* Typically this will be either [<tt> cross join </tt>] or [<tt>, </tt>] |
|
* <p/> |
|
* Note that the spaces are important! |
|
* |
|
* @return The cross join separator |
|
*/ |
|
public String getCrossJoinSeparator() { |
|
return " cross join "; |
|
} |
|
|
|
public ColumnAliasExtractor getColumnAliasExtractor() { |
|
return ColumnAliasExtractor.COLUMN_LABEL_EXTRACTOR; |
|
} |
|
|
|
|
|
// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
/** |
|
* Does this dialect support empty IN lists? |
|
* <p/> |
|
* For example, is [where XYZ in ()] a supported construct? |
|
* |
|
* @return True if empty in lists are supported; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsEmptyInList() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Are string comparisons implicitly case insensitive. |
|
* <p/> |
|
* In other words, does [where 'XYZ' = 'xyz'] resolve to true? |
|
* |
|
* @return True if comparisons are case insensitive. |
|
* @since 3.2 |
|
*/ |
|
public boolean areStringComparisonsCaseInsensitive() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Is this dialect known to support what ANSI-SQL terms "row value |
|
* constructor" syntax; sometimes called tuple syntax. |
|
* <p/> |
|
* Basically, does it support syntax like |
|
* "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...". |
|
* |
|
* @return True if this SQL dialect is known to support "row value |
|
* constructor" syntax; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsRowValueConstructorSyntax() { |
|
// return false here, as most databases do not properly support this construct... |
|
return false; |
|
} |
|
|
|
/** |
|
* If the dialect supports {@link #supportsRowValueConstructorSyntax() row values}, |
|
* does it offer such support in IN lists as well? |
|
* <p/> |
|
* For example, "... where (FIRST_NAME, LAST_NAME) IN ( (?, ?), (?, ?) ) ..." |
|
* |
|
* @return True if this SQL dialect is known to support "row value |
|
* constructor" syntax in the IN list; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsRowValueConstructorSyntaxInInList() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e. |
|
* {@link java.sql.PreparedStatement#setBinaryStream}). |
|
* |
|
* @return True if BLOBs and CLOBs should be bound using stream operations. |
|
* @since 3.2 |
|
*/ |
|
public boolean useInputStreamToInsertBlob() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support parameters within the <tt>SELECT</tt> clause of |
|
* <tt>INSERT ... SELECT ...</tt> statements? |
|
* |
|
* @return True if this is supported; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsParametersInInsertSelect() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect require that references to result variables |
|
* (i.e, select expresssion aliases) in an ORDER BY clause be |
|
* replaced by column positions (1-origin) as defined |
|
* by the select clause? |
|
|
|
* @return true if result variable references in the ORDER BY |
|
* clause should be replaced by column positions; |
|
* false otherwise. |
|
*/ |
|
public boolean replaceResultVariableInOrderByClauseWithPosition() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Renders an ordering fragment |
|
* |
|
* @param expression The SQL order expression. In case of {@code @OrderBy} annotation user receives property placeholder |
|
* (e.g. attribute name enclosed in '{' and '}' signs). |
|
* @param collation Collation string in format {@code collate IDENTIFIER}, or {@code null} |
|
* if expression has not been explicitly specified. |
|
* @param order Order direction. Possible values: {@code asc}, {@code desc}, or {@code null} |
|
* if expression has not been explicitly specified. |
|
* @param nulls Nulls precedence. Default value: {@link NullPrecedence#NONE}. |
|
* @return Renders single element of {@code ORDER BY} clause. |
|
*/ |
|
public String renderOrderByElement(String expression, String collation, String order, NullPrecedence nulls) { |
|
final StringBuilder orderByElement = new StringBuilder( expression ); |
|
if ( collation != null ) { |
|
orderByElement.append( " " ).append( collation ); |
|
} |
|
if ( order != null ) { |
|
orderByElement.append( " " ).append( order ); |
|
} |
|
if ( nulls != NullPrecedence.NONE ) { |
|
orderByElement.append( " nulls " ).append( nulls.name().toLowerCase(Locale.ROOT) ); |
|
} |
|
return orderByElement.toString(); |
|
} |
|
|
|
/** |
|
* Does this dialect require that parameters appearing in the <tt>SELECT</tt> clause be wrapped in <tt>cast()</tt> |
|
* calls to tell the db parser the expected type. |
|
* |
|
* @return True if select clause parameter must be cast()ed |
|
* @since 3.2 |
|
*/ |
|
public boolean requiresCastingOfParametersInSelectClause() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect support asking the result set its positioning |
|
* information on forward only cursors. Specifically, in the case of |
|
* scrolling fetches, Hibernate needs to use |
|
* {@link java.sql.ResultSet#isAfterLast} and |
|
* {@link java.sql.ResultSet#isBeforeFirst}. Certain drivers do not |
|
* allow access to these methods for forward only cursors. |
|
* <p/> |
|
* NOTE : this is highly driver dependent! |
|
* |
|
* @return True if methods like {@link java.sql.ResultSet#isAfterLast} and |
|
* {@link java.sql.ResultSet#isBeforeFirst} are supported for forward |
|
* only cursors; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support definition of cascade delete constraints |
|
* which can cause circular chains? |
|
* |
|
* @return True if circular cascade delete constraints are supported; false |
|
* otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsCircularCascadeDeleteConstraints() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Are subselects supported as the left-hand-side (LHS) of |
|
* IN-predicates. |
|
* <p/> |
|
* In other words, is syntax like "... <subquery> IN (1, 2, 3) ..." supported? |
|
* |
|
* @return True if subselects can appear as the LHS of an in-predicate; |
|
* false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsSubselectAsInPredicateLHS() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Expected LOB usage pattern is such that I can perform an insert |
|
* via prepared statement with a parameter binding for a LOB value |
|
* without crazy casting to JDBC driver implementation-specific classes... |
|
* <p/> |
|
* Part of the trickiness here is the fact that this is largely |
|
* driver dependent. For example, Oracle (which is notoriously bad with |
|
* LOB support in their drivers historically) actually does a pretty good |
|
* job with LOB support as of the 10.2.x versions of their drivers... |
|
* |
|
* @return True if normal LOB usage patterns can be used with this driver; |
|
* false if driver-specific hookiness needs to be applied. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsExpectedLobUsagePattern() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does the dialect support propagating changes to LOB |
|
* values back to the database? Talking about mutating the |
|
* internal value of the locator as opposed to supplying a new |
|
* locator instance... |
|
* <p/> |
|
* For BLOBs, the internal value might be changed by: |
|
* {@link java.sql.Blob#setBinaryStream}, |
|
* {@link java.sql.Blob#setBytes(long, byte[])}, |
|
* {@link java.sql.Blob#setBytes(long, byte[], int, int)}, |
|
* or {@link java.sql.Blob#truncate(long)}. |
|
* <p/> |
|
* For CLOBs, the internal value might be changed by: |
|
* {@link java.sql.Clob#setAsciiStream(long)}, |
|
* {@link java.sql.Clob#setCharacterStream(long)}, |
|
* {@link java.sql.Clob#setString(long, String)}, |
|
* {@link java.sql.Clob#setString(long, String, int, int)}, |
|
* or {@link java.sql.Clob#truncate(long)}. |
|
* <p/> |
|
* NOTE : I do not know the correct answer currently for |
|
* databases which (1) are not part of the cruise control process |
|
* or (2) do not {@link #supportsExpectedLobUsagePattern}. |
|
* |
|
* @return True if the changes are propagated back to the |
|
* database; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsLobValueChangePropogation() { |
|
// todo : pretty sure this is the same as the java.sql.DatabaseMetaData.locatorsUpdateCopy method added in JDBC 4, see HHH-6046 |
|
return true; |
|
} |
|
|
|
/** |
|
* Is it supported to materialize a LOB locator outside the transaction in |
|
* which it was created? |
|
* <p/> |
|
* Again, part of the trickiness here is the fact that this is largely |
|
* driver dependent. |
|
* <p/> |
|
* NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()} |
|
* also support the ability to materialize a LOB outside the owning transaction... |
|
* |
|
* @return True if unbounded materialization is supported; false otherwise. |
|
* @since 3.2 |
|
*/ |
|
public boolean supportsUnboundedLobLocatorMaterialization() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support referencing the table being mutated in |
|
* a subquery. The "table being mutated" is the table referenced in |
|
* an UPDATE or a DELETE query. And so can that table then be |
|
* referenced in a subquery of said UPDATE/DELETE query. |
|
* <p/> |
|
* For example, would the following two syntaxes be supported:<ul> |
|
* <li>delete from TABLE_A where ID not in ( select ID from TABLE_A )</li> |
|
* <li>update TABLE_A set NON_ID = 'something' where ID in ( select ID from TABLE_A)</li> |
|
* </ul> |
|
* |
|
* @return True if this dialect allows references the mutating table from |
|
* a subquery. |
|
*/ |
|
public boolean supportsSubqueryOnMutatingTable() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does the dialect support an exists statement in the select clause? |
|
* |
|
* @return True if exists checks are allowed in the select clause; false otherwise. |
|
*/ |
|
public boolean supportsExistsInSelect() { |
|
return true; |
|
} |
|
|
|
/** |
|
* For the underlying database, is READ_COMMITTED isolation implemented by |
|
* forcing readers to wait for write locks to be released? |
|
* |
|
* @return True if writers block readers to achieve READ_COMMITTED; false otherwise. |
|
*/ |
|
public boolean doesReadCommittedCauseWritersToBlockReaders() { |
|
return false; |
|
} |
|
|
|
/** |
|
* For the underlying database, is REPEATABLE_READ isolation implemented by |
|
* forcing writers to wait for read locks to be released? |
|
* |
|
* @return True if readers block writers to achieve REPEATABLE_READ; false otherwise. |
|
*/ |
|
public boolean doesRepeatableReadCauseReadersToBlockWriters() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect support using a JDBC bind parameter as an argument |
|
* to a function or procedure call? |
|
* |
|
* @return Returns {@code true} if the database supports accepting bind params as args, {@code false} otherwise. The |
|
* default is {@code true}. |
|
*/ |
|
@SuppressWarnings( {"UnusedDeclaration"}) |
|
public boolean supportsBindAsCallableArgument() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support `count(a,b)`? |
|
* |
|
* @return True if the database supports counting tuples; false otherwise. |
|
*/ |
|
public boolean supportsTupleCounts() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Does this dialect support `count(distinct a,b)`? |
|
* |
|
* @return True if the database supports counting distinct tuples; false otherwise. |
|
*/ |
|
public boolean supportsTupleDistinctCounts() { |
|
// oddly most database in fact seem to, so true is the default. |
|
return true; |
|
} |
|
|
|
/** |
|
* If {@link #supportsTupleDistinctCounts()} is true, does the Dialect require the tuple to be wrapped with parens? |
|
* |
|
* @return boolean |
|
*/ |
|
public boolean requiresParensForTupleDistinctCounts() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Return the limit that the underlying database places on the number elements in an {@code IN} predicate. |
|
* If the database defines no such limits, simply return zero or less-than-zero. |
|
* |
|
* @return int The limit, or zero-or-less to indicate no limit. |
|
*/ |
|
public int getInExpressionCountLimit() { |
|
return 0; |
|
} |
|
|
|
/** |
|
* HHH-4635 |
|
* Oracle expects all Lob values to be last in inserts and updates. |
|
* |
|
* @return boolean True of Lob values should be last, false if it |
|
* does not matter. |
|
*/ |
|
public boolean forceLobAsLastValue() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Some dialects have trouble applying pessimistic locking depending upon what other query options are |
|
* specified (paging, ordering, etc). This method allows these dialects to request that locking be applied |
|
* by subsequent selects. |
|
* |
|
* @return {@code true} indicates that the dialect requests that locking be applied by subsequent select; |
|
* {@code false} (the default) indicates that locking should be applied to the main SQL statement.. |
|
*/ |
|
public boolean useFollowOnLocking() { |
|
return false; |
|
} |
|
|
|
/** |
|
* Negate an expression |
|
* |
|
* @param expression The expression to negate |
|
* |
|
* @return The negated expression |
|
*/ |
|
public String getNotExpression(String expression) { |
|
return "not " + expression; |
|
} |
|
|
|
/** |
|
* Get the UniqueDelegate supported by this dialect |
|
* |
|
* @return The UniqueDelegate |
|
*/ |
|
public UniqueDelegate getUniqueDelegate() { |
|
return uniqueDelegate; |
|
} |
|
|
|
/** |
|
* Does this dialect support the <tt>UNIQUE</tt> column syntax? |
|
* |
|
* @return boolean |
|
* |
|
* @deprecated {@link #getUniqueDelegate()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsUnique() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Does this dialect support adding Unique constraints via create and alter table ? |
|
* |
|
* @return boolean |
|
* |
|
* @deprecated {@link #getUniqueDelegate()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsUniqueConstraintInCreateAlterTable() { |
|
return true; |
|
} |
|
|
|
/** |
|
* The syntax used to add a unique constraint to a table. |
|
* |
|
* @param constraintName The name of the unique constraint. |
|
* @return The "add unique" fragment |
|
* |
|
* @deprecated {@link #getUniqueDelegate()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public String getAddUniqueConstraintString(String constraintName) { |
|
return " add constraint " + constraintName + " unique "; |
|
} |
|
|
|
/** |
|
* Is the combination of not-null and unique supported? |
|
* |
|
* @return deprecated |
|
* |
|
* @deprecated {@link #getUniqueDelegate()} should be overridden instead. |
|
*/ |
|
@Deprecated |
|
public boolean supportsNotNullUnique() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Apply a hint to the query. The entire query is provided, allowing the Dialect full control over the placement |
|
* and syntax of the hint. By default, ignore the hint and simply return the query. |
|
* |
|
* @param query The query to which to apply the hint. |
|
* @param hints The hints to apply |
|
* @return The modified SQL |
|
*/ |
|
public String getQueryHintString(String query, List<String> hints) { |
|
return query; |
|
} |
|
|
|
/** |
|
* Certain dialects support a subset of ScrollModes. Provide a default to be used by Criteria and Query. |
|
* |
|
* @return ScrollMode |
|
*/ |
|
public ScrollMode defaultScrollMode() { |
|
return ScrollMode.SCROLL_INSENSITIVE; |
|
} |
|
|
|
/** |
|
* Does this dialect support tuples in subqueries? Ex: |
|
* delete from Table1 where (col1, col2) in (select col1, col2 from Table2) |
|
* |
|
* @return boolean |
|
*/ |
|
public boolean supportsTuplesInSubqueries() { |
|
return true; |
|
} |
|
|
|
public CallableStatementSupport getCallableStatementSupport() { |
|
// most databases do not support returning cursors (ref_cursor)... |
|
return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE; |
|
} |
|
|
|
public static String escapeComment(String comment) { |
|
if (StringHelper.isNotEmpty(comment)) { |
|
final String escaped = ESCAPE_CLOSING_COMMENT_PATTERN.matcher(comment).replaceAll("*\\\\/"); |
|
return ESCAPE_OPENING_COMMENT_PATTERN.matcher(escaped).replaceAll("/\\\\*"); |
|
} |
|
return comment; |
|
} |
|
|
|
/** |
|
* By default interpret this based on DatabaseMetaData. |
|
* |
|
* @return |
|
*/ |
|
public NameQualifierSupport getNameQualifierSupport() { |
|
return null; |
|
} |
|
|
|
protected final BatchLoadSizingStrategy STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY = new BatchLoadSizingStrategy() { |
|
@Override |
|
public int determineOptimalBatchLoadSize(int numberOfKeyColumns, int numberOfKeys) { |
|
return 50; |
|
} |
|
}; |
|
|
|
public BatchLoadSizingStrategy getDefaultBatchLoadSizingStrategy() { |
|
return STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY; |
|
} |
|
|
|
/** |
|
* Does the fetching JDBC statement warning for logging is enabled by default |
|
* |
|
* @return boolean |
|
* |
|
* @since 5.1 |
|
*/ |
|
public boolean isJdbcLogWarningsEnabledByDefault() { |
|
return true; |
|
} |
|
|
|
/** |
|
* Override the DatabaseMetaData#supportsNamedParameters() |
|
* |
|
* @return boolean |
|
* |
|
* @throws SQLException Accessing the DatabaseMetaData can throw it. Just re-throw and Hibernate will handle. |
|
*/ |
|
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { |
|
return databaseMetaData != null && databaseMetaData.supportsNamedParameters(); |
|
} |
|
|
|
/** |
|
* Does this dialect supports Nationalized Types |
|
* |
|
* @return boolean |
|
*/ |
|
public boolean supportsNationalizedTypes() { |
|
return true; |
|
} |
|
|
|
/** |
|
* 获取建表时创建约束字符串 |
|
* |
|
* @param columnList 主键列集合 |
|
* @return String |
|
*/ |
|
public String sqlConstraintString(List<Column> columnList) { |
|
StringBuilder buf = new StringBuilder("primary key ("); |
|
for (int i = 0; i < columnList.size(); i ++) { |
|
if (i != 0) { |
|
buf.append(", "); |
|
} |
|
buf.append(columnList.get(i).getQuotedName(this)); |
|
} |
|
return buf.append(')').toString(); |
|
} |
|
|
|
}
|
|
|