org.hibernate.dialect.DerbyDialect.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.dialect.DerbyDialect.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.dialect;

import java.lang.reflect.Method;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;

import org.hibernate.MappingException;
import org.hibernate.dialect.function.AnsiTrimFunction;
import org.hibernate.dialect.function.DerbyConcatFunction;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.hql.spi.id.local.AfterUseAction;
import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.DerbyCaseFragment;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDerbyDatabaseImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;

import org.jboss.logging.Logger;

/**
 * Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an
 * override for the identity column generator as well as for the case statement
 * issue documented at:
 * http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby
 *
 * @author Simon Johnston
 *
 * @deprecated HHH-6073
 */
@Deprecated
public class DerbyDialect extends DB2Dialect {
    @SuppressWarnings("deprecation")
    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
            DerbyDialect.class.getName());

    private int driverVersionMajor;
    private int driverVersionMinor;
    private final LimitHandler limitHandler;

    /**
     * Constructs a DerbyDialect
     */
    @SuppressWarnings("deprecation")
    public DerbyDialect() {
        super();
        if (this.getClass() == DerbyDialect.class) {
            LOG.deprecatedDerbyDialect();
        }

        registerFunction("concat", new DerbyConcatFunction());
        registerFunction("trim", new AnsiTrimFunction());
        registerColumnType(Types.BLOB, "blob");
        registerDerbyKeywords();
        determineDriverVersion();

        if (driverVersionMajor > 10 || (driverVersionMajor == 10 && driverVersionMinor >= 7)) {
            registerColumnType(Types.BOOLEAN, "boolean");
        }

        this.limitHandler = new DerbyLimitHandler();
    }

    private void determineDriverVersion() {
        try {
            // locate the derby sysinfo class and query its version info
            final Class sysinfoClass = ReflectHelper.classForName("org.apache.derby.tools.sysinfo",
                    this.getClass());
            final Method majorVersionGetter = sysinfoClass.getMethod("getMajorVersion",
                    ReflectHelper.NO_PARAM_SIGNATURE);
            final Method minorVersionGetter = sysinfoClass.getMethod("getMinorVersion",
                    ReflectHelper.NO_PARAM_SIGNATURE);
            driverVersionMajor = (Integer) majorVersionGetter.invoke(null, ReflectHelper.NO_PARAMS);
            driverVersionMinor = (Integer) minorVersionGetter.invoke(null, ReflectHelper.NO_PARAMS);
        } catch (Exception e) {
            LOG.unableToLoadDerbyDriver(e.getMessage());
            driverVersionMajor = -1;
            driverVersionMinor = -1;
        }
    }

    private boolean isTenPointFiveReleaseOrNewer() {
        return driverVersionMajor > 10 || (driverVersionMajor == 10 && driverVersionMinor >= 5);
    }

    @Override
    public String getCrossJoinSeparator() {
        return ", ";
    }

    @Override
    public CaseFragment createCaseFragment() {
        return new DerbyCaseFragment();
    }

    @Override
    public boolean dropConstraints() {
        return true;
    }

    @Override
    public boolean supportsSequences() {
        // technically sequence support was added in 10.6.1.0...
        //
        // The problem though is that I am not exactly sure how to differentiate 10.6.1.0 from any other 10.6.x release.
        //
        // http://db.apache.org/derby/docs/10.0/publishedapi/org/apache/derby/tools/sysinfo.html seems incorrect.  It
        // states that derby's versioning scheme is major.minor.maintenance, but obviously 10.6.1.0 has 4 components
        // to it, not 3.
        //
        // Let alone the fact that it states that versions with the matching major.minor are 'feature
        // compatible' which is clearly not the case here (sequence support is a new feature...)
        return driverVersionMajor > 10 || (driverVersionMajor == 10 && driverVersionMinor >= 6);
    }

    @Override
    public String getQuerySequencesString() {
        if (supportsSequences()) {
            return "select sys.sysschemas.schemaname as sequence_schema, sys.syssequences.* from sys.syssequences left join sys.sysschemas on sys.syssequences.schemaid = sys.sysschemas.schemaid";
        } else {
            return null;
        }
    }

    @Override
    public SequenceInformationExtractor getSequenceInformationExtractor() {
        if (getQuerySequencesString() == null) {
            return SequenceInformationExtractorNoOpImpl.INSTANCE;
        } else {
            return SequenceInformationExtractorDerbyDatabaseImpl.INSTANCE;
        }
    }

    @Override
    public String getSequenceNextValString(String sequenceName) {
        if (supportsSequences()) {
            return "values next value for " + sequenceName;
        } else {
            throw new MappingException("Derby does not support sequence prior to release 10.6.1.0");
        }
    }

    @Override
    public boolean supportsLimit() {
        return isTenPointFiveReleaseOrNewer();
    }

    @Override
    public boolean supportsCommentOn() {
        //HHH-4531
        return false;
    }

    @Override
    @SuppressWarnings("deprecation")
    public boolean supportsLimitOffset() {
        return isTenPointFiveReleaseOrNewer();
    }

    @Override
    public String getForUpdateString() {
        return " for update with rs";
    }

    @Override
    public String getWriteLockString(int timeout) {
        return " for update with rs";
    }

    @Override
    public String getReadLockString(int timeout) {
        return " for read only with rs";
    }

    @Override
    public LimitHandler getLimitHandler() {
        return limitHandler;
    }

    @Override
    public boolean supportsTuplesInSubqueries() {
        return false;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * From Derby 10.5 Docs:
     * <pre>
     * Query
     * [ORDER BY clause]
     * [result offset clause]
     * [fetch first clause]
     * [FOR UPDATE clause]
     * [WITH {RR|RS|CS|UR}]
     * </pre>
     */
    @Override
    public String getLimitString(String query, final int offset, final int limit) {
        final StringBuilder sb = new StringBuilder(query.length() + 50);
        final String normalizedSelect = query.toLowerCase(Locale.ROOT).trim();
        final int forUpdateIndex = normalizedSelect.lastIndexOf("for update");

        if (hasForUpdateClause(forUpdateIndex)) {
            sb.append(query.substring(0, forUpdateIndex - 1));
        } else if (hasWithClause(normalizedSelect)) {
            sb.append(query.substring(0, getWithIndex(query) - 1));
        } else {
            sb.append(query);
        }

        if (offset == 0) {
            sb.append(" fetch first ");
        } else {
            sb.append(" offset ").append(offset).append(" rows fetch next ");
        }

        sb.append(limit).append(" rows only");

        if (hasForUpdateClause(forUpdateIndex)) {
            sb.append(' ');
            sb.append(query.substring(forUpdateIndex));
        } else if (hasWithClause(normalizedSelect)) {
            sb.append(' ').append(query.substring(getWithIndex(query)));
        }
        return sb.toString();
    }

    @Override
    public boolean supportsVariableLimit() {
        // we bind the limit and offset values directly into the sql...
        return false;
    }

    private boolean hasForUpdateClause(int forUpdateIndex) {
        return forUpdateIndex >= 0;
    }

    private boolean hasWithClause(String normalizedSelect) {
        return normalizedSelect.startsWith("with ", normalizedSelect.length() - 7);
    }

    private int getWithIndex(String querySelect) {
        int i = querySelect.lastIndexOf("with ");
        if (i < 0) {
            i = querySelect.lastIndexOf("WITH ");
        }
        return i;
    }

    // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @Override
    public boolean supportsLobValueChangePropogation() {
        return false;
    }

    @Override
    public boolean supportsUnboundedLobLocatorMaterialization() {
        return false;
    }

    private final class DerbyLimitHandler extends AbstractLimitHandler {
        /**
         * {@inheritDoc}
         * <p/>
         * From Derby 10.5 Docs:
         * <pre>
         * Query
         * [ORDER BY clause]
         * [result offset clause]
         * [fetch first clause]
         * [FOR UPDATE clause]
         * [WITH {RR|RS|CS|UR}]
         * </pre>
         */
        @Override
        public String processSql(String sql, RowSelection selection) {
            final StringBuilder sb = new StringBuilder(sql.length() + 50);
            final String normalizedSelect = sql.toLowerCase(Locale.ROOT).trim();
            final int forUpdateIndex = normalizedSelect.lastIndexOf("for update");

            if (hasForUpdateClause(forUpdateIndex)) {
                sb.append(sql.substring(0, forUpdateIndex - 1));
            } else if (hasWithClause(normalizedSelect)) {
                sb.append(sql.substring(0, getWithIndex(sql) - 1));
            } else {
                sb.append(sql);
            }

            if (LimitHelper.hasFirstRow(selection)) {
                sb.append(" offset ").append(selection.getFirstRow()).append(" rows fetch next ");
            } else {
                sb.append(" fetch first ");
            }

            sb.append(getMaxOrLimit(selection)).append(" rows only");

            if (hasForUpdateClause(forUpdateIndex)) {
                sb.append(' ');
                sb.append(sql.substring(forUpdateIndex));
            } else if (hasWithClause(normalizedSelect)) {
                sb.append(' ').append(sql.substring(getWithIndex(sql)));
            }
            return sb.toString();
        }

        @Override
        public boolean supportsLimit() {
            return isTenPointFiveReleaseOrNewer();
        }

        @Override
        @SuppressWarnings("deprecation")
        public boolean supportsLimitOffset() {
            return isTenPointFiveReleaseOrNewer();
        }

        @Override
        public boolean supportsVariableLimit() {
            return false;
        }
    }

    @Override
    public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
            throws SQLException {
        builder.applyIdentifierCasing(dbMetaData);

        builder.applyReservedWords(dbMetaData);

        builder.applyReservedWords(getKeywords());

        builder.setNameQualifierSupport(getNameQualifierSupport());

        return builder.build();
    }

    protected void registerDerbyKeywords() {
        registerKeyword("ADD");
        registerKeyword("ALL");
        registerKeyword("ALLOCATE");
        registerKeyword("ALTER");
        registerKeyword("AND");
        registerKeyword("ANY");
        registerKeyword("ARE");
        registerKeyword("AS");
        registerKeyword("ASC");
        registerKeyword("ASSERTION");
        registerKeyword("AT");
        registerKeyword("AUTHORIZATION");
        registerKeyword("AVG");
        registerKeyword("BEGIN");
        registerKeyword("BETWEEN");
        registerKeyword("BIT");
        registerKeyword("BOOLEAN");
        registerKeyword("BOTH");
        registerKeyword("BY");
        registerKeyword("CALL");
        registerKeyword("CASCADE");
        registerKeyword("CASCADED");
        registerKeyword("CASE");
        registerKeyword("CAST");
        registerKeyword("CHAR");
        registerKeyword("CHARACTER");
        registerKeyword("CHECK");
        registerKeyword("CLOSE");
        registerKeyword("COLLATE");
        registerKeyword("COLLATION");
        registerKeyword("COLUMN");
        registerKeyword("COMMIT");
        registerKeyword("CONNECT");
        registerKeyword("CONNECTION");
        registerKeyword("CONSTRAINT");
        registerKeyword("CONSTRAINTS");
        registerKeyword("CONTINUE");
        registerKeyword("CONVERT");
        registerKeyword("CORRESPONDING");
        registerKeyword("COUNT");
        registerKeyword("CREATE");
        registerKeyword("CURRENT");
        registerKeyword("CURRENT_DATE");
        registerKeyword("CURRENT_TIME");
        registerKeyword("CURRENT_TIMESTAMP");
        registerKeyword("CURRENT_USER");
        registerKeyword("CURSOR");
        registerKeyword("DEALLOCATE");
        registerKeyword("DEC");
        registerKeyword("DECIMAL");
        registerKeyword("DECLARE");
        registerKeyword("DEFERRABLE");
        registerKeyword("DEFERRED");
        registerKeyword("DELETE");
        registerKeyword("DESC");
        registerKeyword("DESCRIBE");
        registerKeyword("DIAGNOSTICS");
        registerKeyword("DISCONNECT");
        registerKeyword("DISTINCT");
        registerKeyword("DOUBLE");
        registerKeyword("DROP");
        registerKeyword("ELSE");
        registerKeyword("END");
        registerKeyword("ENDEXEC");
        registerKeyword("ESCAPE");
        registerKeyword("EXCEPT");
        registerKeyword("EXCEPTION");
        registerKeyword("EXEC");
        registerKeyword("EXECUTE");
        registerKeyword("EXISTS");
        registerKeyword("EXPLAIN");
        registerKeyword("EXTERNAL");
        registerKeyword("FALSE");
        registerKeyword("FETCH");
        registerKeyword("FIRST");
        registerKeyword("FLOAT");
        registerKeyword("FOR");
        registerKeyword("FOREIGN");
        registerKeyword("FOUND");
        registerKeyword("FROM");
        registerKeyword("FULL");
        registerKeyword("FUNCTION");
        registerKeyword("GET");
        registerKeyword("GET_CURRENT_CONNECTION");
        registerKeyword("GLOBAL");
        registerKeyword("GO");
        registerKeyword("GOTO");
        registerKeyword("GRANT");
        registerKeyword("GROUP");
        registerKeyword("HAVING");
        registerKeyword("HOUR");
        registerKeyword("IDENTITY");
        registerKeyword("IMMEDIATE");
        registerKeyword("IN");
        registerKeyword("INDICATOR");
        registerKeyword("INITIALLY");
        registerKeyword("INNER");
        registerKeyword("INOUT");
        registerKeyword("INPUT");
        registerKeyword("INSENSITIVE");
        registerKeyword("INSERT");
        registerKeyword("INT");
        registerKeyword("INTEGER");
        registerKeyword("INTERSECT");
        registerKeyword("INTO");
        registerKeyword("IS");
        registerKeyword("ISOLATION");
        registerKeyword("JOIN");
        registerKeyword("KEY");
        registerKeyword("LAST");
        registerKeyword("LEFT");
        registerKeyword("LIKE");
        registerKeyword("LONGINT");
        registerKeyword("LOWER");
        registerKeyword("LTRIM");
        registerKeyword("MATCH");
        registerKeyword("MAX");
        registerKeyword("MIN");
        registerKeyword("MINUTE");
        registerKeyword("NATIONAL");
        registerKeyword("NATURAL");
        registerKeyword("NCHAR");
        registerKeyword("NVARCHAR");
        registerKeyword("NEXT");
        registerKeyword("NO");
        registerKeyword("NOT");
        registerKeyword("NULL");
        registerKeyword("NULLIF");
        registerKeyword("NUMERIC");
        registerKeyword("OF");
        registerKeyword("ON");
        registerKeyword("ONLY");
        registerKeyword("OPEN");
        registerKeyword("OPTION");
        registerKeyword("OR");
        registerKeyword("ORDER");
        registerKeyword("OUT");
        registerKeyword("OUTER");
        registerKeyword("OUTPUT");
        registerKeyword("OVERLAPS");
        registerKeyword("PAD");
        registerKeyword("PARTIAL");
        registerKeyword("PREPARE");
        registerKeyword("PRESERVE");
        registerKeyword("PRIMARY");
        registerKeyword("PRIOR");
        registerKeyword("PRIVILEGES");
        registerKeyword("PROCEDURE");
        registerKeyword("PUBLIC");
        registerKeyword("READ");
        registerKeyword("REAL");
        registerKeyword("REFERENCES");
        registerKeyword("RELATIVE");
        registerKeyword("RESTRICT");
        registerKeyword("REVOKE");
        registerKeyword("RIGHT");
        registerKeyword("ROLLBACK");
        registerKeyword("ROWS");
        registerKeyword("RTRIM");
        registerKeyword("SCHEMA");
        registerKeyword("SCROLL");
        registerKeyword("SECOND");
        registerKeyword("SELECT");
        registerKeyword("SESSION_USER");
        registerKeyword("SET");
        registerKeyword("SMALLINT");
        registerKeyword("SOME");
        registerKeyword("SPACE");
        registerKeyword("SQL");
        registerKeyword("SQLCODE");
        registerKeyword("SQLERROR");
        registerKeyword("SQLSTATE");
        registerKeyword("SUBSTR");
        registerKeyword("SUBSTRING");
        registerKeyword("SUM");
        registerKeyword("SYSTEM_USER");
        registerKeyword("TABLE");
        registerKeyword("TEMPORARY");
        registerKeyword("TIMEZONE_HOUR");
        registerKeyword("TIMEZONE_MINUTE");
        registerKeyword("TO");
        registerKeyword("TRAILING");
        registerKeyword("TRANSACTION");
        registerKeyword("TRANSLATE");
        registerKeyword("TRANSLATION");
        registerKeyword("TRUE");
        registerKeyword("UNION");
        registerKeyword("UNIQUE");
        registerKeyword("UNKNOWN");
        registerKeyword("UPDATE");
        registerKeyword("UPPER");
        registerKeyword("USER");
        registerKeyword("USING");
        registerKeyword("VALUES");
        registerKeyword("VARCHAR");
        registerKeyword("VARYING");
        registerKeyword("VIEW");
        registerKeyword("WHENEVER");
        registerKeyword("WHERE");
        registerKeyword("WITH");
        registerKeyword("WORK");
        registerKeyword("WRITE");
        registerKeyword("XML");
        registerKeyword("XMLEXISTS");
        registerKeyword("XMLPARSE");
        registerKeyword("XMLSERIALIZE");
        registerKeyword("YEAR");
    }

    /**
     * {@inheritDoc}
     * <p>
     * From Derby docs:
     * <pre>
     *     The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
     * </pre>
     *
     * {@link DB2Dialect} returns a {@link org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy} that
     * will make temporary tables created at startup and hence unavailable for subsequent connections.<br/>
     * see HHH-10238.
     * </p>
      */
    @Override
    public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() {
        return new LocalTemporaryTableBulkIdStrategy(new IdTableSupportStandardImpl() {
            @Override
            public String generateIdTableName(String baseName) {
                return "session." + super.generateIdTableName(baseName);
            }

            @Override
            public String getCreateIdTableCommand() {
                return "declare global temporary table";
            }

            @Override
            public String getCreateIdTableStatementOptions() {
                return "not logged";
            }
        }, AfterUseAction.CLEAN, null);
    }
}