cn.sylinx.hbatis.db.common.DbPro Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbatis-core Show documentation
Show all versions of hbatis-core Show documentation
hbatis is a simple orm framework
The newest version!
package cn.sylinx.hbatis.db.common;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import cn.sylinx.hbatis.db.dialect.DbType;
import cn.sylinx.hbatis.db.dialect.Dialect;
import cn.sylinx.hbatis.db.dialect.DialectFatory;
import cn.sylinx.hbatis.db.mapper.ModelBuilder;
import cn.sylinx.hbatis.db.mapper.QueryMapper;
import cn.sylinx.hbatis.ds.JdbcBlock;
import cn.sylinx.hbatis.ds.JdbcResource;
import cn.sylinx.hbatis.ds.JdbcResourceManager;
import cn.sylinx.hbatis.ds.ResourceHelper;
import cn.sylinx.hbatis.exception.ErrorCodeRecordable;
import cn.sylinx.hbatis.exception.HbatisException;
import cn.sylinx.hbatis.exception.NestedTransactionException;
import cn.sylinx.hbatis.kit.DbKit;
import cn.sylinx.hbatis.kit.Ret;
import cn.sylinx.hbatis.kit.Tuple;
import cn.sylinx.hbatis.log.GLog;
import cn.sylinx.hbatis.plugin.datasource.JdbcResourcePlugin;
import cn.sylinx.hbatis.plugin.model.ModelFabric;
import cn.sylinx.hbatis.plugin.transaction.TransactionIsolationWrapper;
/**
* 数据库操作类
*
* @author han
*
*/
class DbPro {
/**
* 默认批量提交数量
*/
public static final int DEFAULT_BATCH_SIZE = 500;
/**
* 数据源
*/
private final JdbcResource jdbcResource;
/**
* 数据源名称
*/
private final String dataSourceName;
/**
* 方言
*/
private final Dialect dialect;
/**
* 构造器
*
* @param jdbcResourceName 数据源名称
*/
DbPro(String jdbcResourceName) {
this.jdbcResource = JdbcResourceManager.get().get(jdbcResourceName);
if (this.jdbcResource == null) {
throw new HbatisException("jdbc resource not found by jdbcResourceName: " + jdbcResourceName);
}
this.dataSourceName = jdbcResource.getDataSourceName();
this.dialect = DialectFatory.get().createDialect(jdbcResource.getDbType());
}
/**
* 获取数据源名称
*
* @return 据源名称
*/
public String getDataSourceName() {
return dataSourceName;
}
/**
* 获取方言
*
* @return
*/
public Dialect getDialect() {
return dialect;
}
/**
* 自定义操作
*
* @param callable
* @return
*/
public T call(Callable callable) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public T applyBlock(Connection conn) throws SQLException {
return callable.call(conn);
}
});
}
/**
* 使用特定的DdPro
*
* @param jdbcResourceName 数据源名称
* @return DbPro对象
*/
public static DbPro use(String jdbcResourceName) {
return new DbPro(jdbcResourceName);
}
/**
* 使用默认的DdPro
*
* @return DbPro对象
*/
public static DbPro use() {
return new DbPro(JdbcResourcePlugin.DEFAULT_JDBC_RESOURCE_NAME);
}
/**
* 默认500提交
*
* @param sql sql语句列表
* @return 影响行数
*/
public int[] batch(final List sql) {
return batch(sql, DEFAULT_BATCH_SIZE);
}
/**
* 默认500提交,预编译
*
* @param sql sql语句
* @param params 参数数组
* @return 影响行数
*/
public int[] batch(final String sql, final Object[][] params) {
return batch(sql, params, DEFAULT_BATCH_SIZE);
}
/**
* 批量执行
*
* @param sql sql语句
* @param params 参数
* @param batchSize 批量提交数
* @return 影响行数
*/
public int[] batch(final String sql, final Object[][] params, final int batchSize) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
private int[] batchInner(Connection conn, final String sql, final Object[][] params, final int batchSize)
throws SQLException {
if (params == null || params.length == 0)
throw new IllegalArgumentException("The paras array length must more than 0.");
if (batchSize < 1)
throw new IllegalArgumentException("The batchSize must more than 0.");
int counter = 0;
int pointer = 0;
int[] result = new int[params.length];
PreparedStatement pst = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
dialect.setParameters(pst, params[i]);
pst.addBatch();
if (++counter >= batchSize) {
counter = 0;
int[] r = pst.executeBatch();
conn.commit();
for (int k = 0; k < r.length; k++)
result[pointer++] = r[k];
}
}
int[] r = pst.executeBatch();
conn.commit();
for (int k = 0; k < r.length; k++)
result[pointer++] = r[k];
DbKit.closeQuietly(pst);
return result;
}
@Override
public int[] applyBlock(Connection conn) throws SQLException {
Boolean autoCommit = null;
try {
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
return batchInner(conn, sql, params, batchSize);
} catch (Exception e) {
GLog.error("batch error:", e);
conn.rollback();
return null;
} finally {
if (autoCommit != null) {
conn.setAutoCommit(autoCommit);
}
}
}
});
}
/**
* 批量执行
*
* @param sqlList sql语句列表
* @param batchSize 批量提交数量
* @return 影响行数
*/
public int[] batch(final List sqlList, final int batchSize) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
private int[] batchInner(Connection conn, final List sqlList, final int batchSize)
throws SQLException {
if (sqlList == null || sqlList.size() == 0)
throw new IllegalArgumentException("The sqlList length must more than 0.");
if (batchSize < 1)
throw new IllegalArgumentException("The batchSize must more than 0.");
int counter = 0;
int pointer = 0;
int size = sqlList.size();
int[] result = new int[size];
Statement st = conn.createStatement();
for (int i = 0; i < size; i++) {
st.addBatch(sqlList.get(i));
if (++counter >= batchSize) {
counter = 0;
int[] r = st.executeBatch();
conn.commit();
for (int k = 0; k < r.length; k++)
result[pointer++] = r[k];
}
}
int[] r = st.executeBatch();
conn.commit();
for (int k = 0; k < r.length; k++)
result[pointer++] = r[k];
DbKit.closeQuietly(st);
return result;
}
@Override
public int[] applyBlock(Connection conn) throws SQLException {
Boolean autoCommit = null;
try {
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
return batchInner(conn, sqlList, batchSize);
} catch (Exception e) {
GLog.error("batch error:", e);
conn.rollback();
return null;
} finally {
if (autoCommit != null) {
conn.setAutoCommit(autoCommit);
}
}
}
});
}
/**
* 执行事务
*
* @param transaction 事务对象
* @return 是否执行成功
*/
public boolean transaction(final ITransaction transaction) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public Boolean applyBlock(Connection conn) throws SQLException {
Connection transCon = jdbcResource.getTransactionConnection();
if (transCon != null) { // Nested transaction support
Ret result = transaction.run();
if (result.isSuccess())
return true;
throw new NestedTransactionException(
"Notice the outer transaction that the nested transaction return false");
// important:can not return false
}
Boolean autoCommit = null;
try {
autoCommit = conn.getAutoCommit();
int transactionIsolation = (transaction.transactionIsolation() == null
? TransactionIsolationWrapper.ME.getTransactionIsolation()
: transaction.transactionIsolation().intValue());
conn.setTransactionIsolation(transactionIsolation);
conn.setAutoCommit(false);
jdbcResource.setTransactionConnection(conn);
Ret result = transaction.run();
if (result.isSuccess()) {
conn.commit();
} else {
conn.rollback();
}
return result.isSuccess();
} catch (Throwable t) {
GLog.error("transaction error :", t);
conn.rollback();
} finally {
conn.setAutoCommit(autoCommit);
jdbcResource.removeTransactionConnection();
}
return false;
}
});
}
/**
* 带有返回信息的事务处理
*
* @param transaction
* @return
*/
public Ret transactionWithReturn(final ITransaction transaction) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public Ret applyBlock(Connection conn) throws SQLException {
Connection transCon = jdbcResource.getTransactionConnection();
if (transCon != null) { // Nested transaction support
return Ret.nestedRet(transaction.run());
}
Boolean autoCommit = null;
try {
autoCommit = conn.getAutoCommit();
int transactionIsolation = (transaction.transactionIsolation() == null
? TransactionIsolationWrapper.ME.getTransactionIsolation()
: transaction.transactionIsolation().intValue());
conn.setTransactionIsolation(transactionIsolation);
conn.setAutoCommit(false);
jdbcResource.setTransactionConnection(conn);
Ret result = transaction.run();
if (result.isSuccess()) {
GLog.debug("transaction commit");
conn.commit();
} else {
GLog.debug("transaction rollback");
conn.rollback();
}
return result;
} catch (Throwable t) {
GLog.error("transaction error :", t);
conn.rollback();
int code = 500;
if (ErrorCodeRecordable.class.isAssignableFrom(t.getClass())) {
code = ((ErrorCodeRecordable) t).getCode();
}
return Ret.error(code, t.getMessage(), t);
} finally {
conn.setAutoCommit(autoCommit);
jdbcResource.removeTransactionConnection();
}
}
});
}
/**
* 删除
*
* @param t
* @return
*/
public int delete(T t) {
if (t == null) {
throw new HbatisException("模型为空");
}
Tuple tuple = null;
try {
tuple = getDialect().getSqlBuilder().buildDeleteSQL(t);
} catch (Exception e) {
GLog.error("ModelBuilder.buildDeleteSQL(t, mapper) error: ", e);
}
if (tuple == null) {
return 0;
}
String updateSql = (String) tuple.get(0);
Object[] params = (Object[]) tuple.get(1);
return update(updateSql, params);
}
/**
*
* @param t
* @return
*/
public Serializable save(T t) {
if (t == null) {
throw new HbatisException("模型为空");
}
Tuple tuple = null;
try {
tuple = getDialect().getSqlBuilder().buildInsertSQL(t);
} catch (Exception e) {
GLog.error("ModelBuilder.buildInsertSQL(t, mapper) error: ", e);
}
if (tuple == null) {
return null;
}
String updateSql = (String) tuple.get(0);
Object[] params = (Object[]) tuple.get(1);
return save(updateSql, params);
}
/**
* 对象更新
*
* @param t T对象
* @param mapper 更新mapper
* @return 影响行数
*/
public int update(T t) {
if (t == null) {
throw new HbatisException("更新对象为空");
}
Tuple tuple = null;
try {
tuple = getDialect().getSqlBuilder().buildUpdateSQL(t);
} catch (Exception e) {
GLog.error("ModelBuilder.buildUpdateSQL(t, mapper) error: ", e);
throw new RuntimeException(e);
}
if (tuple == null) {
return 0;
}
String updateSql = (String) tuple.get(0);
Object[] params = (Object[]) tuple.get(1);
return update(updateSql, params);
}
/**
* Executes the SQL statement in this PreparedStatement object, which may be any
* kind of SQL statement. Some prepared statements return multiple results; the
* execute method handles these complex statements as well as the simpler form
* of statements handled by the methods executeQuery and executeUpdate.
*
* The execute method returns a boolean to indicate the form of the first
* result. You must call either the method getResultSet or getUpdateCount to
* retrieve the result; you must call getMoreResults to move to any subsequent
* result(s).
*
* @param exeSql
* @param params
* @return true if the first result is a ResultSet object; false if the first
* result is an update count or there is no result
*/
public boolean execute(final String exeSql, final Object... params) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public Boolean applyBlock(Connection conn) throws SQLException {
PreparedStatement pst = conn.prepareStatement(exeSql);
if (params != null) {
dialect.setParameters(pst, params);
}
boolean bl = pst.execute();
DbKit.closeQuietly(pst);
return bl;
}
});
}
public boolean executeLargeUpdate(final List exeSqlList) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public Boolean applyBlock(Connection conn) {
try {
Statement st = conn.createStatement();
for (String sql : exeSqlList) {
st.addBatch(sql);
}
int[] rst = st.executeBatch();
GLog.info("executeLargeUpdate rst:{}", rst);
DbKit.closeQuietly(st);
} catch (Exception e) {
GLog.error("executeLargeUpdate error ", e);
return false;
}
return true;
}
});
}
/**
* 更新操作,包括删除
*
* @param updateSql 更新sql
* @param params 参数
* @return 影响行数
*/
public int update(final String updateSql, final Object... params) {
return call((conn) -> {
PreparedStatement pst = conn.prepareStatement(updateSql);
if (params != null) {
dialect.setParameters(pst, params);
}
int result = pst.executeUpdate();
if (result < 1) {
GLog.info("0 rows updated, sql:{}, params:{}", updateSql, params);
}
DbKit.closeQuietly(pst);
return result;
});
}
/**
* 保存操作
*
* @param insertSql 插入sql
* @param params 参数
* @return 主键
*/
public Serializable save(final String insertSql, final Object... params) {
if (DbType.CLICKHOUSE == dialect.getDbType()) {
int count = update(insertSql, params);
GLog.debug("clickhouse add count: {}", count);
// clickhouse 无需返回主键
return null;
}
return updateWithReturnPk(insertSql, params);
}
/**
* 更新返回主键
*
* @param updateSql
* @param params
* @return
*/
public Serializable updateWithReturnPk(final String updateSql, final Object... params) {
return ResourceHelper.using(jdbcResource, new JdbcBlock() {
@Override
public Serializable applyBlock(Connection conn) throws SQLException {
PreparedStatement pst = conn.prepareStatement(updateSql, PreparedStatement.RETURN_GENERATED_KEYS);
if (params != null) {
dialect.setParameters(pst, params);
}
int result = pst.executeUpdate();
if (result < 1) {
GLog.error("update error, sql:" + updateSql);
}
ResultSet rs = pst.getGeneratedKeys();
Object pk = rs.next() ? rs.getObject(1) : null;
DbKit.closeQuietly(rs, pst);
return (Serializable) pk;
}
});
}
/**
* 获取一条记录
*
* @param sql sql语句
* @param params 参数
* @return Map记录对象
*/
public Map queryFirstMap(final String sql, final Object... params) {
return ResourceHelper.using(jdbcResource, new JdbcBlock