/*
* 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.id;
import javax.persistence.TableGenerator;
import javax.sql.DataSource;
import org.seasar.extension.jdbc.EntityMeta;
import org.seasar.extension.jdbc.PropertyMeta;
import org.seasar.extension.jdbc.SqlLogger;
import org.seasar.extension.jdbc.exception.IdGenerationFailedRuntimeException;
import org.seasar.extension.jdbc.handler.ObjectResultSetHandler;
import org.seasar.extension.jdbc.impl.BasicSelectHandler;
import org.seasar.extension.jdbc.impl.BasicUpdateHandler;
import org.seasar.extension.jdbc.manager.JdbcManagerImplementor;
import org.seasar.extension.jdbc.types.LongType;
import org.seasar.extension.tx.TransactionCallback;
import org.seasar.extension.tx.TransactionManagerAdapter;
import org.seasar.framework.container.SingletonS2Container;
import org.seasar.framework.util.StringUtil;
/**
* @author koichik
*/
public class TableIdGenerator extends AbstractPreAllocateIdGenerator {
/** デフォルトの採番テーブル名 */
public static final String DEFAULT_TABLE = "ID_GENERATOR";
/** デフォルトの採番テーブルの識別子のカラム名 */
public static final String DEFAULT_PK_COLUMN_NAME = "PK";
/** デフォルトの採番テーブルの値のカラム名 */
public static final String DEFAULT_VALUE_COLUMN_NAME = "VALUE";
/** 識別子に付けられたアノテーション */
protected TableGenerator tableGenerator;
/** 採番テーブルのカタログ名 */
protected String catalog;
/** 採番テーブルのスキーマ名 */
protected String schema;
/** 採番テーブル名 */
protected String table;
/** 採番テーブルの識別子のカラム名 */
protected String pkColumnName;
/** 採番テーブルの識別子の値 */
protected String pkColumnValue;
/** 採番テーブルの値のカラム名 */
protected String valueColumnName;
/** 採番テーブルを更新するSQL */
protected String updateSql;
/** 採番テーブルから値を取得するするSQL */
protected String selectSql;
/**
* インスタンスを構築します。
*
* @param entityMeta
* エンティティのメタデータ
* @param propertyMeta
* 識別子を表すプロパティのメタデータ
* @param tableGenerator
* 識別子に付けられたアノテーション
*/
public TableIdGenerator(final EntityMeta entityMeta,
final PropertyMeta propertyMeta, final TableGenerator tableGenerator) {
super(entityMeta, propertyMeta, tableGenerator.allocationSize());
this.tableGenerator = tableGenerator;
catalog = getCatalog();
schema = getSchema();
table = getTable();
pkColumnName = getPkColumnName();
pkColumnValue = getPkColumnValue();
valueColumnName = getValueColumnName();
updateSql = createUpdateSql();
selectSql = createSelectSql();
}
@Override
protected long getNewInitialValue(final JdbcManagerImplementor jdbcManager,
final SqlLogger sqlLogger) {
try {
final TransactionManagerAdapter txAdapter = SingletonS2Container
.getComponent(TransactionManagerAdapter.class);
final Object result = txAdapter
.requiresNew(new TransactionCallback() {
public Object execute(
final TransactionManagerAdapter adapter)
throws Throwable {
return updateIdTable(jdbcManager);
}
});
return Number.class.cast(result).longValue() - allocationSize;
} catch (final IdGenerationFailedRuntimeException e) {
throw e;
} catch (final Throwable t) {
throw new IdGenerationFailedRuntimeException(entityMeta.getName(),
propertyMeta.getName(), t);
}
}
/**
* 採番テーブルを更新して次の識別子の値を返します。
* <p>
* このメソッドはエンティティのINSERTとは独立したトランザクションで実行されます。
* </p>
*
* @param jdbcManager
* 内部的なJDBCマネージャ
* @return 次の識別子の値
*/
protected Number updateIdTable(final JdbcManagerImplementor jdbcManager) {
final DataSource ds = jdbcManager.getDataSource();
final BasicUpdateHandler updateHandler = new BasicUpdateHandler(ds,
updateSql);
final int rows = updateHandler.execute(new Object[] { allocationSize,
pkColumnValue });
if (rows != 1) {
throw new IdGenerationFailedRuntimeException(entityMeta.getName(),
propertyMeta.getName());
}
final BasicSelectHandler selectHandler = new BasicSelectHandler(ds,
selectSql,
new ObjectResultSetHandler(new LongType(), selectSql));
final Object result = selectHandler.execute(
new Object[] { pkColumnValue }, new Class[] { String.class });
if (result == null || !Number.class.isInstance(result)) {
throw new IdGenerationFailedRuntimeException(entityMeta.getName(),
propertyMeta.getName());
}
return Number.class.cast(result);
}
/**
* 採番テーブルのカタログ名を返します。
*
* @return 採番テーブルのカタログ名
*/
protected String getCatalog() {
final String catalog = tableGenerator.catalog();
if (!StringUtil.isEmpty(catalog)) {
return catalog;
}
return entityMeta.getTableMeta().getCatalog();
}
/**
* 採番テーブルのスキーマ名を返します。
*
* @return 採番テーブルのスキーマ名
*/
protected String getSchema() {
final String schema = tableGenerator.schema();
if (!StringUtil.isEmpty(schema)) {
return schema;
}
return entityMeta.getTableMeta().getSchema();
}
/**
* 採番テーブル名を返します。
*
* @return 採番テーブル名
*/
protected String getTable() {
final String table = tableGenerator.table();
if (!StringUtil.isEmpty(table)) {
return table;
}
return DEFAULT_TABLE;
}
/**
* 採番テーブルの識別子のカラム名を返します。
*
* @return 採番テーブルの識別子のカラム名
*/
protected String getPkColumnName() {
final String pkColumnName = tableGenerator.pkColumnName();
if (!StringUtil.isEmpty(pkColumnName)) {
return pkColumnName;
}
return DEFAULT_PK_COLUMN_NAME;
}
/**
* 採番テーブルの識別子の値を返します。
*
* @return 採番テーブルの識別子の値
*/
protected String getPkColumnValue() {
final String pkColumnValue = tableGenerator.pkColumnValue();
if (!StringUtil.isEmpty(pkColumnValue)) {
return pkColumnValue;
}
return entityMeta.getTableMeta().getName() + "_"
+ propertyMeta.getColumnMeta().getName();
}
/**
* 採番テーブルの値のカラム名を返します。
*
* @return 採番テーブルの値のカラム名
*/
protected String getValueColumnName() {
final String valueColumnName = tableGenerator.valueColumnName();
if (!StringUtil.isEmpty(valueColumnName)) {
return valueColumnName;
}
return DEFAULT_VALUE_COLUMN_NAME;
}
/**
* 採番テーブルを更新するSQLを作成して返します。
*
* @return 採番テーブルを更新するSQL
*/
protected String createUpdateSql() {
final StringBuilder buf = new StringBuilder(100);
buf.append("update ");
if (!StringUtil.isEmpty(catalog)) {
buf.append(catalog).append('.');
}
if (!StringUtil.isEmpty(schema)) {
buf.append(schema).append('.');
}
buf.append(table).append(" set ").append(valueColumnName).append(" = ")
.append(valueColumnName).append(" + ? where ").append(
pkColumnName).append(" = ?");
return new String(buf);
}
/**
* 採番テーブルから値を取得するするSQLを作成して返します。
*
* @return 採番テーブルから値を取得するするSQL
*/
protected String createSelectSql() {
final StringBuilder buf = new StringBuilder(100);
buf.append("select ").append(valueColumnName).append(" from ");
if (!StringUtil.isEmpty(catalog)) {
buf.append(catalog).append('.');
}
if (!StringUtil.isEmpty(schema)) {
buf.append(schema).append('.');
}
buf.append(table).append(" where ").append(pkColumnName).append(" = ?");
return new String(buf);
}
}