) l : new HashSet<>(l))
- .orElse(Collections.emptySet());
- }
-
+ /**
+ * 将值强转为 {@code int},并放在 {@link OptionalInt} 中。
+ * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalInt#empty()}。
+ */
+ @Nonnull
public OptionalInt getValueAsInt(String key) {
return OptionalTools.toOptionalInt(this.getAndConvert(key));
}
+ /**
+ * 将值强转为 {@code long},并放在 {@link OptionalLong} 中。
+ * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalLong#empty()}。
+ */
+ @Nonnull
public OptionalLong getValueAsLong(String key) {
return OptionalTools.toOptionalLong(this.getAndConvert(key));
}
+ /**
+ * 将值强转为 {@code double},并放在 {@link OptionalDouble} 中。
+ * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalDouble#empty()}。
+ */
+ @Nonnull
public OptionalDouble getValueAsDouble(String key) {
return OptionalTools.toOptionalDouble(this.getAndConvert(key));
}
diff --git a/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java b/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java
index f0dcd6b..901ba84 100644
--- a/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java
+++ b/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java
@@ -35,9 +35,28 @@ import javax.annotation.Nullable;
import com.google.common.base.CaseFormat;
+import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
+
+/**
+ * DefaultBeanRowMapper
+ *
+ *
+ * 默认实现的将 {@link ResultSet} 转换为 Java Bean 的 {@link RowMapper}。
+ *
+ *
+ *
+ * NOTE: 使用反射获取类型信息,也是使用反射调用无参构造器和 {@code setter} 方法。
+ * 实际使用中还是建议针对目标类型自定义 {@link RowMapper}。
+ *
+ * @author ZhouXY
+ * @since 1.0.0
+ */
public class DefaultBeanRowMapper implements RowMapper {
+ /** Bean 的无参构造器 */
private final Constructor constructor;
+
+ /** 列名到属性的映射 */
private final Map colPropertyMap;
private DefaultBeanRowMapper(Constructor constructor, Map colPropertyMap) {
@@ -45,10 +64,29 @@ public class DefaultBeanRowMapper implements RowMapper {
this.colPropertyMap = colPropertyMap;
}
+ /**
+ * 创建一个 DefaultBeanRowMapper
+ *
+ * @param Bean 类型
+ * @param beanType Bean 类型
+ * @return DefaultBeanRowMapper 对象
+ * @throws SQLException 创建 DefaultBeanRowMapper 出现错误的异常时抛出
+ */
+ @StaticFactoryMethod(DefaultBeanRowMapper.class)
public static DefaultBeanRowMapper of(Class beanType) throws SQLException {
return of(beanType, null);
}
+ /**
+ * 创建一个 DefaultBeanRowMapper
+ *
+ * @param Bean 类型
+ * @param beanType Bean 类型
+ * @param propertyColMap Bean 字段与列名的映射关系。key 是字段,value 是列名。
+ * @return DefaultBeanRowMapper 对象
+ * @throws SQLException 创建 DefaultBeanRowMapper 出现错误的异常时抛出
+ */
+ @StaticFactoryMethod(DefaultBeanRowMapper.class)
public static DefaultBeanRowMapper of(Class beanType, @Nullable Map propertyColMap)
throws SQLException {
try {
@@ -60,6 +98,7 @@ public class DefaultBeanRowMapper implements RowMapper {
BeanInfo beanInfo = Introspector.getBeanInfo(beanType);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+ // Bean 的属性名为小驼峰,对应的列名为下划线
Function super PropertyDescriptor, String> keyMapper;
if (propertyColMap == null || propertyColMap.isEmpty()) {
keyMapper = p -> CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, p.getName());
@@ -84,13 +123,17 @@ public class DefaultBeanRowMapper implements RowMapper {
}
}
+ /** {@inheritDoc} */
@Override
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
try {
+ // 调用无参构造器创建实例
T newInstance = this.constructor.newInstance();
ResultSetMetaData metaData = rs.getMetaData();
+ // 遍历结果的每一列
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String colName = metaData.getColumnName(i);
+ // 获取查询结果列名对应的属性,调用 setter
PropertyDescriptor propertyDescriptor = this.colPropertyMap.get(colName);
if (propertyDescriptor != null) {
Method setter = propertyDescriptor.getWriteMethod();
diff --git a/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java b/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java
new file mode 100644
index 0000000..b1ae1bd
--- /dev/null
+++ b/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 xyz.zhouxy.jdbc;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.collect.Lists;
+
+import xyz.zhouxy.plusone.commons.collection.CollectionTools;
+import xyz.zhouxy.plusone.commons.util.AssertTools;
+import xyz.zhouxy.plusone.commons.util.OptionalTools;
+
+/**
+ * JdbcOperationSupport
+ *
+ *
+ * 提供静态方法,封装 JDBC 基础操作
+ *
+ *
+ * @author ZhouXY
+ * @since 1.0.0
+ */
+class JdbcOperationSupport {
+
+ // #region - query
+
+ /**
+ * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ * @param resultHandler 结果处理器,用于处理 {@link ResultSet}
+ */
+ static T query(Connection conn, String sql, Object[] params, ResultHandler resultHandler)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertResultHandlerNotNull(resultHandler);
+ return queryInternal(conn, sql, params, resultHandler);
+ }
+
+ // #endregion
+
+ // #region - queryList
+
+ /**
+ * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑
+ */
+ static List queryList(Connection conn, String sql, Object[] params, RowMapper rowMapper)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertRowMapperNotNull(rowMapper);
+ return queryListInternal(conn, sql, params, rowMapper);
+ }
+
+ /**
+ * 执行查询,返回结果映射为指定的类型。当结果为单列时使用
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ * @param clazz 将结果映射为指定的类型
+ */
+ static List queryList(Connection conn, String sql, Object[] params, Class clazz)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertClazzNotNull(clazz);
+ return queryListInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz));
+ }
+
+ // #endregion
+
+ // #region - queryFirst
+
+ /**
+ * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional}
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑
+ */
+ static Optional queryFirst(Connection conn, String sql, Object[] params, RowMapper rowMapper)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertRowMapperNotNull(rowMapper);
+ return queryFirstInternal(conn, sql, params, rowMapper);
+ }
+
+ /**
+ * 查询第一行第一列,并转换为指定类型
+ *
+ * @param 目标类型
+ * @param sql SQL
+ * @param params 参数
+ * @param clazz 目标类型
+ */
+ static Optional queryFirst(Connection conn, String sql, Object[] params, Class clazz)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertClazzNotNull(clazz);
+ return queryFirstInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz));
+ }
+
+ /**
+ * 查询第一行第一列,并转换为字符串
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ */
+ static Optional queryFirstString(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getString(1));
+ }
+
+ /**
+ * 查询第一行第一列,并转换为整数值
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ */
+ static OptionalInt queryFirstInt(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getInt(1));
+ return OptionalTools.toOptionalInt(result);
+ }
+
+ /**
+ * 查询第一行第一列,并转换为长整型
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ */
+ static OptionalLong queryFirstLong(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getLong(1));
+ return OptionalTools.toOptionalLong(result);
+ }
+
+ /**
+ * 查询第一行第一列,并转换为双精度浮点型
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ */
+ static OptionalDouble queryFirstDouble(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getDouble(1));
+ return OptionalTools.toOptionalDouble(result);
+ }
+
+ /**
+ * 查询第一行第一列,并转换为 {@link BigDecimal}
+ *
+ * @param conn 数据库连接
+ * @param sql SQL
+ * @param params 参数
+ */
+ static Optional queryFirstBigDecimal(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getBigDecimal(1));
+ }
+
+ // #endregion
+
+ // #region - update & batchUpdate
+
+ /**
+ * 执行更新操作
+ *
+ * @param conn 数据库连接
+ * @param sql 要执行的 SQL
+ * @param params 参数
+ * @return 更新记录数
+ */
+ static int update(Connection conn, String sql, Object[] params)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ try (PreparedStatement stmt = conn.prepareStatement(sql)) {
+ fillStatement(stmt, params);
+ return stmt.executeUpdate();
+ }
+ }
+
+ /**
+ * 执行 SQL 并返回生成的 keys
+ *
+ * @param conn 数据库连接
+ * @param sql 要执行的 SQL
+ * @param params 参数
+ * @param rowMapper 行数据映射逻辑
+ *
+ * @return generated keys
+ * @throws SQLException 执行 SQL 遇到异常情况将抛出
+ */
+ static List update(Connection conn, String sql, Object[] params, RowMapper rowMapper)
+ throws SQLException {
+ assertConnectionNotNull(conn);
+ assertSqlNotNull(sql);
+ assertRowMapperNotNull(rowMapper);
+ final List result = new ArrayList<>();
+ try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
+ fillStatement(stmt, params);
+ stmt.executeUpdate();
+ try (ResultSet generatedKeys = stmt.getGeneratedKeys();) {
+ int rowNumber = 0;
+ while (generatedKeys.next()) {
+ T e = rowMapper.mapRow(generatedKeys, rowNumber++);
+ result.add(e);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * 执行批量更新,批量更新数据,返回每条记录更新的行数
+ *
+ * @param conn 数据库连接
+ * @param sql SQL 语句
+ * @param params 参数列表
+ * @param batchSize 每次批量更新的数据量
+ */
+ static List batchUpdate(Connection conn, String sql, Collection