Spring JdbcTemplate之使用詳解

  • 2019 年 10 月 3 日
  • 筆記

最近在項目中使用到了 Spring 的 JdbcTemplate, 中間遇到了好多坑, 所以花一些時間對 JdbcTemplate 的使用做了一個總結, 方便以後自己的查看。文章中貼出來的API都是經過測試的, 可以放心大膽的拿去用。

概述

JdbcTemplate主要提供4種方法: 

  1. call()方法: 用於執行存儲過程、存儲函數
  2. execute()方法: 可以執行任何SQL語句, 一般用於DDL語句
  3. update()和batchUpdate()方法: 分別對應單個更新、批量更新的語句執行
  4. query()和queryForXXX()方法: 用於單查、列表查詢

前兩種使用的一般較少, 本次主要介紹後兩種方法的使用。

單個更新

    @Test      public void insert() {          String sql = "insert into pass_user (name,age,gender,birthday,create_time,update_time) values (?,?,?,?,?,?)";          jdbcTemplate.update(sql, "張三丰", 18, "male", Instant.now().toEpochMilli(), Instant.now().toEpochMilli(), Instant.now().toEpochMilli());      }

JdbcTemplate的大部分方法都和上面類似, 參數列表的最右邊經常是一個可變參。

批量更新

    @Test      public void batchInsert() {          String sql = "insert into pass_user (name,age,gender,birthday,create_time,update_time) values (?,?,?,?,?,?)";          List<Object[]> args = new ArrayList<>();          for (int i = 0; i < 5; i++) {              args.add(new Object[]{"張三丰", 18, "male", Instant.now().toEpochMilli(), Instant.now().toEpochMilli(), Instant.now().toEpochMilli()});          }          jdbcTemplate.batchUpdate(sql, args);      }

獲取count、sum等聚合函數返回的唯一值

    /**       * 只能接受String,Integer這種單列類型的實體,否則彙報異常       */      @Test      public void queryForCount1() {          String sql = "select count(1) from pass_user where id > ?";          Integer count = jdbcTemplate.queryForObject(sql, Integer.class, 300000);          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", count);      }

在JdbcTemplate中 queryForObject() 方法的文檔說明中, 指定了該方法只能接受單個記錄的某一列值, 否則報 IncorrectResultSizeDataAccessException 異常。

獲取單個記錄的某一列值

    /**       * 只能接受String,Integer這種單列類型的實體,否則彙報異常       */      @Test      public void queryForObject1() {          String sql = "select NAME from pass_user where id = ?";          String name = jdbcTemplate.queryForObject(sql, String.class, 30);//查詢結果空集時會報EmptyResultDataAccessException異常          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", name);      }

另外 queryForObject() 方法在查詢結果集為空集也就是null值時, 會報 EmptyResultDataAccessException 異常。

獲取單個記錄的所有列值

通過RowMapper映射, 我們可以通過 queryForObject() 方法獲取單個記錄的所有列值, 映射方法有兩種。

如下, PassUser實體類不用實現RowMapper介面, 但是實體類的屬性名必須和表中的列名符合駝峰命名匹配,能一一對應起來, 如果兩者不一致,則需要在sql語句中給對應的列取一個別名。

public class PassUser {        private Long id;      private String name;      private Integer age;      private String gender;      private Long birthday;      private Long createTime;      private Long updateTime;        public Long getId() {          return id;      }        public void setId(Long id) {          this.id = id;      }        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }        public Integer getAge() {          return age;      }        public void setAge(Integer age) {          this.age = age;      }        public String getGender() {          return gender;      }        public void setGender(String gender) {          this.gender = gender;      }        public Long getBirthday() {          return birthday;      }        public void setBirthday(Long birthday) {          this.birthday = birthday;      }        public Long getCreateTime() {          return createTime;      }        public void setCreateTime(Long createTime) {          this.createTime = createTime;      }        public Long getUpdateTime() {          return updateTime;      }        public void setUpdateTime(Long updateTime) {          this.updateTime = updateTime;      }        @Override      public String toString() {          return "PassUser{" +                  "id=" + id +                  ", name='" + name + ''' +                  ", age=" + age +                  ", gender='" + gender + ''' +                  ", birthday=" + birthday +                  ", createTime=" + createTime +                  ", updateTime=" + updateTime +                  '}';      }  }

PassUser實體類

     /**       * 可以自動進行駝峰匹配       */      @Test      public void queryForObject2() {          String sql = "select * from pass_user where id = ?";          RowMapper<PassUser> rowMapper = new BeanPropertyRowMapper<>(PassUser.class);          PassUser passUser = jdbcTemplate.queryForObject(sql, rowMapper, 180);//查詢結果空集時會報EmptyResultDataAccessException異常          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", passUser);      }

第二種方式需要實體類實現RowMapper介面,覆寫 mapRow() 方法

public class UserEntity implements RowMapper<UserEntity> {        private Long id;      private String name;      private Integer age;      private String gender;      private Long birthday;      private Long createTime;      private Long updateTime;        public Long getId() {          return id;      }        public void setId(Long id) {          this.id = id;      }        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }        public Integer getAge() {          return age;      }        public void setAge(Integer age) {          this.age = age;      }        public String getGender() {          return gender;      }        public void setGender(String gender) {          this.gender = gender;      }        public Long getBirthday() {          return birthday;      }        public void setBirthday(Long birthday) {          this.birthday = birthday;      }        public Long getCreateTime() {          return createTime;      }        public void setCreateTime(Long createTime) {          this.createTime = createTime;      }        public Long getUpdateTime() {          return updateTime;      }        public void setUpdateTime(Long updateTime) {          this.updateTime = updateTime;      }        @Override      public String toString() {          return "UserEntity{" +                  "id=" + id +                  ", name='" + name + ''' +                  ", age=" + age +                  ", gender='" + gender + ''' +                  ", birthday=" + birthday +                  ", createTime=" + createTime +                  ", updateTime=" + updateTime +                  '}';      }        @Override      public UserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {          UserEntity userEntity = new UserEntity();          userEntity.setName(rs.getString("name"));          userEntity.setAge(rs.getInt("age"));          userEntity.setGender(rs.getString("gender"));          userEntity.setBirthday(rs.getLong("birthday"));          userEntity.setCreateTime(rs.getLong("create_time"));          userEntity.setUpdateTime(rs.getLong("update_time"));          return userEntity;      }  }

實現RowMapper介面

    /**       * 實體類需要實現介面,覆寫方法       */      @Test      public void queryForObject3() {          String sql = "select * from pass_user where id = ?";          UserEntity userEntity = jdbcTemplate.queryForObject(sql, new UserEntity(), 180);//查詢結果空集時會報EmptyResultDataAccessException異常          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", userEntity);      }

獲取多個記錄的某一列值

    /**       * 實體類需要實現介面,覆寫方法       */      @Test      public void queryForList1() {          String sql = "select name from pass_user where id < ?";          List<String> names = jdbcTemplate.queryForList(sql, String.class, 50);//只能查詢單列屬性值集合          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", names);      }

獲取多個記錄的所有列值

    /**       * 實體類需要實現介面,覆寫方法       */      @Test      public void queryForList2() {          String sql = "select * from pass_user where id < ?";          List<UserEntity> userEntityList = jdbcTemplate.query(sql, new UserEntity(), 0);          LOGGER.info("[" + Thread.currentThread().getStackTrace()[1].getMethodName() + "] {}", userEntityList);      }