Package fi.evident.dalesbred.dialects

Source Code of fi.evident.dalesbred.dialects.Dialect

/*
* Copyright (c) 2012 Evident Solutions Oy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package fi.evident.dalesbred.dialects;

import fi.evident.dalesbred.*;
import fi.evident.dalesbred.connection.ConnectionProvider;
import fi.evident.dalesbred.connection.DataSourceConnectionProvider;
import fi.evident.dalesbred.instantiation.TypeConversion;
import fi.evident.dalesbred.instantiation.TypeConversionRegistry;
import fi.evident.dalesbred.tx.TransactionManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Logger;

/**
* Abstracts away the differences of databases.
*/
public abstract class Dialect {

    private static final String SERIALIZATION_FAILURE = "40001";

    private static final Logger log = Logger.getLogger(Dialect.class.getName());

    @NotNull
    private EnumMode enumMode = EnumMode.NATIVE;

    @NotNull
    public Object valueToDatabase(@NotNull Object value) {
        if (value instanceof Enum<?>)
            return createDatabaseEnum((Enum<?>) value);
        else
            return value;
    }

    /**
     * Returns a database representation for given enum-value.
     */
    @NotNull
    private Object createDatabaseEnum(@NotNull Enum<?> value) {
        switch (enumMode) {
            case NAME:
                return value.name();
            case ORDINAL:
                return value.ordinal();
            case NATIVE:
                return createNativeDatabaseEnum(value);
        }

        throw new IllegalStateException("unknown enumMode: " + enumMode);
    }

    @NotNull
    private <T extends Enum<T>> T parseDatabaseEnum(@NotNull Class<T> enumType, @NotNull Object value) {
        switch (enumMode) {
            case NAME:
                return Enum.valueOf(enumType, value.toString());
            case ORDINAL:
                return enumByOrdinal(enumType, ((Number) value).intValue());
            case NATIVE:
                return parseNativeDatabaseEnum(enumType, value);
        }

        throw new IllegalStateException("unknown enumMode: " + enumMode);
    }

    @NotNull
    private static <T extends Enum<T>> T enumByOrdinal(@NotNull Class<T> enumType, int ordinal) {
        Enum<?>[] constants = enumType.getEnumConstants();
        if (ordinal >= 0 && ordinal < constants.length)
            return enumType.cast(constants[ordinal]);
        else
            throw new DatabaseException("invalid ordinal " + ordinal + " for enum type " + enumType.getName());
    }

    @NotNull
    protected Object createNativeDatabaseEnum(@NotNull Enum<?> value) {
        return value.name();
    }

    @NotNull
    protected <T extends Enum<T>> T parseNativeDatabaseEnum(@NotNull Class<T> enumType, @NotNull Object value) {
        return Enum.valueOf(enumType, value.toString());
    }

    @NotNull
    public <T extends Enum<T>> TypeConversion<Object, T> getEnumCoercion(@NotNull final Class<T> enumType) {
        return new TypeConversion<Object, T>(Object.class, enumType) {
            @NotNull
            @Override
            public T convert(@NotNull Object value) {
                return parseDatabaseEnum(enumType, value);
            }

            @NotNull
            @Override
            public String toString() {
                return "EnumCoercion [" + enumType.getName() + ']';
            }
        };
    }

    @Override
    @NotNull
    public String toString() {
        return getClass().getName();
    }

    @NotNull
    public static Dialect detect(@NotNull DataSource dataSource) {
        return detect(new DataSourceConnectionProvider(dataSource));
    }

    @NotNull
    public static Dialect detect(@NotNull TransactionManager transactionManager) {
        return transactionManager.withTransaction(new TransactionSettings(), new TransactionCallback<Dialect>() {
            @Override
            public Dialect execute(@NotNull TransactionContext tx) {
                return detect(tx.getConnection());
            }
        }, new DefaultDialect());
    }

    @NotNull
    public static Dialect detect(@NotNull ConnectionProvider connectionProvider) {
        try {
            Connection connection = connectionProvider.getConnection();
            try {
                return detect(connection);
            } finally {
                connectionProvider.releaseConnection(connection);
            }
        } catch (SQLException e) {
            throw new DatabaseSQLException("Failed to auto-detect database dialect: " + e, e);
        }
    }

    @NotNull
    public static Dialect detect(@NotNull Connection connection) {
        try {
            String productName = connection.getMetaData().getDatabaseProductName();

            if (productName.equals("PostgreSQL")) {
                log.fine("Automatically detected dialect PostgreSQL.");
                return new PostgreSQLDialect();

            } else if (productName.equals("HSQL Database Engine")) {
                log.fine("Automatically detected dialect HSQLDB.");
                return new HsqldbDialect();

            } else if (productName.equals("MySQL")) {
                log.fine("Automatically detected dialect MySQL.");
                return new MySQLDialect();

            } else if (productName.equals("Oracle")) {
                log.fine("Automatically detected dialect Oracle.");
                return new OracleDialect();

            } else if (productName.equals("Microsoft SQL Server")) {
                log.fine("Automatically detected dialect SQLServer.");
                return new SQLServerDialect();

            } else {
                log.info("Could not detect dialect for product name '" + productName + "', falling back to default.");
                return new DefaultDialect();
            }
        } catch (SQLException e) {
            throw new DatabaseSQLException("Failed to auto-detect database dialect: " + e, e);
        }
    }

    @NotNull
    public DatabaseException convertException(@NotNull SQLException e) {
        String sqlState = e.getSQLState();
        if (sqlState == null)
            return new DatabaseSQLException(e);

        if (sqlState.equals(SERIALIZATION_FAILURE))
            return new TransactionSerializationException(e);
        else if (sqlState.startsWith("40"))
            return new TransactionRollbackException(e);
        else
            return new DatabaseSQLException(e);
    }

    public void registerTypeConversions(@NotNull TypeConversionRegistry typeConversionRegistry) {

    }

    /**
     * Sets the way enumerations are persisted.
     */
    public void setEnumMode(@NotNull EnumMode enumMode) {
        this.enumMode = enumMode;
    }

    /**
     * Gets the way enumerations are persisted.
     */
    @NotNull
    public EnumMode getEnumMode() {
        return enumMode;
    }

    /**
     * Bind object to {@link java.sql.PreparedStatement}. Can be overridden by subclasses to
     * implement custom argument binding.
     *
     * @param ps statement to bind object to
     * @param index index of the parameter
     * @param value to bind
     * @throws SQLException if something fails
     */
    public void bindArgument(@NotNull PreparedStatement ps, int index, @Nullable Object value) throws SQLException {
        ArgumentBinder.bindArgument(ps, index, value);
    }
}
TOP

Related Classes of fi.evident.dalesbred.dialects.Dialect

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.