百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

MyBatis常用工具类三-使用SqlRunner操作数据库

ztj100 2025-05-30 19:04 22 浏览 0 评论

MyBatis中提供了一个非常实用的、用于操作数据库的SqlRunner工具类,该类对JDBC做了很好的封装,结合SQL工具类,能够很方便地通过Java代码执行SQL语句并检索SQL执行结果。

SqlRunner类提供了几个操作数据库的方法,分别说明如下。

SqlRunner#closeConnection():用于关闭Connection对象。

SqlRunner#selectOne(String sql, Object… args):执行SELECT语句,SQL语句中可以使用占位符,如果SQL中包含占位符,则可变参数用于为参数占位符赋值,该方法只返回一条记录。若查询结果行数不等于一,则会抛出SQLException异常。

SqlRunner#selectAll(String sql, Object… args):该方法和selectOne()方法的作用相同,只不过该方法可以返回多条记录,方法返回值是一个List对象,List中包含多个Map对象,每个Map对象对应数据库中的一行记录。

SqlRunner#insert(String sql, Object… args):执行一条INSERT语句,插入一条记录。

SqlRunner#update(String sql, Object… args):更新若干条记录。

SqlRunner#delete(String sql, Object… args):删除若干条记录。

SqlRunner#run(String sql):执行任意一条SQL语句,最好为DDL语句。

接下来我们来看一下SqlRunner工具类的使用案例,代码如下:

public class SqlRunnerExample {

    Connection connection = null;

    @Before
    public void initTable() throws SQLException, IOException {
        connection = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
                "sa", "");
        ScriptRunner scriptRunner = new ScriptRunner(connection);
        scriptRunner.setLogWriter(null);
        scriptRunner.runScript(Resources.getResourceAsReader("create-table.sql"));
        scriptRunner.runScript(Resources.getResourceAsReader("init-data.sql"));
    }

    @Test
    public void testSelectOne() throws SQLException {
        SqlRunner sqlRunner = new SqlRunner(connection);
        String qryUserSql = new SQL() {{
            SELECT("*");
            FROM("user");
            WHERE("id = ?");
        }}.toString();
        Map<String, Object> resultMap = sqlRunner.selectOne(qryUserSql, Integer.valueOf(1));
        System.out.println(JSON.toJSONString(resultMap));
    }

    @Test
    public void testDelete() throws SQLException {
        SqlRunner sqlRunner = new SqlRunner(connection);
        String deleteUserSql = new SQL(){{
            DELETE_FROM("user");
            WHERE("id = ?");
        }}.toString();
        sqlRunner.delete(deleteUserSql, Integer.valueOf(1));
    }

    @Test
    public void testUpdate() throws SQLException {
        SqlRunner sqlRunner = new SqlRunner(connection);
        String updateUserSql = new SQL(){{
            UPDATE("user");
            SET("nick_name = ?");
            WHERE("id = ?");
        }}.toString();
        sqlRunner.update(updateUserSql, "Jane", Integer.valueOf(1));
    }

    @Test
    public void testInsert() throws SQLException {
        SqlRunner sqlRunner = new SqlRunner(connection);
        String insertUserSql = new SQL(){{
            INSERT_INTO("user");
            INTO_COLUMNS("create_time,name,password,phone,nick_name");
            INTO_VALUES("?,?,?,?,?");
        }}.toString();
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        sqlRunner.insert(insertUserSql,createTime,"Jane","test","18700000000","J");
    }

    @After
    public void closeConnection() {
        IOUtils.closeQuietly(connection);
    }


}

除了查询外,我们还可以使用SqlRunner完成数据库的增删改操作。

熟悉了SqlRunner工具类的使用后,接下来我们了解一下SqlRunner工具类的实现。以selectAll()方法为例,该方法代码如下:

/*
   * Executes a SELECT statement that returns multiple rows.
   *
   * @param sql  The SQL
   * @param args The arguments to be set on the statement.
   * @return The list of rows expected.
   * @throws SQLException If statement preparation or execution fails
   */
  public List<Map<String, Object>> selectAll(String sql, Object... args) throws SQLException {
    PreparedStatement ps = connection.prepareStatement(sql);
    try {
      setParameters(ps, args);
      ResultSet rs = ps.executeQuery();
      return getResults(rs);
    } finally {
      try {
        ps.close();
      } catch (SQLException e) {
        //ignore
      }
    }
  }

如上面的代码所示,SqlRunner工具类的selectAll()逻辑如下:

(1)调用Connection对象的prepareStatement()方法获取PreparedStatement对象后,调用setParameters()方法为SQL中的参数占位符赋值。

(2)调用PreparedStatement的executeQuery()方法执行查询操作。

(3)调用getResults()方法,将ResultSet对象转换为List对象,其中List对象中的每个Map对象对应数据库中的一条记录。

接下来我们来看一下setParameters()方法的实现,代码如下:

 private void setParameters(PreparedStatement ps, Object... args) throws SQLException {
    for (int i = 0, n = args.length; i < n; i++) {
      if (args[i] == null) {
        throw new SQLException("SqlRunner requires an instance of Null to represent typed null values for JDBC compatibility");
      } else if (args[i] instanceof Null) {
        ((Null) args[i]).getTypeHandler().setParameter(ps, i + 1, null, ((Null) args[i]).getJdbcType());
      } else {
        TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(args[i].getClass());
        if (typeHandler == null) {
          throw new SQLException("SqlRunner could not find a TypeHandler instance for " + args[i].getClass());
        } else {
          typeHandler.setParameter(ps, i + 1, args[i], null);
        }
      }
    }
  }

对setParameters()方法的可变参数进行遍历,根据参数类型获取对应的TypeHandler对象,然后调用MyBatis中TypeHandler对象的setParameter()方法为参数占位符赋值。MyBatis中TypeHandler的作用及实现原理将在后面的章节中详细介绍。

最后,我们再来看一下getResultSet()方法处理结果集的过程,代码如下:

private List<Map<String, Object>> getResults(ResultSet rs) throws SQLException {
    try {
      List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
      List<String> columns = new ArrayList<String>();
      List<TypeHandler<?>> typeHandlers = new ArrayList<TypeHandler<?>>();
      // 1.获取ResultSetMetaData对象。学习JDBC规范时,我们了解到ResultSetMetaData对象中封装了结果集的元数据信息,
      // 包括所有的字段名称及列的数量等信息
      ResultSetMetaData rsmd = rs.getMetaData();
      for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
        columns.add(rsmd.getColumnLabel(i + 1));
        try {
          // 获取所有列的java类型。
          // 根据类型获取对应的TypeHandler对象,将TypeHandler对象注册到变量名为typeHandlers的ArrayList对象中
          Class<?> type = Resources.classForName(rsmd.getColumnClassName(i + 1));
          TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(type);
          if (typeHandler == null) {
            typeHandler = typeHandlerRegistry.getTypeHandler(Object.class);
          }
          typeHandlers.add(typeHandler);
        } catch (Exception e) {
          typeHandlers.add(typeHandlerRegistry.getTypeHandler(Object.class));
        }
      }

      // 遍历ResultSet对象。调用TypeHandler对象的getSesult()方法,
      // 将JDBC类型转换为Java类型,然后将ResultSet对象中的记录行转换为Map对象
      while (rs.next()) {
        Map<String, Object> row = new HashMap<String, Object>();
        for (int i = 0, n = columns.size(); i < n; i++) {
          String name = columns.get(i);
          TypeHandler<?> handler = typeHandlers.get(i);
          row.put(name.toUpperCase(Locale.ENGLISH), handler.getResult(rs, name));
        }
        list.add(row);
      }
      return list;
    } finally {
      if (rs != null) {
        try {
            rs.close();
        } catch (Exception e) {
          // ignore
        }
      }
    }
  }

如上面的代码所示,getResultSet()方法处理结果集的过程如下:

(1)获取ResultSetMetaData对象。学习JDBC规范时,我们了解到ResultSetMetaData对象中封装了结果集的元数据信息,包括所有的字段名称及列的数量等信息。getResultSet()方法中通过ResultSetMetaData对象获取所有列的名称。

(2)获取所有列的JDBC类型。根据类型获取对应的TypeHandler对象,将TypeHandler对象注册到变量名为typeHandlers的ArrayList对象中。

(3)遍历ResultSet对象。调用TypeHandler对象的getSesult()方法,将JDBC类型转换为Java类型,然后将ResultSet对象中的记录行转换为Map对象。

相关推荐

这个 JavaScript Api 已被废弃!请慎用!

在开发过程中,我们可能会不自觉地使用一些已经被标记为废弃的JavaScriptAPI。这些...

JavaScript中10个“过时”的API,你的代码里还在用吗?

JavaScript作为一门不断发展的语言,其API也在持续进化。新的、更安全、更高效的API不断涌现,而一些旧的API则因为各种原因(如安全问题、性能瓶颈、设计缺陷或有了更好的替代品)被标记为“废...

几大开源免费的 JavaScript 富文本编辑器测评

MarkDown编辑器用的时间长了,发现发现富文本编辑器用起来是真的舒服。...

比较好的网页里面的 html 编辑器 推荐

如果您正在寻找嵌入到网页中的HTML编辑器,以便用户可以直接在网页上编辑HTML内容,以下是几个备受推荐的:CKEditor:CKEditor是一个功能强大的、开源的富文本编辑器,可以嵌入到...

Luckysheet 实现excel多人在线协同编辑

前言前些天看到Luckysheet支持协同编辑Excel,正符合我们协同项目的一部分,故而想进一步完善协同文章,但是遇到了一下困难,特此做声明哈,若侵权,请联系我删除文章!若侵犯版权、个人隐私,请联系...

从 Element UI 源码的构建流程来看前端 UI 库设计

作者:前端森林转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ引言...

手把手教你如何用 Decorator 装饰你的 Typescript?「实践」

作者:Nealyang转发连接:https://mp.weixin.qq.com/s/PFgc8xD7gT40-9qXNTpk7A...

推荐五个优秀的富文本编辑器

富文本编辑器是一种可嵌入浏览器网页中,所见即所得的文本编辑器。对于许多从事前端开发的小伙伴来说并不算陌生,它的应用场景非常广泛,平时发个评论、写篇博客文章等都能见到它的身影。...

基于vue + element的后台管理系统解决方案

作者:林鑫转发链接:https://github.com/lin-xin前言该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(WebManagementSystem)开发。基于v...

开源富文本编辑器Quill 2.0重磅发布

开源富文本编辑器Quill正式发布2.0版本。官方TypeScript声明...

Python之Web开发框架学习 Django-表单处理

在Django中创建表单实际上类似于创建模型。同样,我们只需要从Django类继承,则类属性将是表单字段。让我们在myapp文件夹中添加一个forms.py文件以包含我们的应用程序表单。我们将创建一个...

Django测试入门:打造坚实代码基础的钥匙

这一篇说一下django框架的自动化测试,...

Django ORM vs SQLAlchemy:到底谁更香?从入门到上头的选择指南

阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。...

超详细的Django 框架介绍,它来了!

时光荏苒,一晃小编的Tornado框架系列也结束了。这个框架虽然没有之前的FastAPI高流量,但是,它也是小编的心血呀。总共16篇博文,从入门到进阶,包含了框架的方方面面。虽然小编有些方面介绍得不是...

20《Nginx 入门教程》使用 Nginx 部署 Python 项目

今天的目标是完成一个PythonWeb项目的线上部署,我们使用最新的Django项目搭建一个简易的Web工程,然后基于Nginx服务部署该PythonWeb项目。1.前期准备...

取消回复欢迎 发表评论: