zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring事务专题之一、JdbcTemplate实现增删改查

Spring事务 实现 专题 之一 增删 改查 JdbcTemplate
2023-09-27 14:26:04 时间
    <p>本来这篇文章要写spring事务的,但是事务中大部分案例会用到JdbcTemplate相关的功能,所以先把JdbcTemplate拿出来说一下。</p>

什么是JdbcTemplate?

大家来回顾一下,java中操作db最原始的方式就是纯jdbc了,是不是每次操作db都需要加载数据库驱动、获取连接、获取PreparedStatement、执行sql、关闭PreparedStatement、关闭连接等等,操作还是比较繁琐的,spring中提供了一个模块,对jdbc操作进行了封装,使其更简单,就是本文要讲的JdbcTemplate,JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。

下面我们来看一下JdbcTemplate到底怎么玩的?

JdbcTemplate使用步骤

  1. 创建数据源DataSource

  2. 创建JdbcTemplate,new JdbcTemplate(dataSource)

  3. 调用JdbcTemplate的方法操作db,如增删改查

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

输出

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

t_user表数据

  1. mysql> select id,name from t_user;
  2. +-----+---------------+
  3. | id | name |
  4. +-----+---------------+
  5. | 114 | 路人 |
  6. | 115 | java高并发 |
  7. | 116 | spring系列 |
  8. +-----+---------------+
  9. 3 rows in set (0.00 sec)

上面查询返回了t_user表所有的记录,返回了一个集合,集合中是一个Map,Map表示一行记录,key为列名,value为列对应的值。

有没有感觉到特别的方便,只需要jdbcTemplate.queryForList("select * from t_user")这么简单的一行代码,数据就被获取到了。

下面我们继续探索更强大更好用的功能。

增加、删除、修改操作

JdbcTemplate中以update开头的方法,用来执行增、删、改操作,下面来看几个常用的。

无参情况

Api

  1. int update(final String sql)

案例

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

有参情况1

Api

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

案例

sql中使用?作为占位符。

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

有参情况2

Api

  1. int update(String sql, PreparedStatementSetter pss)

通过PreparedStatementSetter来设置参数,是个函数式接口,内部有个setValues方法会传递一个PreparedStatement参数,我们可以通这个参数手动的设置参数的值。

案例

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

获取自增列的值

Api

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

案例

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

输出

  1. 新记录id122
  1. mysql> select id,name from t_user;
  2. +-----+-----------------------+
  3. | id | name |
  4. +-----+-----------------------+
  5. | 114 | 路人 |
  6. | 115 | java高并发 |
  7. | 116 | spring系列 |
  8. | 117 | maven系列 |
  9. | 118 | mysql系列 |
  10. | 122 | 获取自增列的值 |
  11. +-----+-----------------------+
  12. 6 rows in set (0.00 sec)

批量增删改操作

Api

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

案例

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

查询操作

查询一列单行

Api

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

案例

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

输出

  1. 路人

db中对应数据

  1. mysql> select name from t_user where id = 114;
  2. +--------+
  3. | name |
  4. +--------+
  5. | 路人 |
  6. +--------+
  7. 1 row in set (0.00 sec)

使用注意

若queryForObject中sql查询无结果时,会报错

如id为0的记录不存在

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

运行,会弹出一个异常EmptyResultDataAccessException,期望返回一条记录,但实际上却没有找到记录,和期望结果不符,所以报错了

  1. org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
  2. at org.springframework.dao.support.DataAccessUtils.nullableSingleResult(DataAccessUtils.java:97)
  3. at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:784)

这种如何解决呢,需要用到查询多行的方式来解决了,即下面要说到的queryForList相关的方法,无结果的时候会返回一个空的List,我们可以在这个空的List上面做文章。

查询一列多行

Api

以queryForList开头的方法。

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

注意:

上面这个T虽然是泛型,但是只支持Integer.class String.class 这种单数据类型的,自己定义的Bean不支持。(所以用来查询单列数据)

elementType:查询结果需要转换为哪种类型?如String、Integer、Double。

案例

  1. @Test
  2. public void test8() {
  3. DataSource dataSource = DataSourceUtils.getDataSource();
  4. JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
  5. //<T> List<T> queryForList(String sql, Class<T> elementType);
  6. List<String> list1 = jdbcTemplate.queryForList("select name from t_user where id>131", String.class);
  7. System.out.println("list1:" + list1);
  8. //<T> List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args);
  9. List<String> list2 = jdbcTemplate.queryForList("select name from t_user where id>?", String.class, 131);
  10. System.out.println("list2:" + list2);
  11. //<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType);
  12. List<String> list3 = jdbcTemplate.queryForList("select name from t_user where id>?", new Object[]{131}, String.class);
  13. System.out.println("list3:" + list3);
  14. //<T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType);
  15. List<String> list4 = jdbcTemplate.queryForList("select name from t_user where id>?", new Object[]{131}, new int[]{java.sql.Types.INTEGER}, String.class);
  16. System.out.println("list4:" + list4);
  17. }

输出

  1. list1:[郭富城, 张学友, 黎明]
  2. list2:[郭富城, 张学友, 黎明]
  3. list3:[郭富城, 张学友, 黎明]
  4. list4:[郭富城, 张学友, 黎明]

sql结果:

  1. mysql> select name from t_user where id>131;
  2. +-----------+
  3. | name |
  4. +-----------+
  5. | 郭富城 |
  6. | 张学友 |
  7. | 黎明 |
  8. +-----------+
  9. 3 rows in set (0.00 sec)

查询单行记录,将记录转换成一个对象

Api

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

上面这些方法的参数中都有一个rowMapper参数,行映射器,可以将当前行的结果映射为一个自定义的对象。

  1. @FunctionalInterface
  2. public interface RowMapper<T> {
  3. /**
  4. * @param ResultSet 结果集
  5. * @param 当前结果集的第几行
  6. * @return 当前行的结果对象,将当前行的结果映射为一个自定义的对象返回
  7. */
  8. @Nullable
  9. T mapRow(ResultSet rs, int rowNum) throws SQLException;
  10. }

JdbcTemplate内部会遍历ResultSet,然后循环调用RowMapper#mapRow,得到当前行的结果,将其丢到List中返回,如下:

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

案例

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

输出

  1. User(id=134, name=134)

使用注意

当queryForObject中sql查询无结果的时候,会报错,必须要返回一行记录

查询单行记录,返回指定的javabean

RowMapper 有个实现了类 BeanPropertyRowMapper,可以将结果映射为javabean。

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

查询多列多行,每行结果为一个Map

Api

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

每行结果为一个Map,key为列名小写,value为列对应的值。

案例

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

输出

  1. [{id=131, name=刘德华}, {id=132, name=郭富城}, {id=133, name=张学友}, {id=134, name=黎明}]

查询多列多行,将结果映射为javabean

Api

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

案例

  1. @Test
  2. public void test12() {
  3. DataSource dataSource = DataSourceUtils.getDataSource();
  4. JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
  5. String sql = "select id,name from t_user where id>?";
  6. List<User> maps = jdbcTemplate.query(sql, new RowMapper<User>() {
  7. @Nullable
  8. @Override
  9. public User mapRow(ResultSet rs, int rowNum) throws SQLException {
  10. User user = new User();
  11. user.setId(rs.getInt(1));
  12. user.setName(rs.getString(1));
  13. return user;
  14. }
  15. }, 130);
  16. System.out.println(maps);
  17. }

运行输出

  1. [User(id=131, name=刘德华), User(id=132, name=郭富城), User(id=133, name=张学友), User(id=134, name=黎明)]

更简单的方式,使用BeanPropertyRowMapper

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

输出

  1. [User(id=131, name=刘德华), User(id=132, name=郭富城), User(id=133, name=张学友), User(id=134, name=黎明)]

总结

  1. 使用注意:JdbcTemplate中的getObject开头的方法,要求sql必须返回一条记录,否则会报错
  2. BeanPropertyRowMapper可以将行记录映射为javabean
  3. JdbcTemplate采用模板的方式操作jdbc变的特别的容易,代码特别的简洁,不过其内部没有动态sql的功能,即通过参数,动态生成指定的sql,mybatis在动态sql方面做的比较好,大家用的时候可以根据需求进行选择。

案例源码

  1. git地址:
  2. https://gitee.com/javacode2018/spring-series
  3. 本文案例对应源码:
  4. spring-series\lesson-003-jdbctemplate\src\main\java\com\javacode2018\jdbctemplate\demo1\Demo1Test.java

本博客所有系列案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。

继续收门徒,月薪 4W 以下的可以来找我,4W 以上的靠你们自己的天赋了

最新资料

  1. 尚硅谷 Java 学科全套教程(总 207.77GB)
  2. 2021 最新版 Java 微服务学习线路图 + 视频
  3. 阿里技术大佬整理的《Spring 学习笔记.pdf》
  4. 阿里大佬的《MySQL 学习笔记高清.pdf》
  5. 2021 版 java 高并发常见面试题汇总.pdf
  6. Idea 快捷键大全.pdf

来源:http://www.itsoku.com/course/12/124