/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.dialect; import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.ConditionalParenthesisFunction; import org.hibernate.dialect.function.ConvertFunction; import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.NvlFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardJDBCEscapeFunction; import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy; import org.hibernate.dialect.lock.OptimisticLockingStrategy; import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy; import org.hibernate.dialect.lock.PessimisticReadUpdateLockingStrategy; import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy; import org.hibernate.dialect.lock.SelectLockingStrategy; import org.hibernate.dialect.lock.UpdateLockingStrategy; import org.hibernate.exception.internal.CacheSQLStateConverter; import org.hibernate.exception.spi.SQLExceptionConverter; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.id.IdentityGenerator; import org.hibernate.internal.util.StringHelper; import org.hibernate.persister.entity.Lockable; import org.hibernate.sql.CacheJoinFragment; import org.hibernate.sql.JoinFragment; import org.hibernate.type.StandardBasicTypes; /** * Caché 2007.1 dialect. This class is required in order to use Hibernate with Intersystems Caché SQL.<br> * <br> * Compatible with Caché 2007.1. * <br> * <head> * <title>Caché and Hibernate</title> * </head> * <body> * <h1>Caché and Hibernate</h1> * <h2>PREREQUISITES</h2> * These setup instructions assume that both Caché and Hibernate are installed and operational. * <br> * <h2>HIBERNATE DIRECTORIES AND FILES</h2> * JBoss distributes the InterSystems Cache' dialect for Hibernate 3.2.1 * For earlier versions of Hibernate please contact * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC) * for the appropriate source files. * <br> * <h2>CACHÉ DOCUMENTATION</h2> * Documentation for Caché is available online when Caché is running. * It can also be obtained from the * <a href="http://www.intersystems.com/cache/downloads/documentation.html">InterSystems</A> website. * The book, "Object-oriented Application Development Using the Caché Post-relational Database: * is also available from Springer-Verlag. * <br> * <h2>HIBERNATE DOCUMENTATION</h2> * Hibernate comes with extensive electronic documentation. * In addition, several books on Hibernate are available from * <a href="http://www.manning.com">Manning Publications Co</a>. * Three available titles are "Hibernate Quickly", "Hibernate in Action", and "Java Persistence with Hibernate". * <br> * <h2>TO SET UP HIBERNATE FOR USE WITH CACHÉ</h2> * The following steps assume that the directory where Caché was installed is C:\CacheSys. * This is the default installation directory for Caché. * The default installation directory for Hibernate is assumed to be C:\Hibernate. * <p/> * If either product is installed in a different location, the pathnames that follow should be modified appropriately. * <p/> * Caché version 2007.1 and above is recommended for use with * Hibernate. The next step depends on the location of your * CacheDB.jar depending on your version of Caché. * <ol> * <li>Copy C:\CacheSys\dev\java\lib\JDK15\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li> * <p/> * <li>Insert the following files into your Java classpath: * <p/> * <ul> * <li>All jar files in the directory C:\Hibernate\lib</li> * <li>The directory (or directories) where hibernate.properties and/or hibernate.cfg.xml are kept.</li> * </ul> * </li> * <p/> * <li>In the file, hibernate.properties (or hibernate.cfg.xml), * specify the Caché dialect and the Caché version URL settings.</li> * </ol> * <p/> * For example, in Hibernate 3.2, typical entries in hibernate.properties would have the following * "name=value" pairs: * <p/> * <table cols=3 border cellpadding=5 cellspacing=0> * <tr> * <th>Property Name</th> * <th>Property Value</th> * </tr> * <tr> * <td>hibernate.dialect</td> * <td>org.hibernate.dialect.Cache71Dialect</td> * </tr> * <tr> * <td>hibernate.connection.driver_class</td> * <td>com.intersys.jdbc.CacheDriver</td> * </tr> * <tr> * <td>hibernate.connection.username</td> * <td>(see note 1)</td> * </tr> * <tr> * <td>hibernate.connection.password</td> * <td>(see note 1)</td> * </tr> * <tr> * <td>hibernate.connection.url</td> * <td>jdbc:Cache://127.0.0.1:1972/USER</td> * </tr> * </table> * <p/> * <dl> * <dt><b>Note 1</b></dt> * <dd>Please contact your administrator for the userid and password you should use when attempting access via JDBC. * By default, these are chosen to be "_SYSTEM" and "SYS" respectively as noted in the SQL standard.</dd> * </dl> * <br> * <h2>CACHÉ VERSION URL</h2> * This is the standard URL for the JDBC driver. * For a JDBC driver on the machine hosting Caché, use the IP "loopback" address, 127.0.0.1. * For 1972, the default port, specify the super server port of your Caché instance. * For USER, substitute the NAMESPACE which contains your Caché database data. * <br> * <h2>CACHÉ DIALECTS</h2> * Choices for Dialect are: * <br> * <p/> * <ol> * <li>org.hibernate.dialect.Cache71Dialect (requires Caché * 2007.1 or above)</li> * <p/> * </ol> * <br> * <h2>SUPPORT FOR IDENTITY COLUMNS</h2> * Caché 2007.1 or later supports identity columns. For * Hibernate to use identity columns, specify "native" as the * generator. * <br> * <h2>SEQUENCE DIALECTS SUPPORT SEQUENCES</h2> * <p/> * To use Hibernate sequence support with Caché in a namespace, you must FIRST load the following file into that namespace: * <pre> * etc\CacheSequences.xml * </pre> * For example, at the COS terminal prompt in the namespace, run the * following command: * <p> * d LoadFile^%apiOBJ("c:\hibernate\etc\CacheSequences.xml","ck") * <p> * In your Hibernate mapping you can specify sequence use. * <p> * For example, the following shows the use of a sequence generator in a Hibernate mapping: * <pre> * <id name="id" column="uid" type="long" unsaved-value="null"> * <generator class="sequence"/> * </id> * </pre> * <br> * <p/> * Some versions of Hibernate under some circumstances call * getSelectSequenceNextValString() in the dialect. If this happens * you will receive the error message: new MappingException( "Dialect * does not support sequences" ). * <br> * <h2>HIBERNATE FILES ASSOCIATED WITH CACHÉ DIALECT</h2> * The following files are associated with Caché dialect: * <p/> * <ol> * <li>src\org\hibernate\dialect\Cache71Dialect.java</li> * <li>src\org\hibernate\dialect\function\ConditionalParenthesisFunction.java</li> * <li>src\org\hibernate\dialect\function\ConvertFunction.java</li> * <li>src\org\hibernate\exception\CacheSQLStateConverter.java</li> * <li>src\org\hibernate\sql\CacheJoinFragment.java</li> * </ol> * Cache71Dialect ships with Hibernate 3.2. All other dialects are distributed by InterSystems and subclass Cache71Dialect. * * @author Jonathan Levinson */ public class Cache71Dialect extends Dialect { /** * Creates new <code>Cache71Dialect</code> instance. Sets up the JDBC / * Caché type mappings. */ public Cache71Dialect() { super(); commonRegistration(); register71Functions(); } protected final void commonRegistration() { // Note: For object <-> SQL datatype mappings see: // Configuration Manager | Advanced | SQL | System DDL Datatype Mappings // // TBD registerColumnType(Types.BINARY, "binary($1)"); // changed 08-11-2005, jsl registerColumnType( Types.BINARY, "varbinary($1)" ); registerColumnType( Types.BIGINT, "BigInt" ); registerColumnType( Types.BIT, "bit" ); registerColumnType( Types.CHAR, "char(1)" ); registerColumnType( Types.DATE, "date" ); registerColumnType( Types.DECIMAL, "decimal" ); registerColumnType( Types.DOUBLE, "double" ); registerColumnType( Types.FLOAT, "float" ); registerColumnType( Types.INTEGER, "integer" ); registerColumnType( Types.LONGVARBINARY, "longvarbinary" ); // binary %Stream registerColumnType( Types.LONGVARCHAR, "longvarchar" ); // character %Stream registerColumnType( Types.NUMERIC, "numeric($p,$s)" ); registerColumnType( Types.REAL, "real" ); registerColumnType( Types.SMALLINT, "smallint" ); registerColumnType( Types.TIMESTAMP, "timestamp" ); registerColumnType( Types.TIME, "time" ); registerColumnType( Types.TINYINT, "tinyint" ); // TBD should this be varbinary($1)? // registerColumnType(Types.VARBINARY, "binary($1)"); registerColumnType( Types.VARBINARY, "longvarbinary" ); registerColumnType( Types.VARCHAR, "varchar($l)" ); registerColumnType( Types.BLOB, "longvarbinary" ); registerColumnType( Types.CLOB, "longvarchar" ); getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); //getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH); getDefaultProperties().setProperty( Environment.USE_SQL_COMMENTS, "false" ); registerFunction( "abs", new StandardSQLFunction( "abs" ) ); registerFunction( "acos", new StandardJDBCEscapeFunction( "acos", StandardBasicTypes.DOUBLE ) ); registerFunction( "%alphaup", new StandardSQLFunction( "%alphaup", StandardBasicTypes.STRING ) ); registerFunction( "ascii", new StandardSQLFunction( "ascii", StandardBasicTypes.STRING ) ); registerFunction( "asin", new StandardJDBCEscapeFunction( "asin", StandardBasicTypes.DOUBLE ) ); registerFunction( "atan", new StandardJDBCEscapeFunction( "atan", StandardBasicTypes.DOUBLE ) ); registerFunction( "bit_length", new SQLFunctionTemplate( StandardBasicTypes.INTEGER, "($length(?1)*8)" ) ); // hibernate impelemnts cast in Dialect.java registerFunction( "ceiling", new StandardSQLFunction( "ceiling", StandardBasicTypes.INTEGER ) ); registerFunction( "char", new StandardJDBCEscapeFunction( "char", StandardBasicTypes.CHARACTER ) ); registerFunction( "character_length", new StandardSQLFunction( "character_length", StandardBasicTypes.INTEGER ) ); registerFunction( "char_length", new StandardSQLFunction( "char_length", StandardBasicTypes.INTEGER ) ); registerFunction( "cos", new StandardJDBCEscapeFunction( "cos", StandardBasicTypes.DOUBLE ) ); registerFunction( "cot", new StandardJDBCEscapeFunction( "cot", StandardBasicTypes.DOUBLE ) ); registerFunction( "coalesce", new VarArgsSQLFunction( "coalesce(", ",", ")" ) ); registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "", "||", "" ) ); registerFunction( "convert", new ConvertFunction() ); registerFunction( "curdate", new StandardJDBCEscapeFunction( "curdate", StandardBasicTypes.DATE ) ); registerFunction( "current_date", new NoArgSQLFunction( "current_date", StandardBasicTypes.DATE, false ) ); registerFunction( "current_time", new NoArgSQLFunction( "current_time", StandardBasicTypes.TIME, false ) ); registerFunction( "current_timestamp", new ConditionalParenthesisFunction( "current_timestamp", StandardBasicTypes.TIMESTAMP ) ); registerFunction( "curtime", new StandardJDBCEscapeFunction( "curtime", StandardBasicTypes.TIME ) ); registerFunction( "database", new StandardJDBCEscapeFunction( "database", StandardBasicTypes.STRING ) ); registerFunction( "dateadd", new VarArgsSQLFunction( StandardBasicTypes.TIMESTAMP, "dateadd(", ",", ")" ) ); registerFunction( "datediff", new VarArgsSQLFunction( StandardBasicTypes.INTEGER, "datediff(", ",", ")" ) ); registerFunction( "datename", new VarArgsSQLFunction( StandardBasicTypes.STRING, "datename(", ",", ")" ) ); registerFunction( "datepart", new VarArgsSQLFunction( StandardBasicTypes.INTEGER, "datepart(", ",", ")" ) ); registerFunction( "day", new StandardSQLFunction( "day", StandardBasicTypes.INTEGER ) ); registerFunction( "dayname", new StandardJDBCEscapeFunction( "dayname", StandardBasicTypes.STRING ) ); registerFunction( "dayofmonth", new StandardJDBCEscapeFunction( "dayofmonth", StandardBasicTypes.INTEGER ) ); registerFunction( "dayofweek", new StandardJDBCEscapeFunction( "dayofweek", StandardBasicTypes.INTEGER ) ); registerFunction( "dayofyear", new StandardJDBCEscapeFunction( "dayofyear", StandardBasicTypes.INTEGER ) ); // is it necessary to register %exact since it can only appear in a where clause? registerFunction( "%exact", new StandardSQLFunction( "%exact", StandardBasicTypes.STRING ) ); registerFunction( "exp", new StandardJDBCEscapeFunction( "exp", StandardBasicTypes.DOUBLE ) ); registerFunction( "%external", new StandardSQLFunction( "%external", StandardBasicTypes.STRING ) ); registerFunction( "$extract", new VarArgsSQLFunction( StandardBasicTypes.INTEGER, "$extract(", ",", ")" ) ); registerFunction( "$find", new VarArgsSQLFunction( StandardBasicTypes.INTEGER, "$find(", ",", ")" ) ); registerFunction( "floor", new StandardSQLFunction( "floor", StandardBasicTypes.INTEGER ) ); registerFunction( "getdate", new StandardSQLFunction( "getdate", StandardBasicTypes.TIMESTAMP ) ); registerFunction( "hour", new StandardJDBCEscapeFunction( "hour", StandardBasicTypes.INTEGER ) ); registerFunction( "ifnull", new VarArgsSQLFunction( "ifnull(", ",", ")" ) ); registerFunction( "%internal", new StandardSQLFunction( "%internal" ) ); registerFunction( "isnull", new VarArgsSQLFunction( "isnull(", ",", ")" ) ); registerFunction( "isnumeric", new StandardSQLFunction( "isnumeric", StandardBasicTypes.INTEGER ) ); registerFunction( "lcase", new StandardJDBCEscapeFunction( "lcase", StandardBasicTypes.STRING ) ); registerFunction( "left", new StandardJDBCEscapeFunction( "left", StandardBasicTypes.STRING ) ); registerFunction( "len", new StandardSQLFunction( "len", StandardBasicTypes.INTEGER ) ); registerFunction( "$length", new VarArgsSQLFunction( "$length(", ",", ")" ) ); // aggregate functions shouldn't be registered, right? //registerFunction( "list", new StandardSQLFunction("list",StandardBasicTypes.STRING) ); // stopped on $list registerFunction( "$list", new VarArgsSQLFunction( "$list(", ",", ")" ) ); registerFunction( "$listdata", new VarArgsSQLFunction( "$listdata(", ",", ")" ) ); registerFunction( "$listfind", new VarArgsSQLFunction( "$listfind(", ",", ")" ) ); registerFunction( "$listget", new VarArgsSQLFunction( "$listget(", ",", ")" ) ); registerFunction( "$listlength", new StandardSQLFunction( "$listlength", StandardBasicTypes.INTEGER ) ); registerFunction( "locate", new StandardSQLFunction( "$FIND", StandardBasicTypes.INTEGER ) ); registerFunction( "log", new StandardJDBCEscapeFunction( "log", StandardBasicTypes.DOUBLE ) ); registerFunction( "log10", new StandardJDBCEscapeFunction( "log", StandardBasicTypes.DOUBLE ) ); registerFunction( "lower", new StandardSQLFunction( "lower" ) ); registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) ); registerFunction( "minute", new StandardJDBCEscapeFunction( "minute", StandardBasicTypes.INTEGER ) ); registerFunction( "mod", new StandardJDBCEscapeFunction( "mod", StandardBasicTypes.DOUBLE ) ); registerFunction( "month", new StandardJDBCEscapeFunction( "month", StandardBasicTypes.INTEGER ) ); registerFunction( "monthname", new StandardJDBCEscapeFunction( "monthname", StandardBasicTypes.STRING ) ); registerFunction( "now", new StandardJDBCEscapeFunction( "monthname", StandardBasicTypes.TIMESTAMP ) ); registerFunction( "nullif", new VarArgsSQLFunction( "nullif(", ",", ")" ) ); registerFunction( "nvl", new NvlFunction() ); registerFunction( "%odbcin", new StandardSQLFunction( "%odbcin" ) ); registerFunction( "%odbcout", new StandardSQLFunction( "%odbcin" ) ); registerFunction( "%pattern", new VarArgsSQLFunction( StandardBasicTypes.STRING, "", "%pattern", "" ) ); registerFunction( "pi", new StandardJDBCEscapeFunction( "pi", StandardBasicTypes.DOUBLE ) ); registerFunction( "$piece", new VarArgsSQLFunction( StandardBasicTypes.STRING, "$piece(", ",", ")" ) ); registerFunction( "position", new VarArgsSQLFunction( StandardBasicTypes.INTEGER, "position(", " in ", ")" ) ); registerFunction( "power", new VarArgsSQLFunction( StandardBasicTypes.STRING, "power(", ",", ")" ) ); registerFunction( "quarter", new StandardJDBCEscapeFunction( "quarter", StandardBasicTypes.INTEGER ) ); registerFunction( "repeat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "repeat(", ",", ")" ) ); registerFunction( "replicate", new VarArgsSQLFunction( StandardBasicTypes.STRING, "replicate(", ",", ")" ) ); registerFunction( "right", new StandardJDBCEscapeFunction( "right", StandardBasicTypes.STRING ) ); registerFunction( "round", new VarArgsSQLFunction( StandardBasicTypes.FLOAT, "round(", ",", ")" ) ); registerFunction( "rtrim", new StandardSQLFunction( "rtrim", StandardBasicTypes.STRING ) ); registerFunction( "second", new StandardJDBCEscapeFunction( "second", StandardBasicTypes.INTEGER ) ); registerFunction( "sign", new StandardSQLFunction( "sign", StandardBasicTypes.INTEGER ) ); registerFunction( "sin", new StandardJDBCEscapeFunction( "sin", StandardBasicTypes.DOUBLE ) ); registerFunction( "space", new StandardSQLFunction( "space", StandardBasicTypes.STRING ) ); registerFunction( "%sqlstring", new VarArgsSQLFunction( StandardBasicTypes.STRING, "%sqlstring(", ",", ")" ) ); registerFunction( "%sqlupper", new VarArgsSQLFunction( StandardBasicTypes.STRING, "%sqlupper(", ",", ")" ) ); registerFunction( "sqrt", new StandardJDBCEscapeFunction( "SQRT", StandardBasicTypes.DOUBLE ) ); registerFunction( "%startswith", new VarArgsSQLFunction( StandardBasicTypes.STRING, "", "%startswith", "" ) ); // below is for Cache' that don't have str in 2007.1 there is str and we register str directly registerFunction( "str", new SQLFunctionTemplate( StandardBasicTypes.STRING, "cast(?1 as char varying)" ) ); registerFunction( "string", new VarArgsSQLFunction( StandardBasicTypes.STRING, "string(", ",", ")" ) ); // note that %string is deprecated registerFunction( "%string", new VarArgsSQLFunction( StandardBasicTypes.STRING, "%string(", ",", ")" ) ); registerFunction( "substr", new VarArgsSQLFunction( StandardBasicTypes.STRING, "substr(", ",", ")" ) ); registerFunction( "substring", new VarArgsSQLFunction( StandardBasicTypes.STRING, "substring(", ",", ")" ) ); registerFunction( "sysdate", new NoArgSQLFunction( "sysdate", StandardBasicTypes.TIMESTAMP, false ) ); registerFunction( "tan", new StandardJDBCEscapeFunction( "tan", StandardBasicTypes.DOUBLE ) ); registerFunction( "timestampadd", new StandardJDBCEscapeFunction( "timestampadd", StandardBasicTypes.DOUBLE ) ); registerFunction( "timestampdiff", new StandardJDBCEscapeFunction( "timestampdiff", StandardBasicTypes.DOUBLE ) ); registerFunction( "tochar", new VarArgsSQLFunction( StandardBasicTypes.STRING, "tochar(", ",", ")" ) ); registerFunction( "to_char", new VarArgsSQLFunction( StandardBasicTypes.STRING, "to_char(", ",", ")" ) ); registerFunction( "todate", new VarArgsSQLFunction( StandardBasicTypes.STRING, "todate(", ",", ")" ) ); registerFunction( "to_date", new VarArgsSQLFunction( StandardBasicTypes.STRING, "todate(", ",", ")" ) ); registerFunction( "tonumber", new StandardSQLFunction( "tonumber" ) ); registerFunction( "to_number", new StandardSQLFunction( "tonumber" ) ); // TRIM(end_keyword string-expression-1 FROM string-expression-2) // use Hibernate implementation "From" is one of the parameters they pass in position ?3 //registerFunction( "trim", new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1 ?2 from ?3)") ); registerFunction( "truncate", new StandardJDBCEscapeFunction( "truncate", StandardBasicTypes.STRING ) ); registerFunction( "ucase", new StandardJDBCEscapeFunction( "ucase", StandardBasicTypes.STRING ) ); registerFunction( "upper", new StandardSQLFunction( "upper" ) ); // %upper is deprecated registerFunction( "%upper", new StandardSQLFunction( "%upper" ) ); registerFunction( "user", new StandardJDBCEscapeFunction( "user", StandardBasicTypes.STRING ) ); registerFunction( "week", new StandardJDBCEscapeFunction( "user", StandardBasicTypes.INTEGER ) ); registerFunction( "xmlconcat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "xmlconcat(", ",", ")" ) ); registerFunction( "xmlelement", new VarArgsSQLFunction( StandardBasicTypes.STRING, "xmlelement(", ",", ")" ) ); // xmlforest requires a new kind of function constructor registerFunction( "year", new StandardJDBCEscapeFunction( "year", StandardBasicTypes.INTEGER ) ); } protected final void register71Functions() { this.registerFunction( "str", new VarArgsSQLFunction( StandardBasicTypes.STRING, "str(", ",", ")" ) ); } // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean hasAlterTable() { // Does this dialect support the ALTER TABLE syntax? return true; } public boolean qualifyIndexName() { // Do we need to qualify index names with the schema name? return false; } public boolean supportsUnique() { // Does this dialect support the UNIQUE column syntax? return true; } /** * The syntax used to add a foreign key constraint to a table. * * @return String */ public String getAddForeignKeyConstraintString( String constraintName, String[] foreignKey, String referencedTable, String[] primaryKey, boolean referencesPrimaryKey) { // The syntax used to add a foreign key constraint to a table. return new StringBuffer( 300 ) .append( " ADD CONSTRAINT " ) .append( constraintName ) .append( " FOREIGN KEY " ) .append( constraintName ) .append( " (" ) .append( StringHelper.join( ", ", foreignKey ) ) // identifier-commalist .append( ") REFERENCES " ) .append( referencedTable ) .append( " (" ) .append( StringHelper.join( ", ", primaryKey ) ) // identifier-commalist .append( ") " ) .toString(); } public boolean supportsCheck() { // Does this dialect support check constraints? return false; } public String getAddColumnString() { // The syntax used to add a column to a table return " add column"; } public String getCascadeConstraintsString() { // Completely optional cascading drop clause. return ""; } public boolean dropConstraints() { // Do we need to drop constraints before dropping tables in this dialect? return true; } public boolean supportsCascadeDelete() { return true; } public boolean hasSelfReferentialForeignKeyBug() { return true; } // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsTemporaryTables() { return true; } public String generateTemporaryTableName(String baseTableName) { String name = super.generateTemporaryTableName( baseTableName ); return name.length() > 25 ? name.substring( 1, 25 ) : name; } public String getCreateTemporaryTableString() { return "create global temporary table"; } public Boolean performTemporaryTableDDLInIsolation() { return Boolean.FALSE; } public String getCreateTemporaryTablePostfix() { return ""; } public boolean dropTemporaryTableAfterUse() { return true; } // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsIdentityColumns() { return true; } public Class getNativeIdentifierGeneratorClass() { return IdentityGenerator.class; } public boolean hasDataTypeInIdentityColumn() { // Whether this dialect has an Identity clause added to the data type or a completely seperate identity // data type return true; } public String getIdentityColumnString() throws MappingException { // The keyword used to specify an identity column, if identity column key generation is supported. return "identity"; } public String getIdentitySelectString() { return "SELECT LAST_IDENTITY() FROM %TSQL_sys.snf"; } // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsSequences() { return false; } // It really does support sequences, but InterSystems elects to suggest usage of IDENTITY instead :/ // Anyway, below are the actual support overrides for users wanting to use this combo... // // public String getSequenceNextValString(String sequenceName) { // return "select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')"; // } // // public String getSelectSequenceNextValString(String sequenceName) { // return "(select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "'))"; // } // // public String getCreateSequenceString(String sequenceName) { // return "insert into InterSystems.Sequences(Name) values (ucase('" + sequenceName + "'))"; // } // // public String getDropSequenceString(String sequenceName) { // return "delete from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')"; // } // // public String getQuerySequencesString() { // return "select name from InterSystems.Sequences"; // } // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsForUpdate() { // Does this dialect support the FOR UPDATE syntax? return false; } public boolean supportsForUpdateOf() { // Does this dialect support FOR UPDATE OF, allowing particular rows to be locked? return false; } public boolean supportsForUpdateNowait() { // Does this dialect support the Oracle-style FOR UPDATE NOWAIT syntax? return false; } public boolean supportsOuterJoinForUpdate() { return false; } public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) { // InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax... // Set your transaction mode to READ_COMMITTED before using if ( lockMode==LockMode.PESSIMISTIC_FORCE_INCREMENT) { return new PessimisticForceIncrementLockingStrategy( lockable, lockMode); } else if ( lockMode==LockMode.PESSIMISTIC_WRITE) { return new PessimisticWriteUpdateLockingStrategy( lockable, lockMode); } else if ( lockMode==LockMode.PESSIMISTIC_READ) { return new PessimisticReadUpdateLockingStrategy( lockable, lockMode); } else if ( lockMode==LockMode.OPTIMISTIC) { return new OptimisticLockingStrategy( lockable, lockMode); } else if ( lockMode==LockMode.OPTIMISTIC_FORCE_INCREMENT) { return new OptimisticForceIncrementLockingStrategy( lockable, lockMode); } else if ( lockMode.greaterThan( LockMode.READ ) ) { return new UpdateLockingStrategy( lockable, lockMode ); } else { return new SelectLockingStrategy( lockable, lockMode ); } } // LIMIT support (ala TOP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsLimit() { return true; } public boolean supportsLimitOffset() { return false; } public boolean supportsVariableLimit() { return true; } public boolean bindLimitParametersFirst() { // Does the LIMIT clause come at the start of the SELECT statement, rather than at the end? return true; } public boolean useMaxForLimit() { // Does the LIMIT clause take a "maximum" row number instead of a total number of returned rows? return true; } public String getLimitString(String sql, boolean hasOffset) { if ( hasOffset ) { throw new UnsupportedOperationException( "query result offset is not supported" ); } // This does not support the Cache SQL 'DISTINCT BY (comma-list)' extensions, // but this extension is not supported through Hibernate anyway. int insertionPoint = sql.startsWith( "select distinct" ) ? 15 : 6; return new StringBuffer( sql.length() + 8 ) .append( sql ) .insert( insertionPoint, " TOP ? " ) .toString(); } // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException { return col; } public ResultSet getResultSet(CallableStatement ps) throws SQLException { ps.execute(); return ( ResultSet ) ps.getObject( 1 ); } // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public String getLowercaseFunction() { // The name of the SQL function that transforms a string to lowercase return "lower"; } public String getNullColumnString() { // The keyword used to specify a nullable column. return " null"; } public JoinFragment createOuterJoinFragment() { // Create an OuterJoinGenerator for this dialect. return new CacheJoinFragment(); } public String getNoColumnsInsertString() { // The keyword used to insert a row without specifying // any column values return " default values"; } public SQLExceptionConverter buildSQLExceptionConverter() { return new CacheSQLStateConverter( EXTRACTER ); } public static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() { /** * Extract the name of the violated constraint from the given SQLException. * * @param sqle The exception that was the result of the constraint violation. * @return The extracted constraint name. */ public String extractConstraintName(SQLException sqle) { return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() ); } }; // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean supportsEmptyInList() { return false; } public boolean areStringComparisonsCaseInsensitive() { return true; } public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() { return false; } }