diff --git a/fine-hibernate/README.md b/fine-hibernate/README.md new file mode 100644 index 000000000..d78cf61b0 --- /dev/null +++ b/fine-hibernate/README.md @@ -0,0 +1,13 @@ +# fine-hibernate + +改包名的hibernate(5.1.0.Final),包括: + +- hibernate-commons-annotations +- hibernate-core +- hibernate-entitymanager +- hibernate-java8 +- hibernate-jpamodelgen + + +定制: +REPORT-108344: debug日志级别,hibernate会打印出entity信息,没找到控制项,屏蔽一下相关代码 diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/dialect/Dialect.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/dialect/Dialect.java new file mode 100644 index 000000000..0dbecea61 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/dialect/Dialect.java @@ -0,0 +1,2840 @@ +/* + * 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 . + */ +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 sqlFunctions = new HashMap(); + private final Set sqlKeywords = new HashSet(); + + 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 System 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 System 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. $l 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. $l 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}. + *

+ * 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()}. + *

+ * 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 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 LIMIT clause come at the start of the + * SELECT 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 LIMIT clause take a "maximum" row number instead + * of a total number of returned rows? + *

+ * 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) + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 SQL 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 SQL 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 SQL + * 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 FOR UPDATE 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 LOCK 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 LOCK clause string. + */ + public String getReadLockString(int timeout) { + return getForUpdateString(); + } + + + /** + * Is FOR UPDATE OF syntax supported? + * + * @return True if the database supports FOR UPDATE OF syntax; + * false otherwise. + */ + public boolean forUpdateOfColumns() { + // by default we report no support + return false; + } + + /** + * Does this dialect support FOR UPDATE in conjunction with + * outer joined rows? + * + * @return True if outer joined rows can be locked via FOR UPDATE. + */ + public boolean supportsOuterJoinForUpdate() { + return true; + } + + /** + * Get the FOR UPDATE OF column_list 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 FOR UPDATE OF column_list 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 FOR UPDATE OF column_list 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 FOR UPDATE OF column_list clause string. + */ + @SuppressWarnings({"unchecked", "UnusedParameters"}) + public String getForUpdateString(String aliases, LockOptions lockOptions) { + LockMode lockMode = lockOptions.getLockMode(); + final Iterator> itr = lockOptions.getAliasLockIterator(); + while ( itr.hasNext() ) { + // seek the highest lock mode + final Map.Entryentry = itr.next(); + final LockMode lm = entry.getValue(); + if ( lm.greaterThan( lockMode ) ) { + lockMode = lm; + } + } + lockOptions.setLockMode( lockMode ); + return getForUpdateString( lockOptions ); + } + + /** + * Retrieves the FOR UPDATE NOWAIT syntax specific to this dialect. + * + * @return The appropriate FOR UPDATE NOWAIT clause string. + */ + public String getForUpdateNowaitString() { + // by default we report no support for NOWAIT lock semantics + return getForUpdateString(); + } + + /** + * Retrieves the FOR UPDATE SKIP LOCKED syntax specific to this dialect. + * + * @return The appropriate FOR UPDATE SKIP LOCKED clause string. + */ + public String getForUpdateSkipLockedString() { + // by default we report no support for SKIP_LOCKED lock semantics + return getForUpdateString(); + } + + /** + * Get the FOR UPDATE OF column_list NOWAIT 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 FOR UPDATE OF colunm_list NOWAIT clause string. + */ + public String getForUpdateNowaitString(String aliases) { + return getForUpdateString( aliases ); + } + + /** + * Get the FOR UPDATE OF column_list SKIP LOCKED 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 FOR UPDATE colunm_list SKIP LOCKED clause string. + */ + public String getForUpdateSkipLockedString(String aliases) { + return getForUpdateString( aliases ); + } + + /** + * Some dialects support an alternative means to SELECT FOR UPDATE, + * whereby a "lock hint" is appends to the table name in the from clause. + *

+ * contributed by Helge Schulz + * + * @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 SELECT FOR UPDATE, + * whereby a "lock hint" is appends to the table name in the from clause. + *

+ * contributed by Helge Schulz + * + * @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. + *

+ * The behavior here is that of an ANSI SQL SELECT FOR UPDATE. This + * method is really intended to allow dialects which do not support + * SELECT FOR UPDATE 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 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. + *

+ * 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. + *

+ * 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: + *

    + *
  1. a "static" delegate based on the JDBC 4 defined SQLException hierarchy;
  2. + *
  3. the vendor-specific delegate returned by {@link #buildSQLExceptionConversionDelegate()}; + * (it is strongly recommended that specific Dialect implementations + * override {@link #buildSQLExceptionConversionDelegate()})
  4. + *
  5. a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, + * depending on java.sql.DatabaseMetaData#getSQLStateType
  6. + *
+ *

+ * 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. + *

+ * 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: + *

    + *
  1. a "static" delegate based on the JDBC 4 defined SQLException hierarchy;
  2. + *
  3. a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, + * depending on java.sql.DatabaseMetaData#getSQLStateType
  4. + *
+ *

+ * 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. + *

+ * 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. + *

+ * 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 like 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. + *

+ * 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? + *

+ * 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 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. + *

+ * The incoming builder will have the following set:

    + *
  • {@link IdentifierHelperBuilder#isGloballyQuoteIdentifiers()}
  • + *
  • {@link IdentifierHelperBuilder#getUnquotedCaseStrategy()} - initialized to UPPER
  • + *
  • {@link IdentifierHelperBuilder#getQuotedCaseStrategy()} - initialized to MIXED
  • + *
+ *

+ * By default Hibernate will do the following:

    + *
  • Call {@link IdentifierHelperBuilder#applyIdentifierCasing(DatabaseMetaData)} + *
  • Call {@link IdentifierHelperBuilder#applyReservedWords(DatabaseMetaData)} + *
  • Applies {@link AnsiSqlKeywords#sql2003()} as reserved words
  • + *
  • Applies the {#link #sqlKeywords} collected here as reserved words
  • + *
  • Applies the Dialect's NameQualifierSupport, if it defines one
  • + *
+ * + * @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. + *

+ * 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 getTableExporter() { + return tableExporter; + } + + public Exporter getSequenceExporter() { + return sequenceExporter; + } + + public Exporter getIndexExporter() { + return indexExporter; + } + + public Exporter getForeignKeyExporter() { + return foreignKeyExporter; + } + + public Exporter getUniqueKeyExporter() { + return uniqueKeyExporter; + } + + public Exporter 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 ALTER TABLE 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? + *

+ * 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? + *

+ * 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? + *

+ * 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? + *

+ * 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. + *

+ * Typically this will be either [ cross join ] or [, ] + *

+ * 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? + *

+ * 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. + *

+ * 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. + *

+ * 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? + *

+ * 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 SELECT clause of + * INSERT ... SELECT ... 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 SELECT clause be wrapped in cast() + * 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. + *

+ * 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. + *

+ * In other words, is syntax like "... 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... + *

+ * 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... + *

+ * 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)}. + *

+ * 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)}. + *

+ * 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? + *

+ * Again, part of the trickiness here is the fact that this is largely + * driver dependent. + *

+ * 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. + *

+ * For example, would the following two syntaxes be supported:

    + *
  • delete from TABLE_A where ID not in ( select ID from TABLE_A )
  • + *
  • update TABLE_A set NON_ID = 'something' where ID in ( select ID from TABLE_A)
  • + *
+ * + * @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 UNIQUE 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 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 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(); + } + + /** + * 获取建表时分发策略字符串 + * + * @param columnList 主键列集合 + * @return String + */ + public String sqlDistributeString(List columnList) { + return ""; + } + + /** + * 是否强制添加引用符号 + * + * @return 是/否 + */ + public boolean forceQuote() { + return false; + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/event/internal/AbstractFlushingEventListener.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/event/internal/AbstractFlushingEventListener.java new file mode 100644 index 000000000..45c740a14 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/event/internal/AbstractFlushingEventListener.java @@ -0,0 +1,390 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.event.internal; + +import java.io.Serializable; +import java.util.Map; + +import com.fr.third.org.hibernate.HibernateException; +import com.fr.third.org.hibernate.action.internal.CollectionRecreateAction; +import com.fr.third.org.hibernate.action.internal.CollectionRemoveAction; +import com.fr.third.org.hibernate.action.internal.CollectionUpdateAction; +import com.fr.third.org.hibernate.action.internal.QueuedOperationCollectionAction; +import com.fr.third.org.hibernate.collection.spi.PersistentCollection; +import com.fr.third.org.hibernate.engine.internal.Cascade; +import com.fr.third.org.hibernate.engine.internal.CascadePoint; +import com.fr.third.org.hibernate.engine.internal.Collections; +import com.fr.third.org.hibernate.engine.spi.ActionQueue; +import com.fr.third.org.hibernate.engine.spi.CascadingAction; +import com.fr.third.org.hibernate.engine.spi.CascadingActions; +import com.fr.third.org.hibernate.engine.spi.CollectionEntry; +import com.fr.third.org.hibernate.engine.spi.CollectionKey; +import com.fr.third.org.hibernate.engine.spi.EntityEntry; +import com.fr.third.org.hibernate.engine.spi.PersistenceContext; +import com.fr.third.org.hibernate.engine.spi.SessionImplementor; +import com.fr.third.org.hibernate.engine.spi.Status; +import com.fr.third.org.hibernate.event.service.spi.EventListenerRegistry; +import com.fr.third.org.hibernate.event.spi.EventSource; +import com.fr.third.org.hibernate.event.spi.EventType; +import com.fr.third.org.hibernate.event.spi.FlushEntityEvent; +import com.fr.third.org.hibernate.event.spi.FlushEntityEventListener; +import com.fr.third.org.hibernate.event.spi.FlushEvent; +import com.fr.third.org.hibernate.internal.CoreMessageLogger; +import com.fr.third.org.hibernate.internal.util.EntityPrinter; +import com.fr.third.org.hibernate.internal.util.collections.IdentityMap; +import com.fr.third.org.hibernate.internal.util.collections.LazyIterator; +import com.fr.third.org.hibernate.persister.entity.EntityPersister; + +import com.fr.third.org.jboss.logging.Logger; + +/** + * A convenience base class for listeners whose functionality results in flushing. + * + * @author Steve Ebersole + */ +public abstract class AbstractFlushingEventListener implements Serializable { + + private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, AbstractFlushingEventListener.class.getName() ); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Pre-flushing section + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Coordinates the processing necessary to get things ready for executions + * as db calls by preping the session caches and moving the appropriate + * entities and collections to their respective execution queues. + * + * @param event The flush event. + * @throws HibernateException Error flushing caches to execution queues. + */ + protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException { + + LOG.trace( "Flushing session" ); + + EventSource session = event.getSession(); + + final PersistenceContext persistenceContext = session.getPersistenceContext(); + session.getInterceptor().preFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) ); + + prepareEntityFlushes( session, persistenceContext ); + // we could move this inside if we wanted to + // tolerate collection initializations during + // collection dirty checking: + prepareCollectionFlushes( persistenceContext ); + // now, any collections that are initialized + // inside this block do not get updated - they + // are ignored until the next flush + + persistenceContext.setFlushing( true ); + try { + int entityCount = flushEntities( event, persistenceContext ); + int collectionCount = flushCollections( session, persistenceContext ); + + event.setNumberOfEntitiesProcessed( entityCount ); + event.setNumberOfCollectionsProcessed( collectionCount ); + } + finally { + persistenceContext.setFlushing(false); + } + + //some statistics + logFlushResults( event ); + } + + @SuppressWarnings( value = {"unchecked"} ) + private void logFlushResults(FlushEvent event) { + if ( !LOG.isDebugEnabled() ) { + return; + } + final EventSource session = event.getSession(); + final PersistenceContext persistenceContext = session.getPersistenceContext(); + LOG.debugf( + "Flushed: %s insertions, %s updates, %s deletions to %s objects", + session.getActionQueue().numberOfInsertions(), + session.getActionQueue().numberOfUpdates(), + session.getActionQueue().numberOfDeletions(), + persistenceContext.getNumberOfManagedEntities() + ); + LOG.debugf( + "Flushed: %s (re)creations, %s updates, %s removals to %s collections", + session.getActionQueue().numberOfCollectionCreations(), + session.getActionQueue().numberOfCollectionUpdates(), + session.getActionQueue().numberOfCollectionRemovals(), + persistenceContext.getCollectionEntries().size() + ); + //new EntityPrinter( session.getFactory() ).toString(persistenceContext.getEntitiesByKey().entrySet()); + } + + /** + * process cascade save/update at the start of a flush to discover + * any newly referenced entity that must be passed to saveOrUpdate(), + * and also apply orphan delete + */ + private void prepareEntityFlushes(EventSource session, PersistenceContext persistenceContext) throws HibernateException { + + LOG.debug( "Processing flush-time cascades" ); + + final Object anything = getAnything(); + //safe from concurrent modification because of how concurrentEntries() is implemented on IdentityMap + for ( Map.Entry me : persistenceContext.reentrantSafeEntityEntries() ) { +// for ( Map.Entry me : IdentityMap.concurrentEntries( persistenceContext.getEntityEntries() ) ) { + EntityEntry entry = (EntityEntry) me.getValue(); + Status status = entry.getStatus(); + if ( status == Status.MANAGED || status == Status.SAVING || status == Status.READ_ONLY ) { + cascadeOnFlush( session, entry.getPersister(), me.getKey(), anything ); + } + } + } + + private void cascadeOnFlush(EventSource session, EntityPersister persister, Object object, Object anything) + throws HibernateException { + session.getPersistenceContext().incrementCascadeLevel(); + try { + Cascade.cascade( getCascadingAction(), CascadePoint.BEFORE_FLUSH, session, persister, object, anything ); + } + finally { + session.getPersistenceContext().decrementCascadeLevel(); + } + } + + protected Object getAnything() { + return null; + } + + protected CascadingAction getCascadingAction() { + return CascadingActions.SAVE_UPDATE; + } + + /** + * Initialize the flags of the CollectionEntry, including the + * dirty check. + */ + private void prepareCollectionFlushes(PersistenceContext persistenceContext) throws HibernateException { + + // Initialize dirty flags for arrays + collections with composite elements + // and reset reached, doupdate, etc. + + LOG.debug( "Dirty checking collections" ); + + for ( Map.Entry entry : + IdentityMap.concurrentEntries( (Map) persistenceContext.getCollectionEntries() )) { + entry.getValue().preFlush( entry.getKey() ); + } + } + + /** + * 1. detect any dirty entities + * 2. schedule any entity updates + * 3. search out any reachable collections + */ + private int flushEntities(final FlushEvent event, final PersistenceContext persistenceContext) throws HibernateException { + + LOG.trace( "Flushing entities and processing referenced collections" ); + + final EventSource source = event.getSession(); + final Iterable flushListeners = source.getFactory().getServiceRegistry() + .getService( EventListenerRegistry.class ) + .getEventListenerGroup( EventType.FLUSH_ENTITY ) + .listeners(); + + // Among other things, updateReachables() will recursively load all + // collections that are moving roles. This might cause entities to + // be loaded. + + // So this needs to be safe from concurrent modification problems. + + final Map.Entry[] entityEntries = persistenceContext.reentrantSafeEntityEntries(); + final int count = entityEntries.length; + + for ( Map.Entry me : entityEntries ) { + + // Update the status of the object and if necessary, schedule an update + + EntityEntry entry = me.getValue(); + Status status = entry.getStatus(); + + if ( status != Status.LOADING && status != Status.GONE ) { + final FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry ); + for ( FlushEntityEventListener listener : flushListeners ) { + listener.onFlushEntity( entityEvent ); + } + } + } + + source.getActionQueue().sortActions(); + + return count; + } + + /** + * process any unreferenced collections and then inspect all known collections, + * scheduling creates/removes/updates + */ + @SuppressWarnings("unchecked") + private int flushCollections(final EventSource session, final PersistenceContext persistenceContext) throws HibernateException { + LOG.trace( "Processing unreferenced collections" ); + + final Map.Entry[] entries = IdentityMap.concurrentEntries( + (Map) persistenceContext.getCollectionEntries() + ); + + final int count = entries.length; + + for ( Map.Entry me : entries ) { + CollectionEntry ce = me.getValue(); + if ( !ce.isReached() && !ce.isIgnore() ) { + Collections.processUnreachableCollection( me.getKey(), session ); + } + } + + // Schedule updates to collections: + + LOG.trace( "Scheduling collection removes/(re)creates/updates" ); + + ActionQueue actionQueue = session.getActionQueue(); + for ( Map.Entry me : + IdentityMap.concurrentEntries( (Map) persistenceContext.getCollectionEntries() )) { + PersistentCollection coll = me.getKey(); + CollectionEntry ce = me.getValue(); + + if ( ce.isDorecreate() ) { + session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() ); + actionQueue.addAction( + new CollectionRecreateAction( + coll, + ce.getCurrentPersister(), + ce.getCurrentKey(), + session + ) + ); + } + if ( ce.isDoremove() ) { + session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() ); + actionQueue.addAction( + new CollectionRemoveAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + ce.isSnapshotEmpty(coll), + session + ) + ); + } + if ( ce.isDoupdate() ) { + session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() ); + actionQueue.addAction( + new CollectionUpdateAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + ce.isSnapshotEmpty(coll), + session + ) + ); + } + + // todo : I'm not sure the !wasInitialized part should really be part of this check + if ( !coll.wasInitialized() && coll.hasQueuedOperations() ) { + actionQueue.addAction( + new QueuedOperationCollectionAction( + coll, + ce.getLoadedPersister(), + ce.getLoadedKey(), + session + ) + ); + } + + } + + actionQueue.sortCollectionActions(); + + return count; + } + + /** + * Execute all SQL (and second-level cache updates) in a special order so that foreign-key constraints cannot + * be violated:
    + *
  1. Inserts, in the order they were performed + *
  2. Updates + *
  3. Deletion of collection elements + *
  4. Insertion of collection elements + *
  5. Deletes, in the order they were performed + *
+ * + * @param session The session being flushed + */ + protected void performExecutions(EventSource session) { + LOG.trace( "Executing flush" ); + + // IMPL NOTE : here we alter the flushing flag of the persistence context to allow + // during-flush callbacks more leniency in regards to initializing proxies and + // lazy collections during their processing. + // For more information, see HHH-2763 + try { + session.getJdbcCoordinator().flushBeginning(); + session.getPersistenceContext().setFlushing( true ); + // we need to lock the collection caches before executing entity inserts/updates in order to + // account for bi-directional associations + session.getActionQueue().prepareActions(); + session.getActionQueue().executeActions(); + } + finally { + session.getPersistenceContext().setFlushing( false ); + session.getJdbcCoordinator().flushEnding(); + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Post-flushing section + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * 1. Recreate the collection key -> collection map + * 2. rebuild the collection entries + * 3. call Interceptor.postFlush() + */ + protected void postFlush(SessionImplementor session) throws HibernateException { + + LOG.trace( "Post flush" ); + + final PersistenceContext persistenceContext = session.getPersistenceContext(); + persistenceContext.getCollectionsByKey().clear(); + + // the database has changed now, so the subselect results need to be invalidated + // the batch fetching queues should also be cleared - especially the collection batch fetching one + persistenceContext.getBatchFetchQueue().clear(); + + for ( Map.Entry me : IdentityMap.concurrentEntries( persistenceContext.getCollectionEntries() ) ) { + CollectionEntry collectionEntry = me.getValue(); + PersistentCollection persistentCollection = me.getKey(); + collectionEntry.postFlush(persistentCollection); + if ( collectionEntry.getLoadedPersister() == null ) { + //if the collection is dereferenced, unset its session reference and remove from the session cache + //iter.remove(); //does not work, since the entrySet is not backed by the set + persistentCollection.unsetSession( session ); + persistenceContext.getCollectionEntries() + .remove(persistentCollection); + } + else { + //otherwise recreate the mapping between the collection and its key + CollectionKey collectionKey = new CollectionKey( + collectionEntry.getLoadedPersister(), + collectionEntry.getLoadedKey() + ); + persistenceContext.getCollectionsByKey().put(collectionKey, persistentCollection); + } + } + + } + + protected void postPostFlush(SessionImplementor session) { + session.getInterceptor().postFlush( new LazyIterator( session.getPersistenceContext().getEntitiesByKey() ) ); + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/jpa/criteria/expression/LiteralExpression.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/jpa/criteria/expression/LiteralExpression.java new file mode 100644 index 000000000..2ad0c6336 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/jpa/criteria/expression/LiteralExpression.java @@ -0,0 +1,104 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.jpa.criteria.expression; + +import java.io.Serializable; + +import com.fr.third.org.hibernate.jpa.criteria.CriteriaBuilderImpl; +import com.fr.third.org.hibernate.jpa.criteria.ParameterRegistry; +import com.fr.third.org.hibernate.jpa.criteria.ValueHandlerFactory; +import com.fr.third.org.hibernate.jpa.criteria.compile.RenderingContext; + +/** + * Represents a literal expression. + * + * @author Steve Ebersole + */ +public class LiteralExpression extends ExpressionImpl implements Serializable { + private Object literal; + + @SuppressWarnings({ "unchecked" }) + public LiteralExpression(CriteriaBuilderImpl criteriaBuilder, T literal) { + this( criteriaBuilder, (Class) determineClass( literal ), literal ); + } + + private static Class determineClass(Object literal) { + return literal == null ? null : literal.getClass(); + } + + public LiteralExpression(CriteriaBuilderImpl criteriaBuilder, Class type, T literal) { + super( criteriaBuilder, type ); + this.literal = literal; + } + + @SuppressWarnings({ "unchecked" }) + public T getLiteral() { + return (T) literal; + } + + public void registerParameters(ParameterRegistry registry) { + // nothing to do + } + + @SuppressWarnings({ "unchecked" }) + public String render(RenderingContext renderingContext) { + if ( ValueHandlerFactory.isNumeric( literal ) ) { + return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal ); + } + else if ( ValueHandlerFactory.isBoolean( literal ) ) { + return ValueHandlerFactory.determineAppropriateHandler( (Class) literal.getClass() ).render( literal ); + } + + // else... + final String parameterName = renderingContext.registerLiteralParameterBinding( getLiteral(), getJavaType() ); + return ':' + parameterName; + } + + /** + * Inline String literal. + * + * @return escaped String + */ + private String inlineLiteral(String literal) { + return String.format("\'%s\'", escapeLiteral(literal)); + } + + /** + * Escape String literal. + * + * @return escaped String + */ + private String escapeLiteral(String literal) { + return literal.replace("'", "''"); + } + + @SuppressWarnings({"unchecked"}) + public String renderProjection(RenderingContext renderingContext) { + if (ValueHandlerFactory.isCharacter(literal)) { + // In case literal is a Character, pass literal.toString() as the argument. + return inlineLiteral(literal.toString()); + } + // some drivers/servers do not like parameters in the select clause + final ValueHandlerFactory.ValueHandler handler = + ValueHandlerFactory.determineAppropriateHandler(literal.getClass()); + return handler.render(literal); + } + @Override + @SuppressWarnings({ "unchecked" }) + protected void resetJavaType(Class targetType) { + super.resetJavaType( targetType ); + ValueHandlerFactory.ValueHandler valueHandler = getValueHandler(); + if ( valueHandler == null ) { + valueHandler = ValueHandlerFactory.determineAppropriateHandler( targetType ); + forceConversion( valueHandler ); + } + + if ( valueHandler != null ) { + literal = valueHandler.convert( literal ); + } + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/loader/plan/exec/query/internal/SelectStatementBuilder.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/loader/plan/exec/query/internal/SelectStatementBuilder.java new file mode 100644 index 000000000..d61e84bed --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/loader/plan/exec/query/internal/SelectStatementBuilder.java @@ -0,0 +1,235 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.loader.plan.exec.query.internal; + +import com.fr.third.org.hibernate.LockMode; +import com.fr.third.org.hibernate.LockOptions; +import com.fr.third.org.hibernate.dialect.Dialect; +import com.fr.third.org.hibernate.internal.util.StringHelper; + +/** + * Largely a copy of the {@link com.fr.third.org.hibernate.sql.Select} class, but changed up slightly to better meet needs + * of building a SQL SELECT statement from a LoadPlan + * + * @author Steve Ebersole + * @author Gavin King + */ +public class SelectStatementBuilder { + public final Dialect dialect; + + private StringBuilder selectClause = new StringBuilder(); + private StringBuilder fromClause = new StringBuilder(); +// private StringBuilder outerJoinsAfterFrom; + private String outerJoinsAfterFrom; + private StringBuilder whereClause; +// private StringBuilder outerJoinsAfterWhere; + private String outerJoinsAfterWhere; + private StringBuilder orderByClause; + private String comment; + private LockOptions lockOptions = new LockOptions(); + + private int guesstimatedBufferSize = 20; + + /** + * Constructs a select statement builder object. + * + * @param dialect The dialect. + */ + public SelectStatementBuilder(Dialect dialect) { + this.dialect = dialect; + } + + /** + * Appends a select clause fragment + * + * @param selection The selection fragment + */ + public void appendSelectClauseFragment(String selection) { + if ( this.selectClause.length() > 0 ) { + this.selectClause.append( ", " ); + this.guesstimatedBufferSize += 2; + } + this.selectClause.append( selection ); + this.guesstimatedBufferSize += selection.length(); + } + + /** + * Appends the from clause fragment. + * + * @param fragment The from cause fragment. + */ + public void appendFromClauseFragment(String fragment) { + if ( this.fromClause.length() > 0 ) { + this.fromClause.append( ", " ); + this.guesstimatedBufferSize += 2; + } + this.fromClause.append( fragment ); + this.guesstimatedBufferSize += fragment.length(); + } + + /** + * Appends the specified table name and alias as a from clause fragment. + * + * @param tableName The table name. + * @param alias The table alias. + */ + public void appendFromClauseFragment(String tableName, String alias) { + appendFromClauseFragment( tableName + ' ' + alias ); + } + + /** + * Appends the specified restrictions after "cleaning" the specified value + * (by trimming and removing 'and ' from beginning and ' and' from the end). + * If the where clause already exists, this method ensure that ' and ' + * prefixes the cleaned restrictions. + * + * @param restrictions The restrictions. + */ + public void appendRestrictions(String restrictions) { + final String cleaned = cleanRestrictions( restrictions ); + if ( StringHelper.isEmpty( cleaned ) ) { + return; + } + + this.guesstimatedBufferSize += cleaned.length(); + + if ( whereClause == null ) { + whereClause = new StringBuilder( cleaned ); + } + else { + whereClause.append( " and " ).append( cleaned ); + this.guesstimatedBufferSize += 5; + } + } + + private String cleanRestrictions(String restrictions) { + restrictions = restrictions.trim(); + if ( restrictions.startsWith( "and " ) ) { + restrictions = restrictions.substring( 4 ); + } + if ( restrictions.endsWith( " and" ) ) { + restrictions = restrictions.substring( 0, restrictions.length()-4 ); + } + + return restrictions; + } + + /** + * Sets the outer join fragments to be added to the "from" and "where" clauses. + * + * @param outerJoinsAfterFrom The outer join fragment to be appended to the "from" clause. + * @param outerJoinsAfterWhere The outer join fragment to be appended to the "where" clause. + */ + public void setOuterJoins(String outerJoinsAfterFrom, String outerJoinsAfterWhere) { + this.outerJoinsAfterFrom = outerJoinsAfterFrom; + + final String cleanRestrictions = cleanRestrictions( outerJoinsAfterWhere ); + this.outerJoinsAfterWhere = cleanRestrictions; + + this.guesstimatedBufferSize += outerJoinsAfterFrom.length() + cleanRestrictions.length(); + } + + /** + * Appends the "order by" fragment, prefixed by a comma if the "order by" fragment already + * exists. + * + * @param ordering The "order by" fragment to append. + */ + public void appendOrderByFragment(String ordering) { + if ( this.orderByClause == null ) { + this.orderByClause = new StringBuilder(); + } + else { + this.orderByClause.append( ", " ); + this.guesstimatedBufferSize += 2; + } + this.orderByClause.append( ordering ); + } + + /** + * Sets the comment for the select statement. + * + * @param comment The comment. + */ + public void setComment(String comment) { + this.comment = comment; + this.guesstimatedBufferSize += comment.length(); + } + + /** + * Sets the lock mode for the select statement. + * + * @param lockMode The lock mode. + */ + public void setLockMode(LockMode lockMode) { + this.lockOptions.setLockMode( lockMode ); + } + + /** + * Sets the lock options for the select statement. + * + * @param lockOptions The lock options. + */ + public void setLockOptions(LockOptions lockOptions) { + LockOptions.copy( lockOptions, this.lockOptions ); + } + + /** + * Construct an SQL SELECT statement from the given clauses. + * + * @return the SQL SELECT statement. + */ + public String toStatementString() { + StringBuilder buf = new StringBuilder( guesstimatedBufferSize ); + + if ( StringHelper.isNotEmpty( comment ) ) { + buf.append( "/* " ).append( Dialect.escapeComment(comment) ).append( " */ " ); + } + + buf.append( "select " ) + .append( selectClause ) + .append( " from " ) + .append( fromClause ); + + if ( StringHelper.isNotEmpty( outerJoinsAfterFrom ) ) { + buf.append( outerJoinsAfterFrom ); + } + + if ( isNotEmpty( whereClause ) || isNotEmpty( outerJoinsAfterWhere ) ) { + buf.append( " where " ); + // the outerJoinsAfterWhere needs to come before where clause to properly + // handle dynamic filters + if ( StringHelper.isNotEmpty( outerJoinsAfterWhere ) ) { + buf.append( outerJoinsAfterWhere ); + if ( isNotEmpty( whereClause ) ) { + buf.append( " and " ); + } + } + if ( isNotEmpty( whereClause ) ) { + buf.append( whereClause ); + } + } + + if ( orderByClause != null ) { + buf.append( " order by " ).append( orderByClause ); + } + + if ( lockOptions.getLockMode() != LockMode.NONE ) { + buf = new StringBuilder(dialect.applyLocksToSql( buf.toString(), lockOptions, null ) ); + } + + return dialect.transformSelectString( buf.toString() ); + } + + private boolean isNotEmpty(String string) { + return StringHelper.isNotEmpty( string ); + } + + private boolean isNotEmpty(StringBuilder builder) { + return builder != null && builder.length() > 0; + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Delete.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Delete.java new file mode 100644 index 000000000..0fba094dc --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Delete.java @@ -0,0 +1,127 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.sql; +import com.fr.third.org.hibernate.dialect.Dialect; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * An SQL DELETE statement + * + * @author Gavin King + */ +public class Delete { + + private String tableName; + private String versionColumnName; + private String where; + + private Map primaryKeyColumns = new LinkedHashMap(); + + private String comment; + public Delete setComment(String comment) { + this.comment = comment; + return this; + } + + public Delete setTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public String toStatementString() { + StringBuilder buf = new StringBuilder( tableName.length() + 10 ); + if ( comment!=null ) { + buf.append( "/* " ).append(Dialect.escapeComment(comment)).append( " */ " ); + } + buf.append( "delete from " ).append(tableName); + if ( where != null || !primaryKeyColumns.isEmpty() || versionColumnName != null ) { + buf.append( " where " ); + } + boolean conditionsAppended = false; + Iterator iter = primaryKeyColumns.entrySet().iterator(); + while ( iter.hasNext() ) { + Map.Entry e = (Map.Entry) iter.next(); + buf.append( e.getKey() ).append( '=' ).append( e.getValue() ); + if ( iter.hasNext() ) { + buf.append( " and " ); + } + conditionsAppended = true; + } + if ( where!=null ) { + if ( conditionsAppended ) { + buf.append( " and " ); + } + buf.append( where ); + conditionsAppended = true; + } + if ( versionColumnName!=null ) { + if ( conditionsAppended ) { + buf.append( " and " ); + } + buf.append( versionColumnName ).append( "=?" ); + } + return buf.toString(); + } + + public Delete setWhere(String where) { + this.where=where; + return this; + } + + public Delete addWhereFragment(String fragment) { + if ( where == null ) { + where = fragment; + } + else { + where += ( " and " + fragment ); + } + return this; + } + + public Delete setPrimaryKeyColumnNames(String[] columnNames) { + this.primaryKeyColumns.clear(); + addPrimaryKeyColumns(columnNames); + return this; + } + + public Delete addPrimaryKeyColumns(String[] columnNames) { + for ( String columnName : columnNames ) { + addPrimaryKeyColumn( columnName, "?" ); + } + return this; + } + + public Delete addPrimaryKeyColumns(String[] columnNames, boolean[] includeColumns, String[] valueExpressions) { + for ( int i=0; i. + */ +package com.fr.third.org.hibernate.sql; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fr.third.org.hibernate.dialect.Dialect; +import com.fr.third.org.hibernate.type.LiteralType; + +/** + * An SQL INSERT statement + * + * @author Gavin King + */ +public class Insert { + private Dialect dialect; + private String tableName; + private String comment; + private Map columns = new LinkedHashMap(); + + public Insert(Dialect dialect) { + this.dialect = dialect; + } + + protected Dialect getDialect() { + return dialect; + } + + public Insert setComment(String comment) { + this.comment = comment; + return this; + } + + public Insert addColumn(String columnName) { + return addColumn(columnName, "?"); + } + + public Insert addColumns(String[] columnNames) { + for ( int i=0; i. + */ +package com.fr.third.org.hibernate.sql; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.fr.third.org.hibernate.HibernateException; +import com.fr.third.org.hibernate.dialect.Dialect; + +/** + * Implementation of InsertSelect. + * + * @author Steve Ebersole + */ +public class InsertSelect { + private Dialect dialect; + private String tableName; + private String comment; + private List columnNames = new ArrayList(); + private Select select; + + public InsertSelect(Dialect dialect) { + this.dialect = dialect; + } + + public InsertSelect setTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public InsertSelect setComment(String comment) { + this.comment = comment; + return this; + } + + public InsertSelect addColumn(String columnName) { + columnNames.add( columnName ); + return this; + } + + public InsertSelect addColumns(String[] columnNames) { + for ( int i = 0; i < columnNames.length; i++ ) { + this.columnNames.add( columnNames[i] ); + } + return this; + } + + public InsertSelect setSelect(Select select) { + this.select = select; + return this; + } + + public String toStatementString() { + if ( tableName == null ) { + throw new HibernateException( "no table name defined for insert-select" ); + } + if ( select == null ) { + throw new HibernateException( "no select defined for insert-select" ); + } + + StringBuilder buf = new StringBuilder( (columnNames.size() * 15) + tableName.length() + 10 ); + if ( comment!=null ) { + buf.append( "/* " ).append( Dialect.escapeComment(comment) ).append( " */ " ); + } + buf.append( "insert into " ).append( tableName ); + if ( !columnNames.isEmpty() ) { + buf.append( " (" ); + Iterator itr = columnNames.iterator(); + while ( itr.hasNext() ) { + buf.append( itr.next() ); + if ( itr.hasNext() ) { + buf.append( ", " ); + } + } + buf.append( ")" ); + } + buf.append( ' ' ).append( select.toStatementString() ); + return buf.toString(); + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/QuerySelect.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/QuerySelect.java new file mode 100644 index 000000000..d3b36b1b3 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/QuerySelect.java @@ -0,0 +1,215 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.sql; + +import java.util.HashSet; +import java.util.Iterator; + +import com.fr.third.org.hibernate.dialect.Dialect; + +/** + * A translated HQL query + * + * @author Gavin King + */ +public class QuerySelect { + private Dialect dialect; + private JoinFragment joins; + private StringBuilder select = new StringBuilder(); + private StringBuilder where = new StringBuilder(); + private StringBuilder groupBy = new StringBuilder(); + private StringBuilder orderBy = new StringBuilder(); + private StringBuilder having = new StringBuilder(); + private String comment; + private boolean distinct; + + private static final HashSet DONT_SPACE_TOKENS = new HashSet(); + + static { + //dontSpace.add("'"); + DONT_SPACE_TOKENS.add( "." ); + DONT_SPACE_TOKENS.add( "+" ); + DONT_SPACE_TOKENS.add( "-" ); + DONT_SPACE_TOKENS.add( "/" ); + DONT_SPACE_TOKENS.add( "*" ); + DONT_SPACE_TOKENS.add( "<" ); + DONT_SPACE_TOKENS.add( ">" ); + DONT_SPACE_TOKENS.add( "=" ); + DONT_SPACE_TOKENS.add( "#" ); + DONT_SPACE_TOKENS.add( "~" ); + DONT_SPACE_TOKENS.add( "|" ); + DONT_SPACE_TOKENS.add( "&" ); + DONT_SPACE_TOKENS.add( "<=" ); + DONT_SPACE_TOKENS.add( ">=" ); + DONT_SPACE_TOKENS.add( "=>" ); + DONT_SPACE_TOKENS.add( "=<" ); + DONT_SPACE_TOKENS.add( "!=" ); + DONT_SPACE_TOKENS.add( "<>" ); + DONT_SPACE_TOKENS.add( "!#" ); + DONT_SPACE_TOKENS.add( "!~" ); + DONT_SPACE_TOKENS.add( "!<" ); + DONT_SPACE_TOKENS.add( "!>" ); + DONT_SPACE_TOKENS.add( "(" ); //for MySQL + DONT_SPACE_TOKENS.add( ")" ); + } + + public QuerySelect(Dialect dialect) { + this.dialect = dialect; + joins = new QueryJoinFragment( dialect, false ); + } + + public JoinFragment getJoinFragment() { + return joins; + } + + public void addSelectFragmentString(String fragment) { + if ( fragment.length() > 0 && fragment.charAt( 0 ) == ',' ) { + fragment = fragment.substring( 1 ); + } + fragment = fragment.trim(); + if ( fragment.length() > 0 ) { + if ( select.length() > 0 ) { + select.append( ", " ); + } + select.append( fragment ); + } + } + + public void addSelectColumn(String columnName, String alias) { + addSelectFragmentString( columnName + ' ' + alias ); + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public void setWhereTokens(Iterator tokens) { + //if ( conjunctiveWhere.length()>0 ) conjunctiveWhere.append(" and "); + appendTokens( where, tokens ); + } + + public void prependWhereConditions(String conditions) { + if ( where.length() > 0 ) { + where.insert( 0, conditions + " and " ); + } + else { + where.append( conditions ); + } + } + + public void setGroupByTokens(Iterator tokens) { + //if ( groupBy.length()>0 ) groupBy.append(" and "); + appendTokens( groupBy, tokens ); + } + + public void setOrderByTokens(Iterator tokens) { + //if ( orderBy.length()>0 ) orderBy.append(" and "); + appendTokens( orderBy, tokens ); + } + + public void setHavingTokens(Iterator tokens) { + //if ( having.length()>0 ) having.append(" and "); + appendTokens( having, tokens ); + } + + public void addOrderBy(String orderByString) { + if ( orderBy.length() > 0 ) { + orderBy.append( ", " ); + } + orderBy.append( orderByString ); + } + + public String toQueryString() { + StringBuilder buf = new StringBuilder( 50 ); + if ( comment != null ) { + buf.append( "/* " ).append( Dialect.escapeComment(comment) ).append( " */ " ); + } + buf.append( "select " ); + if ( distinct ) { + buf.append( "distinct " ); + } + String from = joins.toFromFragmentString(); + if ( from.startsWith( "," ) ) { + from = from.substring( 1 ); + } + else if ( from.startsWith( " inner join" ) ) { + from = from.substring( 11 ); + } + + buf.append( select.toString() ) + .append( " from" ) + .append( from ); + + String outerJoinsAfterWhere = joins.toWhereFragmentString().trim(); + String whereConditions = where.toString().trim(); + boolean hasOuterJoinsAfterWhere = outerJoinsAfterWhere.length() > 0; + boolean hasWhereConditions = whereConditions.length() > 0; + if ( hasOuterJoinsAfterWhere || hasWhereConditions ) { + buf.append( " where " ); + if ( hasOuterJoinsAfterWhere ) { + buf.append( outerJoinsAfterWhere.substring( 4 ) ); + } + if ( hasWhereConditions ) { + if ( hasOuterJoinsAfterWhere ) { + buf.append( " and (" ); + } + buf.append( whereConditions ); + if ( hasOuterJoinsAfterWhere ) { + buf.append( ")" ); + } + } + } + + if ( groupBy.length() > 0 ) { + buf.append( " group by " ).append( groupBy.toString() ); + } + if ( having.length() > 0 ) { + buf.append( " having " ).append( having.toString() ); + } + if ( orderBy.length() > 0 ) { + buf.append( " order by " ).append( orderBy.toString() ); + } + + return dialect.transformSelectString( buf.toString() ); + } + + private static void appendTokens(StringBuilder buf, Iterator iter) { + boolean lastSpaceable = true; + boolean lastQuoted = false; + while ( iter.hasNext() ) { + String token = (String) iter.next(); + boolean spaceable = !DONT_SPACE_TOKENS.contains( token ); + boolean quoted = token.startsWith( "'" ); + if ( spaceable && lastSpaceable ) { + if ( !quoted || !lastQuoted ) { + buf.append( ' ' ); + } + } + lastSpaceable = spaceable; + buf.append( token ); + lastQuoted = token.endsWith( "'" ); + } + } + + public void setComment(String comment) { + this.comment = comment; + } + + public QuerySelect copy() { + QuerySelect copy = new QuerySelect( dialect ); + copy.joins = this.joins.copy(); + copy.select.append( this.select.toString() ); + copy.where.append( this.where.toString() ); + copy.groupBy.append( this.groupBy.toString() ); + copy.orderBy.append( this.orderBy.toString() ); + copy.having.append( this.having.toString() ); + copy.comment = this.comment; + copy.distinct = this.distinct; + return copy; + } + +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Select.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Select.java new file mode 100644 index 000000000..787fab6b3 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Select.java @@ -0,0 +1,196 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.sql; +import com.fr.third.org.hibernate.LockMode; +import com.fr.third.org.hibernate.LockOptions; +import com.fr.third.org.hibernate.dialect.Dialect; +import com.fr.third.org.hibernate.internal.util.StringHelper; + + +/** + * A simple SQL SELECT statement + * @author Gavin King + */ +public class Select { + + private String selectClause; + private String fromClause; + private String outerJoinsAfterFrom; + private String whereClause; + private String outerJoinsAfterWhere; + private String orderByClause; + private String groupByClause; + private String comment; + private LockOptions lockOptions = new LockOptions(); + public final Dialect dialect; + + private int guesstimatedBufferSize = 20; + + public Select(Dialect dialect) { + this.dialect = dialect; + } + + /** + * Construct an SQL SELECT statement from the given clauses + */ + public String toStatementString() { + StringBuilder buf = new StringBuilder(guesstimatedBufferSize); + if ( StringHelper.isNotEmpty(comment) ) { + buf.append("/* ").append(Dialect.escapeComment(comment)).append(" */ "); + } + + buf.append("select ").append(selectClause) + .append(" from ").append(fromClause); + + if ( StringHelper.isNotEmpty(outerJoinsAfterFrom) ) { + buf.append(outerJoinsAfterFrom); + } + + if ( StringHelper.isNotEmpty(whereClause) || StringHelper.isNotEmpty(outerJoinsAfterWhere) ) { + buf.append(" where " ); + // the outerJoinsAfterWhere needs to come before where clause to properly + // handle dynamic filters + if ( StringHelper.isNotEmpty(outerJoinsAfterWhere) ) { + buf.append(outerJoinsAfterWhere); + if ( StringHelper.isNotEmpty(whereClause) ) { + buf.append( " and " ); + } + } + if ( StringHelper.isNotEmpty( whereClause ) ) { + buf.append(whereClause); + } + } + + if ( StringHelper.isNotEmpty(groupByClause) ) { + buf.append(" group by ").append(groupByClause); + } + + if ( StringHelper.isNotEmpty(orderByClause) ) { + buf.append(" order by ").append(orderByClause); + } + + if (lockOptions.getLockMode() != LockMode.NONE) { + buf.append(dialect.getForUpdateString(lockOptions)); + } + + return dialect.transformSelectString( buf.toString() ); + } + + /** + * Sets the fromClause. + * @param fromClause The fromClause to set + */ + public Select setFromClause(String fromClause) { + this.fromClause = fromClause; + this.guesstimatedBufferSize += fromClause.length(); + return this; + } + + public Select setFromClause(String tableName, String alias) { + this.fromClause = tableName + ' ' + alias; + this.guesstimatedBufferSize += fromClause.length(); + return this; + } + + public Select setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + this.guesstimatedBufferSize += orderByClause.length(); + return this; + } + + public Select setGroupByClause(String groupByClause) { + this.groupByClause = groupByClause; + this.guesstimatedBufferSize += groupByClause.length(); + return this; + } + + public Select setOuterJoins(String outerJoinsAfterFrom, String outerJoinsAfterWhere) { + this.outerJoinsAfterFrom = outerJoinsAfterFrom; + + // strip off any leading 'and' token + String tmpOuterJoinsAfterWhere = outerJoinsAfterWhere.trim(); + if ( tmpOuterJoinsAfterWhere.startsWith("and") ) { + tmpOuterJoinsAfterWhere = tmpOuterJoinsAfterWhere.substring(4); + } + this.outerJoinsAfterWhere = tmpOuterJoinsAfterWhere; + + this.guesstimatedBufferSize += outerJoinsAfterFrom.length() + outerJoinsAfterWhere.length(); + return this; + } + + + /** + * Sets the selectClause. + * @param selectClause The selectClause to set + */ + public Select setSelectClause(String selectClause) { + this.selectClause = selectClause; + this.guesstimatedBufferSize += selectClause.length(); + return this; + } + + public Select setSelectClause(SelectFragment selectFragment) { + setSelectClause( selectFragment.toFragmentString().substring( 2 ) ); + return this; + } + + /** + * Sets the whereClause. + * @param whereClause The whereClause to set + */ + public Select setWhereClause(String whereClause) { + this.whereClause = whereClause; + this.guesstimatedBufferSize += whereClause.length(); + return this; + } + + public Select setComment(String comment) { + this.comment = comment; + this.guesstimatedBufferSize += comment.length(); + return this; + } + + /** + * Get the current lock mode + * @return LockMode + * @deprecated Instead use getLockOptions + */ + @Deprecated + public LockMode getLockMode() { + return lockOptions.getLockMode(); + } + + /** + * Set the lock mode + * @param lockMode + * @return this object + * @deprecated Instead use setLockOptions + */ + @Deprecated + public Select setLockMode(LockMode lockMode) { + lockOptions.setLockMode(lockMode); + return this; + } + + /** + * Get the current lock options + * @return LockOptions + */ + public LockOptions getLockOptions() { + return lockOptions; + } + + /** + * Set the lock options + * @param lockOptions + * @return this object + */ + public Select setLockOptions(LockOptions lockOptions) { + LockOptions.copy(lockOptions, this.lockOptions); + return this; + } +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/SimpleSelect.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/SimpleSelect.java new file mode 100644 index 000000000..cce3611c1 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/SimpleSelect.java @@ -0,0 +1,210 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.sql; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fr.third.org.hibernate.LockMode; +import com.fr.third.org.hibernate.LockOptions; +import com.fr.third.org.hibernate.dialect.Dialect; + +/** + * An SQL SELECT statement with no table joins + * + * @author Gavin King + */ +public class SimpleSelect { + + public SimpleSelect(Dialect dialect) { + this.dialect = dialect; + } + + //private static final Alias DEFAULT_ALIAS = new Alias(10, null); + + private String tableName; + private String orderBy; + private Dialect dialect; + private LockOptions lockOptions = new LockOptions( LockMode.READ ); + private String comment; + + private List columns = new ArrayList(); + private Map aliases = new HashMap(); + private List whereTokens = new ArrayList(); + + public SimpleSelect addColumns(String[] columnNames, String[] columnAliases) { + for ( int i = 0; i < columnNames.length; i++ ) { + if ( columnNames[i] != null ) { + addColumn( columnNames[i], columnAliases[i] ); + } + } + return this; + } + + public SimpleSelect addColumns(String[] columns, String[] aliases, boolean[] ignore) { + for ( int i = 0; i < ignore.length; i++ ) { + if ( !ignore[i] && columns[i] != null ) { + addColumn( columns[i], aliases[i] ); + } + } + return this; + } + + public SimpleSelect addColumns(String[] columnNames) { + for ( String columnName : columnNames ) { + if ( columnName != null ) { + addColumn( columnName ); + } + } + return this; + } + + public SimpleSelect addColumn(String columnName) { + columns.add( columnName ); + //aliases.put( columnName, DEFAULT_ALIAS.toAliasString(columnName) ); + return this; + } + + public SimpleSelect addColumn(String columnName, String alias) { + columns.add( columnName ); + aliases.put( columnName, alias ); + return this; + } + + public SimpleSelect setTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public SimpleSelect setLockOptions(LockOptions lockOptions) { + LockOptions.copy( lockOptions, this.lockOptions ); + return this; + } + + public SimpleSelect setLockMode(LockMode lockMode) { + this.lockOptions.setLockMode( lockMode ); + return this; + } + + public SimpleSelect addWhereToken(String token) { + whereTokens.add( token ); + return this; + } + + private void and() { + if ( whereTokens.size() > 0 ) { + whereTokens.add( "and" ); + } + } + + public SimpleSelect addCondition(String lhs, String op, String rhs) { + and(); + whereTokens.add( lhs + ' ' + op + ' ' + rhs ); + return this; + } + + public SimpleSelect addCondition(String lhs, String condition) { + and(); + whereTokens.add( lhs + ' ' + condition ); + return this; + } + + public SimpleSelect addCondition(String[] lhs, String op, String[] rhs) { + for ( int i = 0; i < lhs.length; i++ ) { + addCondition( lhs[i], op, rhs[i] ); + } + return this; + } + + public SimpleSelect addCondition(String[] lhs, String condition) { + for ( String lh : lhs ) { + if ( lh != null ) { + addCondition( lh, condition ); + } + } + return this; + } + + public String toStatementString() { + StringBuilder buf = new StringBuilder( + columns.size() * 10 + + tableName.length() + + whereTokens.size() * 10 + + 10 + ); + + if ( comment != null ) { + buf.append( "/* " ).append( Dialect.escapeComment(comment) ).append( " */ " ); + } + + buf.append( "select " ); + Set uniqueColumns = new HashSet(); + Iterator iter = columns.iterator(); + boolean appendComma = false; + while ( iter.hasNext() ) { + String col = iter.next(); + String alias = aliases.get( col ); + if ( uniqueColumns.add( alias == null ? col : alias ) ) { + if ( appendComma ) { + buf.append( ", " ); + } + buf.append( col ); + if ( alias != null && !alias.equals( col ) ) { + buf.append( " as " ) + .append( alias ); + } + appendComma = true; + } + } + + buf.append( " from " ) + .append( dialect.appendLockHint( lockOptions, tableName ) ); + + if ( whereTokens.size() > 0 ) { + buf.append( " where " ) + .append( toWhereClause() ); + } + + if ( orderBy != null ) { + buf.append( orderBy ); + } + + if ( lockOptions != null ) { + buf = new StringBuilder(dialect.applyLocksToSql( buf.toString(), lockOptions, null ) ); + } + + return dialect.transformSelectString( buf.toString() ); + } + + public String toWhereClause() { + StringBuilder buf = new StringBuilder( whereTokens.size() * 5 ); + Iterator iter = whereTokens.iterator(); + while ( iter.hasNext() ) { + buf.append( iter.next() ); + if ( iter.hasNext() ) { + buf.append( ' ' ); + } + } + return buf.toString(); + } + + public SimpleSelect setOrderBy(String orderBy) { + this.orderBy = orderBy; + return this; + } + + public SimpleSelect setComment(String comment) { + this.comment = comment; + return this; + } + +} diff --git a/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Update.java b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Update.java new file mode 100644 index 000000000..ede04cad3 --- /dev/null +++ b/fine-hibernate/src/main/java/com/fr/third/org/hibernate/sql/Update.java @@ -0,0 +1,227 @@ +/* + * 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 . + */ +package com.fr.third.org.hibernate.sql; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fr.third.org.hibernate.dialect.Dialect; +import com.fr.third.org.hibernate.type.LiteralType; + +/** + * An SQL UPDATE statement + * + * @author Gavin King + */ +public class Update { + + private String tableName; + private String versionColumnName; + private String where; + private String assignments; + private String comment; + + private Map primaryKeyColumns = new LinkedHashMap(); + private Map columns = new LinkedHashMap(); + private Map whereColumns = new LinkedHashMap(); + + private Dialect dialect; + + public Update(Dialect dialect) { + this.dialect = dialect; + } + + public String getTableName() { + return tableName; + } + + public Update appendAssignmentFragment(String fragment) { + if ( assignments == null ) { + assignments = fragment; + } + else { + assignments += ", " + fragment; + } + return this; + } + + public Update setTableName(String tableName) { + this.tableName = tableName; + return this; + } + + public Update setPrimaryKeyColumnNames(String[] columnNames) { + this.primaryKeyColumns.clear(); + addPrimaryKeyColumns(columnNames); + return this; + } + + public Update addPrimaryKeyColumns(String[] columnNames) { + for ( String columnName : columnNames ) { + addPrimaryKeyColumn( columnName, "?" ); + } + return this; + } + + public Update addPrimaryKeyColumns(String[] columnNames, boolean[] includeColumns, String[] valueExpressions) { + for ( int i=0; iFileScanListener that can be found in the - * SchedulerContext. - * - * @author jhouse - * @see org.quartz.jobs.FileScanListener - */ -public class FileScanJob implements StatefulJob { - - public static String FILE_NAME = "FILE_NAME"; - public static String FILE_SCAN_LISTENER_NAME = "FILE_SCAN_LISTENER_NAME"; - private static String LAST_MODIFIED_TIME = "LAST_MODIFIED_TIME"; - - private final Log log = LogFactory.getLog(getClass()); - - public FileScanJob() { - } - - /** - * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) - */ - public void execute(JobExecutionContext context) throws JobExecutionException { - JobDataMap mergedJobDataMap = context.getMergedJobDataMap(); - SchedulerContext schedCtxt = null; - try { - schedCtxt = context.getScheduler().getContext(); - } catch (SchedulerException e) { - throw new JobExecutionException("Error obtaining scheduler context.", e, false); - } - - String fileName = mergedJobDataMap.getString(FILE_NAME); - String listenerName = mergedJobDataMap.getString(FILE_SCAN_LISTENER_NAME); - - if(fileName == null) { - throw new JobExecutionException("Required parameter '" + - FILE_NAME + "' not found in merged JobDataMap"); - } - if(listenerName == null) { - throw new JobExecutionException("Required parameter '" + - FILE_SCAN_LISTENER_NAME + "' not found in merged JobDataMap"); - } - - FileScanListener listener = (FileScanListener)schedCtxt.get(listenerName); - - if(listener == null) { - throw new JobExecutionException("FileScanListener named '" + - listenerName + "' not found in SchedulerContext"); - } - - long lastDate = -1; - if(mergedJobDataMap.containsKey(LAST_MODIFIED_TIME)) { - lastDate = mergedJobDataMap.getLong(LAST_MODIFIED_TIME); - } - - long newDate = getLastModifiedDate(fileName); - - if(newDate < 0) { - log.warn("File '"+fileName+"' does not exist."); - return; - } - - if(lastDate > 0 && (newDate != lastDate)) { - // notify call back... - log.info("File '"+fileName+"' updated, notifying listener."); - listener.fileUpdated(fileName); - } else if (log.isDebugEnabled()) { - log.debug("File '"+fileName+"' unchanged."); - } - - // It is the JobDataMap on the JobDetail which is actually stateful - context.getJobDetail().getJobDataMap().put(LAST_MODIFIED_TIME, newDate); - } - - protected long getLastModifiedDate(String fileName) { - - File file = new File(fileName); - - if(!file.exists()) { - return -1; - } else { - return file.lastModified(); - } - } -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak deleted file mode 100644 index 4ed568acf..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.simpl; - -import java.util.Iterator; -import java.util.LinkedList; -import java.net.URL; -import java.io.InputStream; - -import org.quartz.spi.ClassLoadHelper; - -/** - * A ClassLoadHelper uses all of the ClassLoadHelper - * types that are found in this package in its attempts to load a class, when - * one scheme is found to work, it is promoted to the scheme that will be used - * first the next time a class is loaded (in order to improve perfomance). - * - *

- * This approach is used because of the wide variance in class loader behavior - * between the various environments in which Quartz runs (e.g. disparate - * application servers, stand-alone, mobile devices, etc.). Because of this - * disparity, Quartz ran into difficulty with a one class-load style fits-all - * design. Thus, this class loader finds the approach that works, then - * 'remembers' it. - *

- * - * @see org.quartz.spi.ClassLoadHelper - * @see org.quartz.simpl.LoadingLoaderClassLoadHelper - * @see org.quartz.simpl.SimpleClassLoadHelper - * @see org.quartz.simpl.ThreadContextClassLoadHelper - * @see org.quartz.simpl.InitThreadContextClassLoadHelper - * - * @author jhouse - */ -public class CascadingClassLoadHelper implements ClassLoadHelper { - - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private LinkedList loadHelpers; - - private ClassLoadHelper bestCandidate; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - public void initialize() { - loadHelpers = new LinkedList(); - - loadHelpers.add(new LoadingLoaderClassLoadHelper()); - loadHelpers.add(new SimpleClassLoadHelper()); - loadHelpers.add(new ThreadContextClassLoadHelper()); - loadHelpers.add(new InitThreadContextClassLoadHelper()); - - Iterator iter = loadHelpers.iterator(); - while (iter.hasNext()) { - ClassLoadHelper loadHelper = (ClassLoadHelper) iter.next(); - loadHelper.initialize(); - } - } - - /** - * Return the class with the given name. - */ - public Class loadClass(String name) throws ClassNotFoundException { - - if (bestCandidate != null) { - try { - return bestCandidate.loadClass(name); - } catch (Exception e) { - bestCandidate = null; - } - } - - ClassNotFoundException cnfe = null; - Class clazz = null; - ClassLoadHelper loadHelper = null; - - Iterator iter = loadHelpers.iterator(); - while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); - - try { - clazz = loadHelper.loadClass(name); - break; - } catch (ClassNotFoundException e) { - cnfe = e; - } - } - - if (clazz == null) { - throw cnfe; - } - - bestCandidate = loadHelper; - - return clazz; - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - public URL getResource(String name) { - - if (bestCandidate != null) { - try { - return bestCandidate.getResource(name); - } catch (Exception e) { - bestCandidate = null; - } - } - - URL result = null; - ClassLoadHelper loadHelper = null; - - Iterator iter = loadHelpers.iterator(); - while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); - - result = loadHelper.getResource(name); - if (result != null) { - break; - } - } - - bestCandidate = loadHelper; - return result; - - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - public InputStream getResourceAsStream(String name) { - - if (bestCandidate != null) { - try { - return bestCandidate.getResourceAsStream(name); - } catch (Exception e) { - bestCandidate = null; - } - } - - InputStream result = null; - ClassLoadHelper loadHelper = null; - - Iterator iter = loadHelpers.iterator(); - while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); - - result = loadHelper.getResourceAsStream(name); - if (result != null) { - break; - } - } - - bestCandidate = loadHelper; - return result; - - } - -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak deleted file mode 100644 index 9c6f84d1e..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.simpl; - -import org.quartz.spi.ClassLoadHelper; - -import java.net.URL; -import java.io.InputStream; - -/** - * A ClassLoadHelper that uses either the context class loader - * of the thread that initialized Quartz. - * - * @see org.quartz.spi.ClassLoadHelper - * @see org.quartz.simpl.ThreadContextClassLoadHelper - * @see org.quartz.simpl.SimpleClassLoadHelper - * @see org.quartz.simpl.CascadingClassLoadHelper - * @see org.quartz.simpl.LoadingLoaderClassLoadHelper - * - * @author jhouse - */ -public class InitThreadContextClassLoadHelper implements ClassLoadHelper { - - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private ClassLoader initClassLoader; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - public void initialize() { - initClassLoader = Thread.currentThread().getContextClassLoader(); - } - - /** - * Return the class with the given name. - */ - public Class loadClass(String name) throws ClassNotFoundException { - return initClassLoader.loadClass(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - public URL getResource(String name) { - return initClassLoader.getResource(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - public InputStream getResourceAsStream(String name) { - return initClassLoader.getResourceAsStream(name); - } -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak deleted file mode 100644 index 8121baa7c..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.simpl; - -import org.quartz.spi.ClassLoadHelper; - -import java.net.URL; -import java.io.InputStream; - -/** - * A ClassLoadHelper that uses either the loader of it's own - * class (this.getClass().getClassLoader().loadClass( .. )). - * - * @see org.quartz.spi.ClassLoadHelper - * @see org.quartz.simpl.InitThreadContextClassLoadHelper - * @see org.quartz.simpl.SimpleClassLoadHelper - * @see org.quartz.simpl.CascadingClassLoadHelper - * - * @author jhouse - */ -public class LoadingLoaderClassLoadHelper implements ClassLoadHelper { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - public void initialize() { - } - - /** - * Return the class with the given name. - */ - public Class loadClass(String name) throws ClassNotFoundException { - return getClassLoader().loadClass(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - public URL getResource(String name) { - return getClassLoader().getResource(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - public InputStream getResourceAsStream(String name) { - return getClassLoader().getResourceAsStream(name); - } - - private ClassLoader getClassLoader() { - return this.getClass().getClassLoader(); - } -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak deleted file mode 100644 index 309a0ff0b..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.simpl; - -import org.quartz.spi.ClassLoadHelper; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Method; -import java.net.URL; -import java.io.InputStream; - -/** - * A ClassLoadHelper that simply calls Class.forName(..). - * - * @see org.quartz.spi.ClassLoadHelper - * @see org.quartz.simpl.ThreadContextClassLoadHelper - * @see org.quartz.simpl.CascadingClassLoadHelper - * @see org.quartz.simpl.LoadingLoaderClassLoadHelper - * - * @author jhouse - */ -public class SimpleClassLoadHelper implements ClassLoadHelper { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - public void initialize() { - } - - /** - * Return the class with the given name. - */ - public Class loadClass(String name) throws ClassNotFoundException { - return Class.forName(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - public URL getResource(String name) { - return getClassLoader().getResource(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - public InputStream getResourceAsStream(String name) { - return getClassLoader().getResourceAsStream(name); - } - - private ClassLoader getClassLoader() { - // To follow the same behavior of Class.forName(...) I had to play - // dirty (Supported by Sun, IBM & BEA JVMs) - // ToDo - Test it more. - try { - // Get a reference to this class' class-loader - ClassLoader cl = this.getClass().getClassLoader(); - // Create a method instance represnting the protected - // getCallerClassLoader method of class ClassLoader - Method mthd = ClassLoader.class.getDeclaredMethod( - "getCallerClassLoader", new Class[0]); - // Make the method accessible. - AccessibleObject.setAccessible(new AccessibleObject[] {mthd}, true); - // Try to get the caller's class-loader - return (ClassLoader)mthd.invoke(cl, new Object[0]); - } catch (Exception all) { - // Use this class' class-loader - return this.getClass().getClassLoader(); - } - } - -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak deleted file mode 100644 index 80a9a3f7d..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.simpl; - -import org.quartz.spi.ClassLoadHelper; - -import java.net.URL; -import java.io.InputStream; - -/** - * A ClassLoadHelper that uses either the current thread's - * context class loader (Thread.currentThread().getContextClassLoader().loadClass( .. )). - * - * @see org.quartz.spi.ClassLoadHelper - * @see org.quartz.simpl.InitThreadContextClassLoadHelper - * @see org.quartz.simpl.SimpleClassLoadHelper - * @see org.quartz.simpl.CascadingClassLoadHelper - * @see org.quartz.simpl.LoadingLoaderClassLoadHelper - * - * @author jhouse - */ -public class ThreadContextClassLoadHelper implements ClassLoadHelper { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - public void initialize() { - } - - /** - * Return the class with the given name. - */ - public Class loadClass(String name) throws ClassNotFoundException { - return getClassLoader().loadClass(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - public URL getResource(String name) { - return getClassLoader().getResource(name); - } - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - public InputStream getResourceAsStream(String name) { - return getClassLoader().getResourceAsStream(name); - } - - - private ClassLoader getClassLoader() { - return Thread.currentThread().getContextClassLoader(); - } -} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ClassLoadHelper.java.bak deleted file mode 100644 index 63a5930f3..000000000 --- a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ClassLoadHelper.java.bak +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2004-2005 OpenSymphony - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - */ - -/* - * Previously Copyright (c) 2001-2004 James House - */ -package org.quartz.spi; - -import java.net.URL; -import java.io.InputStream; - -/** - * An interface for classes wishing to provide the service of loading classes - * and resources within the scheduler... - * - * @author jhouse - */ -public interface ClassLoadHelper { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling - * thread, which is the thread that is initializing Quartz. - */ - void initialize(); - - /** - * Return the class with the given name. - */ - Class loadClass(String name) throws ClassNotFoundException; - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.net.URL object - */ - URL getResource(String name); - - /** - * Finds a resource with a given name. This method returns null if no - * resource with this name is found. - * @param name name of the desired resource - * @return a java.io.InputStream object - */ - InputStream getResourceAsStream(String name); -}