The Forty-Second Chapter of Spring's Road to God: Playing with JdbcTemplate

Originally, this article was about spring transactions, but most cases in transactions will use JdbcTemplate-related functions, so let’s talk about JdbcTemplate first.

What is JdbcTemplate?

Let’s review, the most original way to operate db in java is pure jdbc, does it need to load the database driver, obtain the connection, obtain the PreparedStatement, execute sql, close the PreparedStatement, close the connection, etc. every time you operate the db, the operation is still relatively To be cumbersome, spring provides a module that encapsulates jdbc operations to make it simpler. It is the JdbcTemplate that this article will talk about. JdbcTemplate is Spring's encapsulation of JDBC, with the purpose of making JDBC easier to use.

Let's take a look at how JdbcTemplate works?

JdbcTemplate usage steps

  1. Create a data source DataSource

  2. Create JdbcTemplate, new JdbcTemplate(dataSource)

  3. Call the method of JdbcTemplate to operate db, such as adding, deleting, modifying and checking

    public class DataSourceUtils {
        public static DataSource getDataSource() {
            org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
            dataSource.setUsername("root");
            dataSource.setPassword("root123");
            dataSource.setInitialSize(5);
            return dataSource;
        }
    }
    
    @Test
    public void test0() {
        //1.创建数据源DataSource
        DataSource dataSource = DataSourceUtils.getDataSource();
        //2.创建JdbcTemplate,new JdbcTemplate(dataSource)
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        //3.调用JdbcTemplate的方法操作db,如增删改查
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from t_user");
        System.out.println(maps);
    }

output

[{id=114, name=路人}, {id=115, name=java高并发}, {id=116, name=spring系列}]

t_user table data

mysql> select id,name from t_user;
+-----+---------------+
| id  | name          |
+-----+---------------+
| 114 | 路人          |
| 115 | java高并发    |
| 116 | spring系列    |
+-----+---------------+
3 rows in set (0.00 sec)

The above query returns t_userall the records of the table, and returns a collection, which is a Map, Map represents a row of records, key is the column name, and value is the value corresponding to the column.

Do you feel it is particularly convenient? It only takes jdbcTemplate.queryForList("select * from t_user")such a simple line of code to get the data.

Let's continue to explore more powerful and more useful functions.

Add, delete, modify operations

The methods starting with update in JdbcTemplate are used to perform addition, deletion, and modification operations . Let’s look at a few commonly used ones.

No parameters

Api

int update(final String sql)

the case

@Test
public void test1() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    int updateRows = jdbcTemplate.update("INSERT INTO t_user (name) VALUE ('maven系列')");
    System.out.println("影响行数:" + updateRows);
}

There are cases 1

Api

int update(String sql, Object... args)

the case

Use ? as a placeholder in sql.

@Test
public void test2() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    int updateRows = jdbcTemplate.update("INSERT INTO t_user (name) VALUE (?)", "mybatis系列");
    System.out.println("影响行数:" + updateRows);
}

There is a reference situation 2

Api

int update(String sql, PreparedStatementSetter pss)

Setting parameters through PreparedStatementSetter is a functional interface. There is a setValues ​​method inside that will pass a PreparedStatement parameter. We can manually set the value of the parameter through this parameter.

the case

@Test
public void test3() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    int updateRows = jdbcTemplate.update("INSERT INTO t_user (name) VALUE (?)", new PreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement ps) throws SQLException {
            ps.setString(1, "mysql系列");
        }
    });
    System.out.println("影响行数:" + updateRows);
}

Get the value of the auto-increment column

Api

public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)

the case

@Test
public void test4() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "INSERT INTO t_user (name) VALUE (?)";
    KeyHolder keyHolder = new GeneratedKeyHolder();
    int rowCount = jdbcTemplate.update(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
            //手动创建PreparedStatement,注意第二个参数:Statement.RETURN_GENERATED_KEYS
            PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            ps.setString(1, "获取自增列的值");
            return ps;
        }
    }, keyHolder);
    System.out.println("新记录id:" + keyHolder.getKey().intValue());
}

output

New record id: 122
mysql> select id,name from t_user;
+-----+-----------------------+
| id  | name                  |
+-----+-----------------------+
| 114 | 路人                  |
| 115 | java高并发            |
| 116 | spring系列            |
| 117 | maven系列             |
| 118 | mysql系列             |
| 122 | 获取自增列的值        |
+-----+-----------------------+
6 rows in set (0.00 sec)

Batch add, delete, modify operations

Api

int[] batchUpdate(final String[] sql);
int[] batchUpdate(String sql, List<Object[]> batchArgs);
int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);

the case

@Test
public void test5() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    List<Object[]> list = Arrays.asList(
            new Object[]{"刘德华"}, 
            new Object[]{"郭富城"}, 
            new Object[]{"张学友"}, 
            new Object[]{"黎明"});
    int[] updateRows = jdbcTemplate.batchUpdate("INSERT INTO t_user (name) VALUE (?)", list);
    for (int updateRow : updateRows) {
        System.out.println(updateRow);
    }
}

query operation

Query a column with a single row

Api

/**
 * sql:执行的sql,如果有参数,参数占位符?
 * requiredType:返回的一列数据对应的java类型,如String
 * args:?占位符对应的参数列表
 **/
<T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args)

the case

@Test
public void test6() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String name = jdbcTemplate.queryForObject("select name from t_user where id = ?", String.class, 114);
    System.out.println(name);
}

output

passerby

Corresponding data in db

mysql> select name from t_user where id = 114;
+--------+
| name   |
+--------+
| 路人   |
+--------+
1 row in set (0.00 sec)

Caution

If the sql query in queryForObject has no results, an error will be reported

If the record with id 0 does not exist

mysql> select name from t_user where id = 0;
Empty set (0.00 sec)
@Test
public void test7() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String name = jdbcTemplate.queryForObject("select name from t_user where id = ?", String.class, 0);
    System.out.println(name);
} 

When running, an exception will pop up EmptyResultDataAccessException, expecting to return a record, but actually no record is found, which does not match the expected result, so an error is reported

org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

 at org.springframework.dao.support.DataAccessUtils.nullableSingleResult(DataAccessUtils.java:97)
 at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:784)

How to solve this problem needs to be solved by querying multiple rows, that is, the related methods mentioned below queryForListwill return an empty List when there is no result, and we can make a fuss about this empty List.

Query a column with multiple rows

Api

Methods starting with queryForList.

<T> List<T> queryForList(String sql, Class<T> elementType);
<T> List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args);
<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType);
<T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType);

Notice:

Although the above T is a generic type, it only supports single data types such as Integer.class String.class, and self-defined beans do not support it. (So ​​used to query single-column data)

elementType: What type of query result needs to be converted? Such as String, Integer, Double.

the case

@Test
public void test8() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    //<T> List<T> queryForList(String sql, Class<T> elementType);
    List<String> list1 = jdbcTemplate.queryForList("select name from t_user where id>131", String.class);
    System.out.println("list1:" + list1);

    //<T> List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args);
    List<String> list2 = jdbcTemplate.queryForList("select name from t_user where id>?", String.class, 131);
    System.out.println("list2:" + list2);

    //<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType);
    List<String> list3 = jdbcTemplate.queryForList("select name from t_user where id>?", new Object[]{131}, String.class);
    System.out.println("list3:" + list3);

    //<T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType);
    List<String> list4 = jdbcTemplate.queryForList("select name from t_user where id>?", new Object[]{131}, new int[]{java.sql.Types.INTEGER}, String.class);
    System.out.println("list4:" + list4);
}

output

list1:[Aaron Kwok, Jacky Cheung, dawn] 
list2:[Aaron Kwok, Jacky Cheung, dawn] 
list3:[Aaron Kwok, Jacky Cheung, dawn] 
list4:[Aaron Kwok, Jacky Cheung, dawn]

sql result:

mysql> select name from t_user where id>131;
+-----------+
| name      |
+-----------+
| 郭富城    |
| 张学友    |
| 黎明      |
+-----------+
3 rows in set (0.00 sec)

Query a single row record and convert the record into an object

Api

<T> T queryForObject(String sql, RowMapper<T> rowMapper);
<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper);
<T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);
<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);

Among the parameters of the above methods, there is a rowMapper parameter, the row mapper, which can map the result of the current row to a custom object.

@FunctionalInterface
public interface RowMapper<T> {

 /**
  * @param ResultSet 结果集
  * @param 当前结果集的第几行
  * @return 当前行的结果对象,将当前行的结果映射为一个自定义的对象返回
  */
 @Nullable
 T mapRow(ResultSet rs, int rowNum) throws SQLException;

}

JdbcTemplate will traverse the ResultSet internally, and then call RowMapper#mapRow in a loop to get the result of the current row, throw it into the List and return it, as follows:

List<T> results = new ArrayList<>();
int rowNum = 0;
while (rs.next()) {
    results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;

the case

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    private Integer id;
    private String name;
}
@Test
public void test9() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "select id,name from t_user where id = ?";
    //查询id为34的用户信息
    User user = jdbcTemplate.queryForObject(sql, new RowMapper<User>() {
        @Nullable
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt(1));
            user.setName(rs.getString(1));
            return user;
        }
    }, 134);
    System.out.println(user);
}

output

User(id=134, name=134)

Caution

When the sql query in queryForObject has no result, an error will be reported, and a row of records must be returned

Query a single-line record and return the specified javabean

RowMapper has a class that implements BeanPropertyRowMapper, which can map the result to javabean.

@Test
public void test10() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "select id,name from t_user where id = ?";
    //查询id为34的用户信息
    RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
    User user = jdbcTemplate.queryForObject(sql, rowMapper, 134);
    System.out.println(user);
}

Query multiple columns and multiple rows, and the result of each row is a Map

Api

List<Map<String, Object>> queryForList(String sql);
List<Map<String, Object>> queryForList(String sql, Object... args);

The result of each row is a Map, the key is the lowercase column name, and the value is the value corresponding to the column.

the case

@Test
public void test11() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "select id,name from t_user where id>?";
    List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, 130);
    System.out.println(maps);
}

output

[{id=131, name=Andy Lau}, {id=132, name=Aaron Kwok}, {id=133, name=Jacky Cheung}, {id=134, name=Dawn}]

Query multiple columns and multiple rows, and map the results to javabeans

Api

<T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args)

the case

@Test
public void test12() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "select id,name from t_user where id>?";
    List<User> maps = jdbcTemplate.query(sql, new RowMapper<User>() {
        @Nullable
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt(1));
            user.setName(rs.getString(1));
            return user;
        }
    }, 130);
    System.out.println(maps);
}

run output

[User(id=131, name=Andy Lau), User(id=132, name=Aaron Kwok), User(id=133, name=Jacky Cheung), User(id=134, name=Liming)]

An easier way, useBeanPropertyRowMapper

@Test
public void test13() {
    DataSource dataSource = DataSourceUtils.getDataSource();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "select id,name from t_user where id>?";
    List<User> maps = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class), 130);
    System.out.println(maps);
}

output

[User(id=131, name=Andy Lau), User(id=132, name=Aaron Kwok), User(id=133, name=Jacky Cheung), User(id=134, name=Liming)]

Summarize

  1. Note: The method starting with getObject in JdbcTemplate requires that sql must return a record , otherwise an error will be reported
  2. BeanPropertyRowMapper can map row records to javabeans
  3. JdbcTemplate uses templates to operate jdbc very easily, and the code is particularly concise, but there is no dynamic sql function inside it, that is, the specified sql is dynamically generated through parameters. Mybatis is better at dynamic sql, and everyone uses it Time can be selected according to needs.

Case source code

Git address: 
https://gitee.com/javacode2018/spring-series 

Corresponding source code of this case: 
spring-series\lesson-003-jdbctemplate\src\main\java\com\javacode2018\jdbctemplate\demo1\Demo1Test.java

Passerby A All java case codes will be put on this in the future, everyone watch it, you can continue to pay attention to the dynamics.

Source: https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648936449&idx=2&sn=da1e98e5914821f040d5530e8ca9d9bc&scene=21#wechat_redirect

Guess you like

Origin blog.csdn.net/china_coding/article/details/130774433