solon-data-sqlutils
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-data-sqlutils</artifactId>
</dependency>
对应的响应式版本:solon-data-rx-sqlutils
1、描述
数据扩展插件。提供基础的 sql 调用,比较反朴归真。v3.0.2 后支持
- 支持事务管理
- 支持多数据源
- 支持流式输出
- 支持批量执行
- 支持存储过程
一般用于 SQL 很少的项目;或者对性能要求极高的项目;或者不适合 ROM 框架的场景;或者作为引擎用;等...... SqlUtils 总体上分为查询操作(query 开发)和更新操作(update 开头)。分别对应 JDBC 的 Statement:executeQuery()
和 Statement:executeUpdate()
。
2、配置示例
配置数据源(具体参考:《数据源的配置与构建》)
solon.dataSources:
db1!:
class: "com.zaxxer.hikari.HikariDataSource"
jdbcUrl: jdbc:mysql://localhost:3306/rock?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
driverClassName: com.mysql.cj.jdbc.Driver
username: root
password: 123456
db2:
class: "com.zaxxer.hikari.HikariDataSource"
jdbcUrl: jdbc:mysql://localhost:3306/rock?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
driverClassName: com.mysql.cj.jdbc.Driver
username: root
password: 123456
配置数据源后,可按数据源名直接注入(或手动获取):
//注入
@Component
public class DemoService {
@Inject //默认数据源
SqlUtils sqlUtils;
@Inject("db2") //db2 数据源
SqlUtils sqlUtils;
}
//或者手动获取
SqlUtils sqlUtils = SqlUtils.ofName("db1");
可以更换默认的行转换器(可选):
@Component
public class RowConverterFactoryImpl implements RowConverterFactory<Object> {
@Override
public RowConverter<Object> create(Class<?> tClass) {
return new RowConverterImpl(tClass);
}
private static class RowConverterImpl implements RowConverter<Object> {
private final Class<?> tClass;
private ResultSetMetaData metaData;
public RowConverterImpl(Class<?> tClass) {
this.tClass = tClass;
}
@Override
public Object convert(ResultSet rs) throws SQLException {
if (metaData == null) {
metaData = rs.getMetaData();
}
Map<String, Object> map = new LinkedHashMap<>();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
String name = metaData.getColumnName(i);
Object value = rs.getObject(i);
map.put(name, value);
}
if (Map.class == tClass) {
return map;
} else {
return BeanUtil.toBean(map, tClass);
}
}
}
}
可更换默认的工具工厂(可选。比如:添加 sql 打印):
@Component
public class SqlUtilsFactoryImpl implements SqlUtilsFactory {
@Override
public SqlExecutor create(DataSource ds, String sql, Object... args) {
System.out.println("sql:" + sql);
return new SimpleSqlExecutor(ds, sql, args);
}
}
3、查询操作
- 查询并获取值(只查一列)
public void getValue() throws SQLException {
//获取值
Long val = sqlUtils.sql("select count(*) from appx")
.queryValue();
//获取值列表
List<Integer> valList = sqlUtils.sql("select app_id from appx limit 5")
.queryValueList();
}
- 查询并获取行
// Entity 形式
public void getRow() throws SQLException {
//获取行列表
List<Appx> rowList = sqlUtils.sql("select * from appx limit 2")
.queryRowList(Appx.calss);
//获取行
Appx row = sqlUtils.sql("select * from appx where app_id=?", 11)
.queryRow(Appx.calss);
}
// Map 形式
public void getRowMap() throws SQLException {
//获取行列表
List<Map> rowList = sqlUtils.sql("select * from appx limit 2")
.queryRowList(Map.calss);
//获取行
Map row = sqlUtils.sql("select * from appx where app_id=?", 11)
.queryRow(Map.calss);
}
- 查询并获取行迭代器(流式输出)
public void getRowIterator() throws SQLException {
String sql = "select * from appx";
//(流读取)用完要 close //比较省内存
try(RowIterator<Appx> rowIterator = sqlUtils.sql(sql).queryRowIterator(100, Appx.class)){
while(rowIterator.hasNext()){
Appx app = rowIterator.next();
...
}
}
}
4、查询构建器操作
以上几种查询方式,都是一行代码就解决的。复杂的查询怎么办?比如管理后台的条件统计,可以先使用构建器:
public List<Appx> findDataStat(int group_id, String channel, int scale) throws SQLException {
SqlBuilder sqlSpec = new SqlBuilder();
sqlSpec.append("select group_id, sum(amount) amount from appx ")
.append("where group_id = ? ", group_id)
.appendIf(channel != null, "and channel like ? ", channel + "%");
//可以分离控制
if(scale > 10){
sqlSpec.append("and scale = ? ", scale);
}
sqlSpec.append("group by group_id ");
return sqlUtils.sql(sqlSpec).queryRowList(Appx.class);
}
管理后台常见的分页查询:
public Page<Appx> findDataPage(int group_id, String channel) throws SQLException {
SqlBuilder sqlSpec = new SqlBuilder()
.append("from appx where group_id = ? ", group_id)
.appendIf(channel != null, "and channel like ? ", channel + "%");
//备份
sqlSpec.backup();
sqlSpec.insert("select * ");
sqlSpec.append("limit ?,? ", 10,10); //分页获取列表
//查询列表
List<Appx> list = sqlUtils.sql(sqlSpec).queryRowList(Appx.class);
//回滚(可以复用备份前的代码构建)
sqlSpec.restore();
sqlSpec.insert("select count(*) ");
//查询总数
Long total = sqlUtils.sql(sqlSpec).queryValue();
return new Page(list, total);
}
构建器支持 ?...
集合占位符查询:
public List<Appx> findDataList() throws SQLException {
SqlBuilder sqlSpec = new SqlBuilder()
.append("select * from appx where app_id in (?...) ", Arrays.asList(1,2,3,4));
//查询列表
List<Appx> list = sqlUtils.sql(sqlSpec).queryRowList(Appx.class);
}
5、更新操作
- 插入
public void add() throws SQLException {
sqlUtils.sq("insert test(id,v1,v2) values(?,?,?)", 2, 2, 2).update();
//返回自增主键
long key = sqlUtils.sql("insert test(id,v1,v2) values(?,?,?)", 2, 2, 2)
.updateReturnKey();
}
- 更新
public void exe() throws SQLException {
sqlUtils.sql("delete from test where id=?", 2).update();
}
- 批量执行(插入、或更新、或删除)
public void exeBatch() throws SQLException {
List<Object[]> argsList = new ArrayList<>();
argsList.add(new Object[]{1, 1, 1});
argsList.add(new Object[]{2, 2, 2});
argsList.add(new Object[]{3, 3, 3});
argsList.add(new Object[]{4, 4, 4});
argsList.add(new Object[]{5, 5, 5});
int[] rows = sqlUtils.sql("insert test(id,v1,v2) values(?,?,?)")
.updateBatch(argsList);
}
6、接口说明
SqlUtils(Sql 工具类)
public interface SqlUtils {
static SqlUtils of(DataSource dataSource) {
return new SimpleSqlUtils(dataSource);
}
/**
* 执行代码
*
* @param sql 代码
* @param args 参数
*/
SqlExecutor sql(String sql, Object... args);
/**
* 执行代码
*
* @param sqlSpec 代码申明
*/
default SqlExecutor sql(SqlSpec sqlSpec) {
return sql(sqlSpec.getSql(), sqlSpec.getArgs());
}
}
SqlExecutor (Sql 执行器)
public interface SqlExecutor {
/**
* 查询并获取值
*
* @return 值
*/
@Nullable
<T> T queryValue() throws SQLException;
/**
* 查询并获取值列表
*
* @return 值列表
*/
@Nullable
<T> List<T> queryValueList() throws SQLException;
/**
* 查询并获取行
*
* @param tClass Map.class or T.class
* @return 值
*/
@Nullable
<T> T queryRow(Class<T> tClass) throws SQLException;
/**
* 查询并获取行
*
* @return 值
*/
@Nullable
<T> T queryRow(RowConverter<T> converter) throws SQLException;
/**
* 查询并获取行列表
*
* @param tClass Map.class or T.class
* @return 值列表
*/
@Nullable
<T> List<T> queryRowList(Class<T> tClass) throws SQLException;
/**
* 查询并获取行列表
*
* @return 值列表
*/
@Nullable
<T> List<T> queryRowList(RowConverter<T> converter) throws SQLException;
/**
* 查询并获取行遍历器(流式读取)
*
* @param tClass Map.class or T.class
* @return 行遍历器
*/
<T> RowIterator<T> queryRowIterator(int fetchSize, Class<T> tClass) throws SQLException;
/**
* 查询并获取行遍历器(流式读取)
*
* @return 行遍历器
*/
<T> RowIterator<T> queryRowIterator(int fetchSize, RowConverter<T> converter) throws SQLException;
/**
* 更新(插入、或更新、或删除)
*
* @return 受影响行数
*/
int update() throws SQLException;
/**
* 批量更新(插入、或更新、或删除)
*
* @param args 参数
* @param binder 绑定器
* @return 受影响行数组
*/
<S> int update(S args, StatementBinder<S> binder) throws SQLException;
/**
* 批量更新(插入、或更新、或删除)
*
* @param argsList 参数集合
* @return 受影响行数组
*/
int[] updateBatch(Collection<Object[]> argsList) throws SQLException;
/**
* 批量更新(插入、或更新、或删除)
*
* @param argsList 参数集合
* @param binder 绑定器
* @return 受影响行数组
*/
<S> int[] updateBatch(Collection<S> argsList, StatementBinder<S> binder) throws SQLException;
/**
* 更新并返回主键
*
* @return 主键
*/
@Nullable
<T> T updateReturnKey() throws SQLException;
/**
* 更新并返回主键
*
* @return 主键
*/
@Nullable
<T, S> T updateReturnKey(S args, StatementBinder<S> binder) throws SQLException;
}
配套示例:
https://gitee.com/opensolon/solon-examples/tree/main/4.Solon-Data/demo4010-sqlutils