Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cn.hutool.db.StatementUtil Maven / Gradle / Ivy
Go to download
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
package cn.hutool.db;
import cn.hutool.core.collection.ArrayIter;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.handler.HandleHelper;
import cn.hutool.db.handler.RsHandler;
import cn.hutool.db.sql.NamedSql;
import cn.hutool.db.sql.SqlBuilder;
import cn.hutool.db.sql.SqlLog;
import cn.hutool.db.sql.SqlUtil;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Statement和PreparedStatement工具类
*
* @author looly
* @since 4.0.10
*/
public class StatementUtil {
/**
* 填充SQL的参数。
*
* @param ps PreparedStatement
* @param params SQL参数
* @return {@link PreparedStatement}
* @throws SQLException SQL执行异常
*/
public static PreparedStatement fillParams(PreparedStatement ps, Object... params) throws SQLException {
if (ArrayUtil.isEmpty(params)) {
return ps;
}
return fillParams(ps, new ArrayIter<>(params));
}
/**
* 填充SQL的参数。
* 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理
*
* @param ps PreparedStatement
* @param params SQL参数
* @return {@link PreparedStatement}
* @throws SQLException SQL执行异常
*/
public static PreparedStatement fillParams(PreparedStatement ps, Iterable> params) throws SQLException {
return fillParams(ps, params, null);
}
/**
* 填充SQL的参数。
* 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理
*
* @param ps PreparedStatement
* @param params SQL参数
* @param nullTypeCache null参数的类型缓存,避免循环中重复获取类型
* @return {@link PreparedStatement}
* @throws SQLException SQL执行异常
* @since 4.6.7
*/
public static PreparedStatement fillParams(PreparedStatement ps, Iterable> params, Map nullTypeCache) throws SQLException {
if (null == params) {
return ps;// 无参数
}
int paramIndex = 1;//第一个参数从1计数
for (Object param : params) {
setParam(ps, paramIndex++, param, nullTypeCache);
}
return ps;
}
/**
* 创建{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sqlBuilder {@link SqlBuilder}包括SQL语句和参数
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 4.1.3
*/
public static PreparedStatement prepareStatement(Connection conn, SqlBuilder sqlBuilder) throws SQLException {
return prepareStatement(conn, sqlBuilder.build(), sqlBuilder.getParamValueArray());
}
/**
* 创建{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param params "?"对应参数列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 3.2.3
*/
public static PreparedStatement prepareStatement(Connection conn, String sql, Collection params) throws SQLException {
return prepareStatement(conn, sql, params.toArray(new Object[0]));
}
/**
* 创建{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param params "?"对应参数列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 3.2.3
*/
public static PreparedStatement prepareStatement(Connection conn, String sql, Object... params) throws SQLException {
return prepareStatement(GlobalDbConfig.returnGeneratedKey, conn, sql, params);
}
/**
* 创建{@link PreparedStatement}
*
* @param returnGeneratedKey 当为insert语句时,是否返回主键
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param params "?"对应参数列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 5.8.19
*/
public static PreparedStatement prepareStatement(boolean returnGeneratedKey, Connection conn, String sql, Object... params) throws SQLException {
Assert.notBlank(sql, "Sql String must be not blank!");
sql = sql.trim();
if(ArrayUtil.isNotEmpty(params) && 1 == params.length && params[0] instanceof Map){
// 检查参数是否为命名方式的参数
final NamedSql namedSql = new NamedSql(sql, Convert.toMap(String.class, Object.class, params[0]));
sql = namedSql.getSql();
params = namedSql.getParams();
}
SqlLog.INSTANCE.log(sql, ArrayUtil.isEmpty(params) ? null : params);
PreparedStatement ps;
if (returnGeneratedKey && StrUtil.startWithIgnoreCase(sql, "insert")) {
// 插入默认返回主键
ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
ps = conn.prepareStatement(sql);
}
return fillParams(ps, params);
}
/**
* 创建批量操作的{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param paramsBatch "?"对应参数批次列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 4.1.13
*/
public static PreparedStatement prepareStatementForBatch(Connection conn, String sql, Object[]... paramsBatch) throws SQLException {
return prepareStatementForBatch(conn, sql, new ArrayIter<>(paramsBatch));
}
/**
* 创建批量操作的{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param paramsBatch "?"对应参数批次列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 4.1.13
*/
public static PreparedStatement prepareStatementForBatch(Connection conn, String sql, Iterable paramsBatch) throws SQLException {
Assert.notBlank(sql, "Sql String must be not blank!");
sql = sql.trim();
SqlLog.INSTANCE.log(sql, paramsBatch);
PreparedStatement ps = conn.prepareStatement(sql);
final Map nullTypeMap = new HashMap<>();
for (Object[] params : paramsBatch) {
fillParams(ps, new ArrayIter<>(params), nullTypeMap);
ps.addBatch();
}
return ps;
}
/**
* 创建批量操作的{@link PreparedStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param fields 字段列表,用于获取对应值
* @param entities "?"对应参数批次列表
* @return {@link PreparedStatement}
* @throws SQLException SQL异常
* @since 4.6.7
*/
public static PreparedStatement prepareStatementForBatch(Connection conn, String sql, Iterable fields, Entity... entities) throws SQLException {
Assert.notBlank(sql, "Sql String must be not blank!");
sql = sql.trim();
SqlLog.INSTANCE.logForBatch(sql);
PreparedStatement ps = conn.prepareStatement(sql);
//null参数的类型缓存,避免循环中重复获取类型
final Map nullTypeMap = new HashMap<>();
for (Entity entity : entities) {
fillParams(ps, CollUtil.valuesOfKeys(entity, fields), nullTypeMap);
ps.addBatch();
}
return ps;
}
/**
* 创建{@link CallableStatement}
*
* @param conn 数据库连接
* @param sql SQL语句,使用"?"做为占位符
* @param params "?"对应参数列表
* @return {@link CallableStatement}
* @throws SQLException SQL异常
* @since 4.1.13
*/
public static CallableStatement prepareCall(Connection conn, String sql, Object... params) throws SQLException {
Assert.notBlank(sql, "Sql String must be not blank!");
sql = sql.trim();
SqlLog.INSTANCE.log(sql, params);
final CallableStatement call = conn.prepareCall(sql);
fillParams(call, params);
return call;
}
/**
* 获得自增键的值
* 此方法对于Oracle无效(返回null)
*
* @param ps PreparedStatement
* @return 自增键的值,不存在返回null
* @throws SQLException SQL执行异常
*/
public static Long getGeneratedKeyOfLong(Statement ps) throws SQLException {
return getGeneratedKeys(ps, (rs)->{
Long generatedKey = null;
if (rs != null && rs.next()) {
try {
generatedKey = rs.getLong(1);
} catch (SQLException e) {
// 自增主键不为数字或者为Oracle的rowid,跳过
}
}
return generatedKey;
});
}
/**
* 获得所有主键
*
* @param ps PreparedStatement
* @return 所有主键
* @throws SQLException SQL执行异常
*/
public static List getGeneratedKeys(Statement ps) throws SQLException {
return getGeneratedKeys(ps, HandleHelper::handleRowToList);
}
/**
* 获取主键,并使用{@link RsHandler} 处理后返回
* @param statement {@link Statement}
* @param rsHandler 主键结果集处理器
* @param 自定义主键类型
* @return 主键
* @throws SQLException SQL执行异常
* @since 5.5.3
*/
public static T getGeneratedKeys(Statement statement, RsHandler rsHandler) throws SQLException {
try (final ResultSet rs = statement.getGeneratedKeys()) {
return rsHandler.handle(rs);
}
}
/**
* 获取null字段对应位置的数据类型
* 有些数据库对于null字段必须指定类型,否则会插入报错,此方法用于获取其类型,如果获取失败,使用默认的{@link Types#VARCHAR}
*
* @param ps {@link Statement}
* @param paramIndex 参数位置,第一位从1开始
* @return 数据类型,默认{@link Types#VARCHAR}
* @since 4.6.7
*/
public static int getTypeOfNull(PreparedStatement ps, int paramIndex) {
int sqlType = Types.VARCHAR;
final ParameterMetaData pmd;
try {
pmd = ps.getParameterMetaData();
sqlType = pmd.getParameterType(paramIndex);
} catch (SQLException ignore) {
// ignore
// log.warn("Null param of index [{}] type get failed, by: {}", paramIndex, e.getMessage());
}
return sqlType;
}
/**
* 为{@link PreparedStatement} 设置单个参数
*
* @param ps {@link PreparedStatement}
* @param paramIndex 参数位置,从1开始
* @param param 参数
* @throws SQLException SQL异常
* @since 4.6.7
*/
public static void setParam(PreparedStatement ps, int paramIndex, Object param) throws SQLException {
setParam(ps, paramIndex, param, null);
}
//--------------------------------------------------------------------------------------------- Private method start
/**
* 为{@link PreparedStatement} 设置单个参数
*
* @param ps {@link PreparedStatement}
* @param paramIndex 参数位置,从1开始
* @param param 参数,不能为{@code null}
* @param nullTypeCache 用于缓存参数为null位置的类型,避免重复获取
* @throws SQLException SQL异常
* @since 4.6.7
*/
private static void setParam(PreparedStatement ps, int paramIndex, Object param, Map nullTypeCache) throws SQLException {
if (null == param) {
Integer type = (null == nullTypeCache) ? null : nullTypeCache.get(paramIndex);
if (null == type) {
type = getTypeOfNull(ps, paramIndex);
if (null != nullTypeCache) {
nullTypeCache.put(paramIndex, type);
}
}
ps.setNull(paramIndex, type);
}
// 日期特殊处理,默认按照时间戳传入,避免毫秒丢失
if (param instanceof java.util.Date) {
if (param instanceof java.sql.Date) {
ps.setDate(paramIndex, (java.sql.Date) param);
} else if (param instanceof java.sql.Time) {
ps.setTime(paramIndex, (java.sql.Time) param);
} else {
ps.setTimestamp(paramIndex, SqlUtil.toSqlTimestamp((java.util.Date) param));
}
return;
}
// 针对大数字类型的特殊处理
if (param instanceof Number) {
if (param instanceof BigDecimal) {
// BigDecimal的转换交给JDBC驱动处理
ps.setBigDecimal(paramIndex, (BigDecimal) param);
return;
}
if (param instanceof BigInteger) {
// BigInteger转为BigDecimal
ps.setBigDecimal(paramIndex, new BigDecimal((BigInteger) param));
return;
}
// 忽略其它数字类型,按照默认类型传入
}
//InputStream,解决oracle情况下setObject(inputStream)报错问题,java.sql.SQLException: 无效的列类型
if(param instanceof InputStream){
ps.setBinaryStream(paramIndex, (InputStream) param);
return;
}
//java.sql.Blob
if(param instanceof Blob){
ps.setBlob(paramIndex, (Blob) param);
}
// 其它参数类型
ps.setObject(paramIndex, param);
}
//--------------------------------------------------------------------------------------------- Private method end
}