Solon v3.0.3

solon-data-sqlutils

</> markdown
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-data-sqlutils</artifactId>
</dependency>

1、描述

数据扩展插件。提供基础的 sql 调用(代码仅 20 KB左右),比较极为反朴归真。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;
}

//或者手动获取
DataSource ds = Solon.context().getBean("db1");
SqlUtils sqlUtils = SqlUtils.of(ds);

可以更换默认的行转换器(可选):

@Configuration
public static class SqlConfig {
    //替换默认的转换器( Row:toBean 时被使用 )
    @Bean
    public Row.Converter converter() {
        return (r, c) -> BeanUtil.toBean(r.toMap(), c);
    }
}

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();
}
  • 查询并获取行
public void getRow() throws SQLException {
    //获取行列表
    RowList rowList = sqlUtils.sql("select * from appx limit 2")
                                 .queryRowList();
    //获取行
    Row row = sqlUtils.sql("select * from appx where app_id=?", 11)
                       .queryRow();
                       
     //行的接口
     row.getObject(1);         //columIdx
     row.getObject("app_id");  //columLabel
     Map  map = row.toMap();
     Appx app = row.toBean(Appx.calss);
}
  • 查询并获取行迭代器(流式输出)
public void getRowIterator() throws SQLException {
    String sql = "select * from appx";
    
    //(流读取)用完要 close //比较省内存
    try(RowIterator rowIterator = sqlUtils.sql(sql).queryRowIterator(100)){
        while(rowIterator.hasNext()){
           Appx app = rowIterator.next().toBean(Appx.class);
           ...
        }
    }
}

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().toBeanList(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().toBeanList(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().toBeanList(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;

    /**
     * 查询并获取行
     *
     * @return 行
     */
    @Nullable
    Row queryRow() throws SQLException;

    /**
     * 查询并获取行列表
     *
     * @return 行列表
     */
    @Nullable
    RowList queryRowList() throws SQLException;

    /**
     * 查询并获取行遍历器(流式读取)
     *
     * @return 行遍历器
     */
    RowIterator queryRowIterator(int fetchSize) throws SQLException;


    /**
     * 更新(插入、或更新、或删除)
     *
     * @return 受影响行数
     */
    int update() throws SQLException;

    /**
     * 批量更新(插入、或更新、或删除)
     *
     * @return 受影响行数组
     */
    int[] updateBatch(Collection<Object[]> argsList) throws SQLException;


    /**
     * 更新并返回主键
     *
     * @return 主键
     */
    long updateReturnKey() throws SQLException;
}

配套示例:

https://gitee.com/opensolon/solon-examples/tree/main/4.Solon-Data/demo4010-sqlutils