/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * 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. */ package org.seasar.extension.jdbc.gen.internal.dialect; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.persistence.GenerationType; import org.seasar.extension.jdbc.gen.internal.sqltype.BinaryType; import org.seasar.extension.jdbc.gen.internal.sqltype.BlobType; import org.seasar.extension.jdbc.gen.internal.sqltype.BooleanType; import org.seasar.extension.jdbc.gen.internal.sqltype.ClobType; import org.seasar.extension.jdbc.gen.internal.sqltype.DecimalType; import org.seasar.extension.jdbc.gen.internal.sqltype.FloatType; import org.seasar.extension.jdbc.util.ConnectionUtil; import org.seasar.framework.util.ResultSetUtil; import org.seasar.framework.util.StatementUtil; /** * DB2の方言を扱うクラスです。 * * @author taedium */ public class Db2GenDialect extends StandardGenDialect { /** テーブルが見つからないことを示すSQLステート */ protected static String TABLE_NOT_FOUND_SQL_STATE = "42704"; /** カラムが見つからないことを示すSQLステート */ protected static String COLUMN_NOT_FOUND_SQL_STATE = "42703"; /** シーケンスが見つからないことを示すSQLステート */ protected static String SEQUENCE_NOT_FOUND_SQL_STATE = "42704"; /** * インスタンスを構築します。 */ public Db2GenDialect() { sqlTypeMap .put(Types.BINARY, new BinaryType("varchar($l) for bit data")); sqlTypeMap.put(Types.BLOB, new BlobType("blob($l)")); sqlTypeMap.put(Types.BOOLEAN, new BooleanType("smallint")); sqlTypeMap.put(Types.CLOB, new ClobType("clob($l)")); sqlTypeMap.put(Types.DECIMAL, new DecimalType("decimal($p,$s)")); sqlTypeMap.put(Types.FLOAT, new FloatType("real")); columnTypeMap.put("blob", Db2ColumnType.BLOB); columnTypeMap.put("char () for bit data", Db2ColumnType.CHAR_BIT); columnTypeMap.put("clob", Db2ColumnType.CLOB); columnTypeMap.put("decimal", Db2ColumnType.DECIMAL); columnTypeMap.put("long varchar for bit data", Db2ColumnType.LONGVARCHAR_BIT); columnTypeMap.put("long varchar", Db2ColumnType.LONGVARCHAR); columnTypeMap.put("varchar () for bit data", Db2ColumnType.VARCHAR_BIT); } @Override public String getName() { return "db2"; } @Override public String getDefaultSchemaName(String userName) { return userName != null ? userName.toUpperCase() : null; } @Override public GenerationType getDefaultGenerationType() { return GenerationType.IDENTITY; } @Override public boolean supportsSequence() { return true; } @Override public String getSequenceDefinitionFragment(String dataType, long initialValue, int allocationSize) { return "as " + dataType + " start with " + initialValue + " increment by " + allocationSize; } @Override public String getIdentityColumnDefinition() { return "generated by default as identity"; } @Override public String getSqlBlockDelimiter() { return "@"; } @Override public boolean isTableNotFound(Throwable t) { for (SQLException e : getAllSQLExceptions(t)) { if (TABLE_NOT_FOUND_SQL_STATE.equals(e.getSQLState())) { return true; } } return false; } @Override public boolean isColumnNotFound(Throwable t) { for (SQLException e : getAllSQLExceptions(t)) { if (COLUMN_NOT_FOUND_SQL_STATE.equals(e.getSQLState())) { return true; } } return false; } @Override public boolean isSequenceNotFound(Throwable t) { for (SQLException e : getAllSQLExceptions(t)) { if (SEQUENCE_NOT_FOUND_SQL_STATE.equals(e.getSQLState())) { return true; } } return false; } @Override public boolean supportsIdentityInsert() { return true; } @Override public boolean supportsIdentity() { return true; } /** * 原因となった、もしくは関連付けられたすべての{@link SQLException}を取得します。 * * @param t * 例外 * @return 原因となった、もしくは関連付けられた{@link SQLException}のリスト */ protected List<SQLException> getAllSQLExceptions(Throwable t) { List<SQLException> sqlExceptionList = new ArrayList<SQLException>(); while (t != null) { if (t instanceof SQLException) { SQLException cause = SQLException.class.cast(t); sqlExceptionList.add(cause); if (cause.getNextException() != null) { cause = cause.getNextException(); sqlExceptionList.add(cause); t = cause; continue; } } t = t.getCause(); } return sqlExceptionList; } @Override public SqlBlockContext createSqlBlockContext() { return new Db2SqlBlockContext(); } @Override public boolean supportsNullableUnique() { return false; } @Override public String getSequenceNextValString(String sequenceName, int allocationSize) { return "values nextval for " + sequenceName; } @Override public boolean supportsCommentInCreateTable() { return false; } @Override public boolean supportsCommentOn() { return true; } @Override public boolean isAutoIncrement(Connection connection, String catalogName, String schemaName, String tableName, String columnName) throws SQLException { String sql = "select generated from syscat.columns where tabschema = ? and tabname = ? and colname = ?"; logger.debug(String.format(sql.replace("?", "'%s'"), schemaName, tableName, columnName)); PreparedStatement ps = ConnectionUtil.prepareStatement(connection, sql); ps.setString(1, schemaName); ps.setString(2, tableName); ps.setString(3, columnName); try { ResultSet rs = ps.executeQuery(); try { if (rs.next()) { String generated = rs.getString(1); return "A".equals(generated) || "D".equals(generated); } return false; } finally { ResultSetUtil.close(rs); } } finally { StatementUtil.close(ps); } } /** * DB2用の{@link StandardColumnType}の実装です。 * * @author taedium */ public static class Db2ColumnType extends StandardColumnType { private static Db2ColumnType BLOB = new Db2ColumnType("blob($l)", byte[].class); private static Db2ColumnType CHAR_BIT = new Db2ColumnType( "char($l) for bit data", byte[].class); private static Db2ColumnType CLOB = new Db2ColumnType("clob($l)", String.class); private static Db2ColumnType DECIMAL = new Db2ColumnType( "decimal($p,$s)", BigDecimal.class); private static Db2ColumnType LONGVARCHAR_BIT = new Db2ColumnType( "long varchar for bit data", byte[].class); private static Db2ColumnType LONGVARCHAR = new Db2ColumnType( "long varchar", String.class); private static Db2ColumnType VARCHAR_BIT = new Db2ColumnType( "varchar($l) for bit data", byte[].class); /** * インスタンスを構築します。 * * @param dataType * データ型 * @param attributeClass * 属性のクラス */ public Db2ColumnType(String dataType, Class<?> attributeClass) { super(dataType, attributeClass); } /** * インスタンスを構築します。 * * @param dataType * データ型 * @param attributeClass * 属性のクラス * @param lob * LOBの場合{@code true} */ public Db2ColumnType(String dataType, Class<?> attributeClass, boolean lob) { super(dataType, attributeClass, lob); } } /** * DB2用の{@link SqlBlockContext}の実装クラスです。 * * @author taedium */ public static class Db2SqlBlockContext extends StandardSqlBlockContext { /** * インスタンスを構築します。 */ protected Db2SqlBlockContext() { sqlBlockStartKeywordsList.add(Arrays.asList("create", "procedure")); sqlBlockStartKeywordsList.add(Arrays.asList("create", "function")); sqlBlockStartKeywordsList.add(Arrays.asList("create", "trigger")); sqlBlockStartKeywordsList.add(Arrays.asList("alter", "procedure")); sqlBlockStartKeywordsList.add(Arrays.asList("alter", "function")); sqlBlockStartKeywordsList.add(Arrays.asList("alter", "trigger")); } } }