
nablarch.common.dao.BasicDaoContext Maven / Gradle / Ivy
The newest version!
package nablarch.common.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import jakarta.persistence.Entity;
import jakarta.persistence.GenerationType;
import jakarta.persistence.OptimisticLockException;
import nablarch.common.idgenerator.IdGenerator;
import nablarch.core.beans.BeanUtil;
import nablarch.core.beans.ConversionUtil;
import nablarch.core.db.DbAccessException;
import nablarch.core.db.connection.AppDbConnection;
import nablarch.core.db.dialect.Dialect;
import nablarch.core.db.statement.ParameterizedSqlPStatement;
import nablarch.core.db.statement.ResultSetIterator;
import nablarch.core.db.statement.SelectOption;
import nablarch.core.db.statement.SqlPStatement;
import nablarch.core.db.statement.SqlRow;
/**
* {@link nablarch.common.dao.DaoContext}のデフォルト実装クラス。
*
* @author kawasima
* @author Hisaaki Shioiri
*/
public class BasicDaoContext implements DaoContext {
/** デフォルトのページサイズ(取得件数) */
private static final long DEFAULT_PER = 25L;
/** サイズ0のオブジェクト配列 */
private static final Object[] EMPTY_PARAMS = new Object[0];
/** データベース接続 */
private AppDbConnection dbConnection;
/** ページ番号 */
private Long page;
/** ページサイズ(取得件数) */
private Long per;
/** 遅延ロードするか否か */
private boolean defer = false;
/** {@link GenerationType}と{@link IdGenerator}との対応表 */
private final Map idGenerators =
new EnumMap(GenerationType.class);
/** SQLビルダー */
private final StandardSqlBuilder sqlBuilder;
/** データベース方言 */
private final Dialect dialect;
/**
* 実行コンテキストを生成する。
*
* 本クラスは直接インスタンス化するのではなく、ファクトリクラス({@link nablarch.common.dao.BasicDaoContextFactory}からインスタンス化する。
* @param sqlBuilder SQLビルダー
* @param dialect データベース方言
*/
BasicDaoContext(StandardSqlBuilder sqlBuilder, Dialect dialect) {
this.sqlBuilder = sqlBuilder;
this.dialect = dialect;
}
/**
* {@inheritDoc}
*
* この実装では、プライマリーキーのメタデータを{@link java.sql.DatabaseMetaData}から取得する。
* @throws IllegalArgumentException (主キーのカラム数と指定した条件数が一致しない場合)
* @throws NoDataException (検索条件に該当するレコードが存在しない場合)
*/
@Override
public T findById(final Class entityClass, final Object... id) {
T result = findByIdOrNull(entityClass, id);
if (result == null) {
throw new NoDataException();
}
return result;
}
/**
* {@inheritDoc}
*
* この実装では、プライマリーキーのメタデータを{@link java.sql.DatabaseMetaData}から取得する。
* @throws IllegalArgumentException (主キーのカラム数と指定した条件数が一致しない場合)
*/
@Override
public T findByIdOrNull(final Class entityClass, final Object... id) {
final List idColumns = EntityUtil.findIdColumns(entityClass);
if (id.length != idColumns.size()) {
throw new IllegalArgumentException("Mismatch the counts of id columns. expected=" + idColumns.size());
}
final String sql = sqlBuilder.buildSelectByIdSql(entityClass);
final SqlPStatement stmt = dbConnection.prepareStatement(sql);
for (int i = 0; i < idColumns.size(); i++) {
final ColumnMeta meta = idColumns.get(i);
stmt.setObject(i + 1, ConversionUtil.convert(meta.getJdbcType(), id[i]));
}
final ResultSetIterator rsIter = stmt.executeQuery();
if (!rsIter.next()) {
return null;
}
final SqlRow row = rsIter.getRow();
return EntityUtil.createEntity(entityClass, row);
}
@Override
public EntityList findAll(final Class entityClass) {
final String sql = sqlBuilder.buildSelectAllSql(entityClass);
final SqlPStatement stmt = dbConnection.prepareStatement(sql);
final SqlResourceHolder holder = new SqlResourceHolder(stmt.executeQuery());
if (defer) {
return new DeferredEntityList(entityClass, holder);
} else {
final EntityList results = new EntityList();
ResultSetIterator rows = holder.getResultSetIterator();
for (SqlRow row : rows) {
results.add(EntityUtil.createEntity(entityClass, row));
}
return results;
}
}
@Override
public EntityList findAllBySqlFile(final Class entityClass, final String sqlId, final Object params) {
if (page == null) {
return findAllBySqlFileWithoutPaginate(entityClass, sqlId, params);
} else {
return findAllBySqlFIleWithPaginate(entityClass, sqlId, params);
}
}
@Override
public EntityList findAllBySqlFile(final Class entityClass, final String sqlId) {
return findAllBySqlFile(entityClass, sqlId, EMPTY_PARAMS);
}
/**
* 検索クエリを実行する。
*
* @param normalizedSqlId SQL ID
* @param params バインド変数
* @param selectOption 検索オプション
* @return 検索結果
*/
@SuppressWarnings("unchecked")
protected SqlResourceHolder executeQuery(final String normalizedSqlId, final Object params, SelectOption selectOption) {
if (params.getClass().isArray()) {
final Object[] paramsArray = (Object[]) params;
final SqlPStatement stmt = dbConnection
.prepareStatementBySqlId(normalizedSqlId, selectOption);
for (int i = 0; i < paramsArray.length; i++) {
stmt.setObject(i + 1, paramsArray[i]);
}
return new SqlResourceHolder(stmt.executeQuery());
} else {
final ParameterizedSqlPStatement stmt = dbConnection
.prepareParameterizedSqlStatementBySqlId(normalizedSqlId, params, selectOption);
if (params instanceof Map) {
return new SqlResourceHolder(stmt.executeQueryByMap((Map) params));
} else {
return new SqlResourceHolder(stmt.executeQueryByObject(params));
}
}
}
/**
* ページングなしの場合の検索を実行する。
*
* @param entityClass エンティティクラス
* @param sqlId SQL ID
* @param params バインド変数
* @param エンティティクラス
* @return エンティティクラスのリスト
*/
protected EntityList findAllBySqlFileWithoutPaginate(
final Class entityClass, final String sqlId, final Object params) {
final SqlResourceHolder holder = executeQuery(normalizeSqlId(sqlId, entityClass), params, new SelectOption(0, 0));
if (defer) {
return new DeferredEntityList(entityClass, holder);
} else {
final EntityList results = new EntityList();
ResultSetIterator rows = holder.getResultSetIterator();
for (SqlRow row : rows) {
results.add(createResultInstance(entityClass, row));
}
results.setResultCount(results.size());
return results;
}
}
/**
* 検索結果オブジェクトを生成する。
*
* @param entityClass エンティティクラス。
* @param row 検索結果
* @param 総称型
* @return 検索結果オブジェクト
*/
private T createResultInstance(final Class entityClass, final SqlRow row) {
if (entityClass.equals(SqlRow.class)) {
@SuppressWarnings("unchecked") final T t = (T) row;
return t;
} else {
return EntityUtil.createEntity(entityClass, row);
}
}
/**
* ページネーションつきの検索を実行する。
*
* 遅延ロード({@link #defer}がtrueの場合)、{@link IllegalArgumentException}を送出する。
*
* @param entityClass エンティティクラス
* @param sqlId SQL ID
* @param params バインド変数
* @param エンティティクラス
* @return エンティティリストを返します。
*/
protected EntityList findAllBySqlFIleWithPaginate(
final Class entityClass, final String sqlId, final Object params) {
if (defer) {
throw new IllegalArgumentException("Can't search with defer and pagination.");
}
final long count = countBySqlFile(entityClass, sqlId, params);
final EntityList results = new EntityList();
results.setPage(page);
results.setMax(per);
results.setResultCount(count);
final SqlResourceHolder holder = executeQuery(normalizeSqlId(sqlId, entityClass), params,
new SelectOption(results.getPagination().getStartPosition(), results.getPagination().getMax()));
try {
for (SqlRow row : holder.getResultSetIterator()) {
results.add(createResultInstance(entityClass, row));
}
} finally {
holder.dispose();
}
return results;
}
/**
* {@inheritDoc}
* @throws NoDataException (検索条件に該当するレコードが存在しない場合)
*/
@Override
public T findBySqlFile(final Class entityClass, final String sqlId, final Object params) {
T t = findBySqlFileOrNull(entityClass, sqlId, params);
if (t == null) {
throw new NoDataException();
}
return t;
}
/**
* {@inheritDoc}
*/
@Override
public T findBySqlFileOrNull(final Class entityClass, final String sqlId, final Object params) {
final SqlResourceHolder holder = executeQuery(normalizeSqlId(sqlId, entityClass), params, new SelectOption(0, 0));
try {
ResultSetIterator rows = holder.getResultSetIterator();
if (rows.next()) {
final SqlRow row = holder.getResultSetIterator().getRow();
if (entityClass.equals(SqlRow.class)) {
@SuppressWarnings("unchecked")
T t = (T) row;
return t;
} else {
return EntityUtil.createEntity(entityClass, row);
}
} else {
return null;
}
} finally {
holder.dispose();
}
}
@SuppressWarnings("unchecked")
@Override
public long countBySqlFile(final Class entityClass, final String sqlId, final Object params) {
final ResultSetIterator rs;
if (params.getClass().isArray()) {
final Object[] paramsArray = (Object[]) params;
final SqlPStatement stmtCount = dbConnection.prepareCountStatementBySqlId(
normalizeSqlId(sqlId, entityClass));
for (int i = 0; i < paramsArray.length; i++) {
stmtCount.setObject(i + 1, paramsArray[i]);
}
rs = stmtCount.executeQuery();
} else {
final ParameterizedSqlPStatement stmtCount = dbConnection
.prepareParameterizedCountSqlStatementBySqlId(
normalizeSqlId(sqlId, entityClass), params);
if (params instanceof Map) {
rs = stmtCount.executeQueryByMap((Map) params);
} else {
rs = stmtCount.executeQueryByObject(params);
}
}
return getCountQueryResult(rs);
}
/**
* 件数取得クエリから結果を取得する。
*
* @param rs 件数取得クエリの実行結果
* @return 件数
*/
private long getCountQueryResult(ResultSetIterator rs) {
long count;
try {
if (rs.next()) {
count = rs.getLong(1);
} else {
throw new IllegalStateException("Count query didn't return result.");
}
} finally {
rs.close();
}
return count;
}
@Override
public int update(final T entity) throws OptimisticLockException {
final SqlWithParams sqlWithParams = sqlBuilder.buildUpdateSql(entity);
final SqlPStatement stmt = dbConnection.prepareStatement(sqlWithParams.getSql());
for (int i = 0; i < sqlWithParams.getParams().size(); i++) {
stmt.setObject(i + 1, sqlWithParams.getParams().get(i));
}
final int rows = stmt.executeUpdate();
if ((EntityUtil.findVersionColumn(entity) != null) && (rows == 0)) {
throw new OptimisticLockException();
}
return rows;
}
@Override
public void batchUpdate(final List entities) {
if (entities.isEmpty()) {
return;
}
final Class> entityClass = entities.get(0).getClass();
final BatchSqlWithColumns sqlWithColumns = sqlBuilder.buildBatchUpdateSql(entityClass);
final SqlPStatement stmt = dbConnection.prepareStatement(sqlWithColumns.getSql());
final List columns = sqlWithColumns.getColumns();
for (T entity : entities) {
addBatchParameter(stmt, entity, columns);
}
stmt.executeBatch();
}
@Override
public void insert(final T entity) {
ColumnMeta generatedValueColumn = EntityUtil.findGeneratedValueColumn(entity);
GenerationType generationType = findGeneratedType(generatedValueColumn);
preInsert(entity, generationType);
final SqlWithParams sqlWithParams;
final SqlPStatement stmt;
if (generationType == GenerationType.IDENTITY) {
sqlWithParams = sqlBuilder.buildInsertWithIdentityColumnSql(entity);
stmt = dbConnection.prepareStatement(sqlWithParams.getSql(), new String[] {
DatabaseUtil.convertIdentifiers(generatedValueColumn.getName()) });
} else {
sqlWithParams = sqlBuilder.buildInsertSql(entity);
stmt = dbConnection.prepareStatement(sqlWithParams.getSql());
}
final ListIterator
© 2015 - 2025 Weber Informatics LLC | Privacy Policy