在 Java 领域,数据持久化有几个常见的方案,有 Spring 自带的 JdbcTemplate 、有 MyBatis,还有 JPA,在这些方案中,最简单的就是 Spring 自带的 JdbcTemplate 了,这个东西虽然没有 MyBatis 那么方便,但是比起最开始的 Jdbc 已经强了很多了,它没有 MyBatis 功能那么强大,当然也意味着它的使用比较简单,事实上,JdbcTemplate 算是最简单的数据持久化方案了,本文就和大伙来说说这个东西的使用。
1. 基本配置 JdbcTemplate 基本用法实际上很简单,开发者在创建一个 SpringBoot 项目时,除了选择基本的 Web 依赖,再记得选上 Jdbc 依赖,以及数据库驱动依赖即可,如下:
项目创建成功之后,记得添加 Druid 数据库连接池依赖(注意这里可以添加专门为 Spring Boot 打造的 druid-spring-boot-starter
,而不是我们一般在 SSM 中添加的 Druid),所有添加的依赖如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-jdbc</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.27</version > <scope > runtime</scope > </dependency >
项目创建完后,接下来只需要在 application.properties 中提供数据的基本配置即可,如下:
1 2 3 4 spring.datasource.type =com.alibaba.druid.pool.DruidDataSource spring.datasource.username =root spring.datasource.password =123 spring.datasource.url =jdbc:mysql:///test01?useUnicode=true&characterEncoding=UTF-8
如此之后,所有的配置就算完成了,接下来就可以直接使用 JdbcTemplate 了?咋这么方便呢?其实这就是 SpringBoot 的自动化配置带来的好处,我们先说用法,一会来说原理。
2. 基本用法 首先我们来创建一个 User Bean,如下:
1 2 3 4 5 6 public class User { private Long id; private String username; private String address; }
然后来创建一个 UserService 类,在 UserService 类中注入 JdbcTemplate ,如下:
1 2 3 4 5 @Service public class UserService { @Autowired JdbcTemplate jdbcTemplate; }
好了,如此之后,准备工作就算完成了。
2.1 增 JdbcTemplate 中,除了查询有几个 API 之外,增删改统一都使用 update 来操作,自己来传入 SQL 即可。例如添加数据,方法如下:
1 2 3 public int addUser (User user) { return jdbcTemplate.update("insert into user (username,address) values (?,?);" , user.getUsername(), user.getAddress()); }
update 方法的返回值就是 SQL 执行受影响的行数。
这里只需要传入 SQL 即可,如果你的需求比较复杂,例如在数据插入的过程中希望实现主键回填,那么可以使用 PreparedStatementCreator,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int addUser2 (User user) { KeyHolder keyHolder = new GeneratedKeyHolder (); int update = jdbcTemplate.update(new PreparedStatementCreator () { @Override public PreparedStatement createPreparedStatement (Connection connection) throws SQLException { PreparedStatement ps = connection.prepareStatement("insert into user (username,address) values (?,?);" , Statement.RETURN_GENERATED_KEYS); ps.setString(1 , user.getUsername()); ps.setString(2 , user.getAddress()); return ps; } }, keyHolder); user.setId(keyHolder.getKey().longValue()); System.out.println(user); return update; }
实际上这里就相当于完全使用了 JDBC 中的解决方案了,首先在构建 PreparedStatement 时传入 Statement.RETURN_GENERATED_KEYS,然后传入 KeyHolder,最终从 KeyHolder 中获取刚刚插入数据的 id 保存到 user 对象的 id 属性中去。
你能想到的 JDBC 的用法,在这里都能实现,Spring 提供的 JdbcTemplate 虽然不如 MyBatis,但是比起 Jdbc 还是要方便很多的。
2.2 删 删除也是使用 update API,传入你的 SQL 即可:
1 2 3 public int deleteUserById (Long id) { return jdbcTemplate.update("delete from user where id=?" , id); }
当然你也可以使用 PreparedStatementCreator。
2.3 改 1 2 3 public int updateUserById (User user) { return jdbcTemplate.update("update user set username=?,address=? where id=?" , user.getUsername(), user.getAddress(),user.getId()); }
当然你也可以使用 PreparedStatementCreator。
2.4 查 查询的话,稍微有点变化,这里主要向大伙介绍 query 方法,例如查询所有用户:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public List<User> getAllUsers () { return jdbcTemplate.query("select * from user" , new RowMapper <User>() { @Override public User mapRow (ResultSet resultSet, int i) throws SQLException { String username = resultSet.getString("username" ); String address = resultSet.getString("address" ); long id = resultSet.getLong("id" ); User user = new User (); user.setAddress(address); user.setUsername(username); user.setId(id); return user; } }); }
查询的时候需要提供一个 RowMapper,就是需要自己手动映射,将数据库中的字段和对象的属性一一对应起来,这样。。。。嗯看起来有点麻烦,实际上,如果数据库中的字段和对象属性的名字一模一样的话,有另外一个简单的方案,如下:
1 2 3 public List<User> getAllUsers2 () { return jdbcTemplate.query("select * from user" , new BeanPropertyRowMapper <>(User.class)); }
至于查询时候传参也是使用占位符,这个和前文的一致,这里不再赘述。
2.5 其他 除了这些基本用法之外,JdbcTemplate 也支持其他用法,例如调用存储过程等,这些都比较容易,而且和 Jdbc 本身都比较相似,这里也就不做介绍了,有兴趣可以留言讨论。
3. 原理分析 那么在 SpringBoot 中,配置完数据库基本信息之后,就有了一个 JdbcTemplate 了,这个东西是从哪里来的呢?源码在 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
类中,该类源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Configuration @ConditionalOnClass({ DataSource.class, JdbcTemplate.class }) @ConditionalOnSingleCandidate(DataSource.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) @EnableConfigurationProperties(JdbcProperties.class) public class JdbcTemplateAutoConfiguration { @Configuration static class JdbcTemplateConfiguration { private final DataSource dataSource; private final JdbcProperties properties; JdbcTemplateConfiguration(DataSource dataSource, JdbcProperties properties) { this .dataSource = dataSource; this .properties = properties; } @Bean @Primary @ConditionalOnMissingBean(JdbcOperations.class) public JdbcTemplate jdbcTemplate () { JdbcTemplate jdbcTemplate = new JdbcTemplate (this .dataSource); JdbcProperties.Template template = this .properties.getTemplate(); jdbcTemplate.setFetchSize(template.getFetchSize()); jdbcTemplate.setMaxRows(template.getMaxRows()); if (template.getQueryTimeout() != null ) { jdbcTemplate .setQueryTimeout((int ) template.getQueryTimeout().getSeconds()); } return jdbcTemplate; } } @Configuration @Import(JdbcTemplateConfiguration.class) static class NamedParameterJdbcTemplateConfiguration { @Bean @Primary @ConditionalOnSingleCandidate(JdbcTemplate.class) @ConditionalOnMissingBean(NamedParameterJdbcOperations.class) public NamedParameterJdbcTemplate namedParameterJdbcTemplate ( JdbcTemplate jdbcTemplate) { return new NamedParameterJdbcTemplate (jdbcTemplate); } } }
从这个类中,大致可以看出,当当前类路径下存在 DataSource 和 JdbcTemplate 时,该类就会被自动配置,jdbcTemplate 方法则表示,如果开发者没有自己提供一个 JdbcOperations 的实例的话,系统就自动配置一个 JdbcTemplate Bean(JdbcTemplate 是 JdbcOperations 接口的一个实现)。好了,不知道大伙有没有收获呢?