本来、この記事は Spring トランザクションに関するものでしたが、トランザクションのほとんどのケースでは JdbcTemplate 関連の関数が使用されるため、最初に JdbcTemplate について説明します。
JdbcTemplateとは何ですか?
Java で db を操作する最も独創的な方法は純粋な jdbc です。操作するたびに、データベース ドライバーをロードし、接続を取得し、PreparedStatement を取得し、SQL を実行し、PreparedStatement を閉じ、接続を閉じるなどの処理が必要ですか。 db, 操作はまだ比較的複雑です, Spring は、JDBC 操作をカプセル化して簡単にするモジュールを提供します. この記事で説明するのは JdbcTemplate です. JdbcTemplate は Spring による JDBC のカプセル化であり、JDBC を簡単にすることを目的としています使用します。
JdbcTemplate がどのように機能するかを見てみましょう。
JdbcTemplate の使用手順
-
データソースの作成
-
JdbcTemplate、新しい JdbcTemplate(dataSource) を作成します
-
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、名前=リミン)]
要約する
- 注: JdbcTemplate の getObject で始まるメソッドでは、SQL が Record を返す必要があります。そうでない場合はエラーが報告されます。
- BeanPropertyRowMapper は行レコードを JavaBeans にマップできます
- 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