/*
* 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);
}
}