Spring's Road to God の第 42 章: JdbcTemplate で遊ぶ

本来、この記事は Spring トランザクションに関するものでしたが、トランザクションのほとんどのケースでは JdbcTemplate 関連の関数が使用されるため、最初に JdbcTemplate について説明します。

JdbcTemplateとは何ですか?

Java で db を操作する最も独創的な方法は純粋な jdbc です。操作するたびに、データベース ドライバーをロードし、接続を取得し、PreparedStatement を取得し、SQL を実行し、PreparedStatement を閉じ、接続を閉じるなどの処理が必要ですか。 db, 操作はまだ比較的複雑です, Spring は、JDBC 操作をカプセル化して簡単にするモジュールを提供します. この記事で説明するのは JdbcTemplate です. JdbcTemplate は Spring による JDBC のカプセル化であり、JDBC を簡単にすることを目的としています使用します。

JdbcTemplate がどのように機能するかを見てみましょう。

JdbcTemplate の使用手順

  1. データソースの作成

  2. JdbcTemplate、新しい JdbcTemplate(dataSource) を作成します

  3. JdbcTemplateのメソッドを呼び出してdbの追加、削除、変更、確認などの操作を行います。

    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);
    }

出力

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

t_user テーブルのデータ

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

上記のクエリはt_userテーブルのすべてのレコードを返し、コレクション (Map) を返します。Map はレコードの行を表し、key は列名、value は列に対応する値です。

jdbcTemplate.queryForList("select * from t_user")特に便利だと思いますか?データを取得するには、非常に簡単なコード行を実行するだけです。

より強力で便利な機能を引き続き探索してみましょう。

追加、削除、変更操作

JdbcTemplate の update で始まるメソッドは、追加、削除、変更操作を実行するために使用されます。一般的に使用されるメソッドをいくつか見てみましょう。

パラメータなし

アピ

int update(final String sql)

ケース

@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);
}

ケースがあります 1

アピ

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

ケース

? を 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);
}

参考状況2あり

アピ

int update(String sql, PreparedStatementSetter pss)

PreparedStatementSetter によるパラメータの設定は関数型インターフェイスです。内部には PreparedStatement パラメータを渡す setValues メソッドがあり、このパラメータを通じてパラメータの値を手動で設定できます。

ケース

@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);
}

自動インクリメント列の値を取得します

アピ

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

ケース

@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());
}

出力

新しいレコード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)

一括追加、削除、変更操作

アピ

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

ケース

@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);
    }
}

クエリ操作

単一行の列をクエリする

アピ

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

ケース

@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);
}

出力

通行人

db内の対応するデータ

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

注意

queryForObject の SQL クエリに結果がない場合、エラーが報告されます。

ID 0 のレコードが存在しない場合

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);
} 

実行時に例外がポップアップしEmptyResultDataAccessException、レコードが返されることを期待していましたが、実際にはレコードが見つからず、期待された結果と一致しないため、エラーが報告されます。

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)

この問題を解決するには、複数の行をクエリすることで解決する必要があります。つまり、以下で説明する関連メソッドは、queryForList結果がない場合に空の List を返します。この空の List について大騒ぎすることができます。

複数の行を含む列をクエリする

アピ

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);

知らせ:

上記の T はジェネリック型ですが、Integer.class String.class などの単一のデータ型のみをサポートしており、自己定義 Bean はサポートしていません。(単一列データのクエリに使用されるため)

elementType: どのタイプのクエリ結果を変換する必要がありますか? 文字列、整数、倍精度浮動小数点数など。

ケース

@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);
}

出力

list1:[アーロン・クォック、ジャッキー・チャン、夜明け]
リスト2:[アーロン・クォック、ジャッキー・
チャン、夜明け] リスト3:[アーロン・クォック、ジャッキー・チャン
、夜明け] リスト4:[アーロン・クォック、ジャッキー・チャン、夜明け]

SQLの結果:

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

単一行レコードをクエリし、そのレコードをオブジェクトに変換します。

アピ

<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);

上記のメソッドのパラメーターの中には、現在の行の結果をカスタム オブジェクトにマップできる行マッパーである rowMapper パラメーターがあります。

@FunctionalInterface
public interface RowMapper<T> {

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

}

次のように、JdbcTemplate は内部で ResultSet を走査し、ループ内で RowMapper#mapRow を呼び出して現在の行の結果を取得し、それを List にスローして返します。

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

ケース

@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);
}

出力

User(id=134, name=134)

注意

queryForObject の SQL クエリに結果がない場合、エラーが報告され、レコードの行が返される必要があります。

単一行レコードをクエリして、指定された Javabean を返します。

RowMapper には BeanPropertyRowMapper を実装するクラスがあり、結果を 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);
}

複数の列と複数の行をクエリすると、各行の結果がマップになります

アピ

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

各行の結果はマップであり、キーは小文字の列名、値は列に対応する値です。

ケース

@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);
}

出力

[{id=131, name=アンディ・ラウ}、{id=132, name=アーロン・クォック}、{id=133, name=ジャッキー・チャン}、{id=134, name=ドーン}]

複数の列と複数の行をクエリし、結果を JavaBeans にマッピングします。

アピ

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

ケース

@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);
}

実行出力

[ユーザー(id=131、名前=アンディ・ラウ)、ユーザー(id=132、名前=アーロン・クォック)、ユーザー(id=133、名前=ジャッキー・チャン)、ユーザー(id=134、名前=リミン)]

もっと簡単な方法としては、BeanPropertyRowMapper

@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);
}

出力

[ユーザー(id=131、名前=アンディ・ラウ)、ユーザー(id=132、名前=アーロン・クォック)、ユーザー(id=133、名前=ジャッキー・チャン)、ユーザー(id=134、名前=リミン)]

要約する

  1. 注: JdbcTemplate の getObject で始まるメソッドでは、SQL が Record を返す必要があります。そうでない場合はエラーが報告されます。
  2. BeanPropertyRowMapper は行レコードを JavaBeans にマップできます
  3. JdbcTemplate はテンプレートを使用して jdbc を非常に簡単に操作でき、コードは特に簡潔ですが、内部に動的 SQL 関数はありません。つまり、指定された SQL はパラメータを通じて動的に生成されます。Mybatis は動的 SQL が得意で、誰もがそれを使用しています。ニーズに応じて選択できます。

ケースのソースコード

Git アドレス: 
https://gitee.com/javacode2018/spring-series

この場合の対応するソースコード: 
spring-series\lesson-003-jdbctemplate\src\main\java\com\javacode2018\jdbctemplate\demo1\Demo1Test.java

通行人 A 今後、すべての Java ケース コードがこれに載せられる予定です。皆さんはそれを見て、引き続きダイナミクスに注目してください。

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

おすすめ

転載: blog.csdn.net/china_coding/article/details/130774433