/* * 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.model; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.sql.DataSource; import org.seasar.extension.jdbc.annotation.ReferentialActionType; import org.seasar.extension.jdbc.gen.desc.ColumnDesc; import org.seasar.extension.jdbc.gen.desc.ForeignKeyDesc; import org.seasar.extension.jdbc.gen.desc.PrimaryKeyDesc; import org.seasar.extension.jdbc.gen.desc.SequenceDesc; import org.seasar.extension.jdbc.gen.desc.TableDesc; import org.seasar.extension.jdbc.gen.desc.UniqueKeyDesc; import org.seasar.extension.jdbc.gen.dialect.GenDialect; import org.seasar.extension.jdbc.gen.internal.desc.SequenceDescFactoryImpl; import org.seasar.extension.jdbc.gen.internal.exception.SequenceNextValFailedRuntimeException; import org.seasar.extension.jdbc.gen.model.ColumnModel; import org.seasar.extension.jdbc.gen.model.ForeignKeyModel; import org.seasar.extension.jdbc.gen.model.PrimaryKeyModel; import org.seasar.extension.jdbc.gen.model.SequenceModel; import org.seasar.extension.jdbc.gen.model.SqlIdentifierCaseType; import org.seasar.extension.jdbc.gen.model.SqlKeywordCaseType; import org.seasar.extension.jdbc.gen.model.TableModel; import org.seasar.extension.jdbc.gen.model.TableModelFactory; import org.seasar.extension.jdbc.gen.model.UniqueKeyModel; import org.seasar.extension.jdbc.util.ConnectionUtil; import org.seasar.extension.jdbc.util.DataSourceUtil; import org.seasar.framework.log.Logger; import org.seasar.framework.util.PreparedStatementUtil; import org.seasar.framework.util.ResultSetUtil; import org.seasar.framework.util.StatementUtil; /** * {@link TableModelFactory}の実装クラスです。 * * @author taedium */ public class TableModelFactoryImpl implements TableModelFactory { /** ロガー */ protected static Logger logger = Logger .getLogger(SequenceDescFactoryImpl.class); /** 方言 */ protected GenDialect dialect; /** データソース */ protected DataSource dataSource; /** SQLステートメントの区切り文字 */ protected char delimiter; /** テーブルオプション、存在しない場合は{@code null} */ protected String tableOption; /** SQLの識別子の大文字小文字を変換するかどうかを示す列挙型 */ protected SqlIdentifierCaseType sqlIdentifierCaseType; /** SQLのキーワードの大文字小文字を変換するかどうかを示す列挙型 */ protected SqlKeywordCaseType sqlKeywordCaseType; /** コメントを使用する場合{@code true} */ protected boolean useComment; /** 生成モデルのサポート */ protected GeneratedModelSupport generatedModelSupport = new GeneratedModelSupport(); /** * @param dialect * 方言 * @param dataSource * データソース * @param sqlIdentifierCaseType * SQLの識別子の大文字小文字を変換するかどうかを示す列挙型 * @param sqlKeywordCaseType * SQLのキーワードの大文字小文字を変換するかどうかを示す列挙型 * @param delimiter * 区切り文字 * @param tableOption * テーブルオプション、存在しない場合は{@code null} * @param useComment * コメントを使用する場合{@code true} */ public TableModelFactoryImpl(GenDialect dialect, DataSource dataSource, SqlIdentifierCaseType sqlIdentifierCaseType, SqlKeywordCaseType sqlKeywordCaseType, char delimiter, String tableOption, boolean useComment) { if (dialect == null) { throw new NullPointerException("dialect"); } if (dataSource == null) { throw new NullPointerException("dataSource"); } if (sqlIdentifierCaseType == null) { throw new NullPointerException("sqlIdentifierCaseType"); } if (sqlKeywordCaseType == null) { throw new NullPointerException("sqlKeywordCaseType"); } this.dialect = dialect; this.dataSource = dataSource; this.sqlIdentifierCaseType = sqlIdentifierCaseType; this.sqlKeywordCaseType = sqlKeywordCaseType; this.delimiter = delimiter; this.tableOption = tableOption; this.useComment = useComment; } public TableModel getTableModel(TableDesc tableDesc) { TableModel tableModel = new TableModel(); tableModel.setCanonicalTableName(tableDesc.getCanonicalName()); tableModel.setName(identifier(tableDesc.getFullName())); tableModel.setDialect(dialect); tableModel.setDelimiter(delimiter); tableModel.setTableOption(keyword(tableOption)); tableModel.setSqlIdentifierCaseType(sqlIdentifierCaseType); tableModel.setSqlKeywordCaseType(sqlKeywordCaseType); tableModel.setComment(normalizeComment(tableDesc.getComment())); tableModel.setUseComment(useComment); doPrimaryKeyModel(tableModel, tableDesc); doUniqueKeyModel(tableModel, tableDesc); doForeignKeyModel(tableModel, tableDesc); doSequenceModel(tableModel, tableDesc); doColumnModel(tableModel, tableDesc); doGeneratedInfo(tableModel, tableDesc); return tableModel; } /** * 主キーモデルを処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doPrimaryKeyModel(TableModel tableModel, TableDesc tableDesc) { PrimaryKeyDesc pkDesc = tableDesc.getPrimaryKeyDesc(); if (pkDesc == null) { return; } PrimaryKeyModel pkModel = new PrimaryKeyModel(); pkModel.setName(identifier(tableDesc.getName() + "_PK")); for (String columnName : pkDesc.getColumnNameList()) { pkModel.addColumnName(identifier(columnName)); } tableModel.setPrimaryKeyModel(pkModel); } /** * 一意キーモデルを処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doUniqueKeyModel(TableModel tableModel, TableDesc tableDesc) { int index = 1; for (UniqueKeyDesc ukDesc : tableDesc.getUniqueKeyDescList()) { UniqueKeyModel ukModel = new UniqueKeyModel(); ukModel.setName(identifier(tableDesc.getName() + "_UK" + index)); ukModel.setDropUniqueKeySyntax(keyword(dialect .getDropUniqueKeySyntax())); for (String columnName : ukDesc.getColumnNameList()) { ukModel.addColumnName(identifier(columnName)); } tableModel.addUniqueKeyModel(ukModel); index++; } } /** * 外部キーモデルを処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doForeignKeyModel(TableModel tableModel, TableDesc tableDesc) { int index = 1; for (ForeignKeyDesc fkDesc : tableDesc.getForeignKeyDescList()) { ForeignKeyModel fkModel = new ForeignKeyModel(); fkModel.setName(identifier(tableDesc.getName() + "_FK" + index)); fkModel.setReferencedTableName(fkDesc.getReferencedFullTableName()); fkModel.setDropForeignKeySyntax(keyword(dialect .getDropForeignKeySyntax())); for (String columnName : fkDesc.getColumnNameList()) { fkModel.addColumnName(identifier(columnName)); } for (String referencedColumnName : fkDesc .getReferencedColumnNameList()) { fkModel.addReferencedColumnName(referencedColumnName); } fkModel.setOnDelete(toActionName(fkDesc.getOnDelete())); fkModel.setOnUpdate(toActionName(fkDesc.getOnUpdate())); tableModel.addForeignKeyModel(fkModel); index++; } } /** * 参照動作の名前に変換します。 * * @param actionType * @return 参照動作の名前 */ protected String toActionName(ReferentialActionType actionType) { if (actionType == null || actionType == ReferentialActionType.NO_ACTION) { return null; } String actionName = actionType.name().replace('_', ' ').toLowerCase(); return keyword(actionName); } /** * シーケンスモデルを処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doSequenceModel(TableModel tableModel, TableDesc tableDesc) { for (SequenceDesc sequenceDesc : tableDesc.getSequenceDescList()) { SequenceModel sequenceModel = new SequenceModel(); sequenceModel.setName(identifier(sequenceDesc.getSequenceName())); Long nextValue = getNextValue(sequenceDesc.getSequenceName(), sequenceDesc.getAllocationSize()); long initialValue = nextValue != null ? Math.max(nextValue, sequenceDesc.getInitialValue()) : sequenceDesc .getInitialValue(); String definition = dialect.getSequenceDefinitionFragment( sequenceDesc.getDataType(), initialValue, sequenceDesc .getAllocationSize()); sequenceModel.setDefinition(keyword(definition)); tableModel.addSequenceModel(sequenceModel); } } /** * カラムモデルを処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doColumnModel(TableModel tableModel, TableDesc tableDesc) { for (ColumnDesc columnDesc : tableDesc.getColumnDescList()) { ColumnModel columnModel = new ColumnModel(); columnModel.setName(identifier(columnDesc.getName())); String definition = columnDesc.getDefinition(); if (columnDesc.isIdentity()) { definition += " " + dialect.getIdentityColumnDefinition(); } else { if (!columnDesc.isNullable()) { definition += " not null"; } } columnModel.setDefinition(keyword(definition)); columnModel.setComment(normalizeComment(columnDesc.getComment())); tableModel.addColumnModel(columnModel); } } /** * シーケンスの次の値を返します。 * * @param sequenceName * シーケンス名 * @param allocationSize * 割り当てサイズ * @return シーケンスの次の値、シーケンスが存在しない場合は{@code null} */ protected Long getNextValue(String sequenceName, int allocationSize) { String sql = dialect.getSequenceNextValString(sequenceName, allocationSize); logger.debug(sql); Connection connection = DataSourceUtil.getConnection(dataSource); try { PreparedStatement ps = ConnectionUtil.prepareStatement(connection, sql); try { ResultSet rs = PreparedStatementUtil.executeQuery(ps); try { if (rs.next()) { return rs.getLong(1); } throw new SequenceNextValFailedRuntimeException( sequenceName); } finally { ResultSetUtil.close(rs); } } finally { StatementUtil.close(ps); } } catch (SequenceNextValFailedRuntimeException e) { throw e; } catch (Exception e) { if (dialect.isSequenceNotFound(e)) { logger.log("DS2JDBCGen0017", new Object[] { sequenceName }); return null; } throw new SequenceNextValFailedRuntimeException(sequenceName, e); } finally { ConnectionUtil.close(connection); } } /** * キーワードの大文字小文字を変換します。 * * @param keyword * キーワード * @return 変換された文字列 */ protected String keyword(String keyword) { return sqlKeywordCaseType.convert(keyword); } /** * 識別子の大文字小文字を変換します。 * * @param identifier * 識別子 * @return 変換された文字列 */ protected String identifier(String identifier) { return sqlIdentifierCaseType.convert(identifier); } /** * コメントを正規化します。 * * @param comment * コメント * @return 正規化されたコメント */ protected String normalizeComment(String comment) { if (comment == null) { return ""; } return comment.replace("'", "''"); } /** * 生成情報を処理します。 * * @param tableModel * テーブルモデル * @param tableDesc * テーブル記述 */ protected void doGeneratedInfo(TableModel tableModel, TableDesc tableDesc) { generatedModelSupport.fillGeneratedInfo(this, tableModel); } }