摘要:本文学习了如何在Spring中使用JdbcTemplate操作数据。
环境
Windows 10 企业版 LTSC 21H2 MySQL 5.7.40 Java 1.8 Maven 3.6.3 Spring 5.3.23
1 概述 JdbcTemplate是Spring框架提供的一个核心类,用于简化JDBC操作。它封装了JDBC访问数据库的模板代码,让开发者能够专注于业务逻辑。
JdbcTemplate的主要优势:
简化JDBC代码,减少模板代码
自动处理资源的获取和释放
统一异常处理,将SQLException异常转换为Spring的DataAccessException异常
提供回调接口,便于自定义数据处理逻辑
2 环境准备 2.1 数据 创建数据库表:
mysql 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE DATABASE IF NOT EXISTS test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE test; CREATE TABLE IF NOT EXISTS user ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR (100 ) NOT NULL , age INT NOT NULL , email VARCHAR (100 ) ); INSERT INTO user (name, age, email) VALUES ('张三' , 25 , 'zhangsan@example.com' ), ('李四' , 30 , 'lisi@example.com' ), ('王五' , 28 , 'wangwu@example.com' );
2.2 依赖 添加依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.3.23</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.3.23</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.49</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.8</version > </dependency >
2.3 实体类 创建实体类:
java 1 2 3 4 5 6 7 public class User { private Integer id; private String name; private Integer age; private String email; }
3 配置方式 3.1 XML配置 创建服务类:
java 1 2 3 4 5 6 7 public class UserService { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate (JdbcTemplate jdbcTemplate) { this .jdbcTemplate = jdbcTemplate; } }
创建配置文件:
spring.xml 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 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="userService" class ="com.example.service.UserService" > <property name ="jdbcTemplate" ref ="jdbcTemplate" /> </bean > <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/test?useSSL=false& serverTimezone=GMT%2B8" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> <property name ="initialSize" value ="10" /> <property name ="minIdle" value ="10" /> <property name ="maxActive" value ="20" /> <property name ="maxWait" value ="60000" /> </bean > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <constructor-arg ref ="dataSource" /> </bean > </beans >
创建启动类:
java 1 2 3 4 5 6 7 8 9 10 11 12 public class DemoApplication { public static void main (String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("spring.xml" ); UserService userService = context.getBean(UserService.class); System.out.println(userService); context.close(); } }
3.2 注解配置 修改服务类:
java 1 2 3 4 5 6 7 8 9 @Service public class UserService { private JdbcTemplate jdbcTemplate; @Autowired public void setJdbcTemplate (JdbcTemplate jdbcTemplate) { this .jdbcTemplate = jdbcTemplate; } }
创建配置类:
java 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 @Configuration @ComponentScan("com.example") public class DemoConfig { @Bean public DataSource dataSource () { DruidDataSource dataSource = new DruidDataSource (); dataSource.setDriverClassName("com.mysql.jdbc.Driver" ); dataSource.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=GMT%2B8" ); dataSource.setUsername("root" ); dataSource.setPassword("123456" ); dataSource.setInitialSize(10 ); dataSource.setMinIdle(10 ); dataSource.setMaxActive(20 ); dataSource.setMaxWait(60000 ); return dataSource; } @Bean public JdbcTemplate jdbcTemplate (DataSource dataSource) { return new JdbcTemplate (dataSource); } }
修改启动类:
java 1 2 3 4 5 6 7 8 9 10 11 12 public class DemoApplication { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (DemoConfig.class); UserService userService = context.getBean(UserService.class); System.out.println(userService); context.close(); } }
4 核心功能 4.1 普通更新 对应增删改操作。
常用方法:
java 1 2 3 4 5 6 public int update (final String sql) throws DataAccessException;public int update (String sql, @Nullable Object... args) throws DataAccessException;public int update (final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder) throws DataAccessException;
示例:
java 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 43 44 45 public int insert (User user) { String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)" ; int result = jdbcTemplate.update(sql, user.getName(), user.getAge(), user.getEmail()); System.out.println(result); return result; } public int insertWithId (User user) { String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)" ; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder (); int result = jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1 , user.getName()); ps.setInt(2 , user.getAge()); ps.setString(3 , user.getEmail()); return ps; }, keyHolder); System.out.println(result); user.setId(keyHolder.getKey().intValue()); System.out.println(user.getId()); return result; } public int updateById (User user) { String sql = "UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?" ; int result = jdbcTemplate.update(sql, user.getName(), user.getAge(), user.getEmail(), user.getId()); System.out.println(result); return result; } public int deleteById (Integer id) { String sql = "DELETE FROM user WHERE id = ?" ; int result = jdbcTemplate.update(sql, id); System.out.println(result); return result; }
4.2 普通查询 4.2.1 查询简单数据 常用方法:
java 1 2 3 4 5 6 7 8 9 public <T> T query (PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse) throws DataAccessException;public <T> T queryForObject (String sql, Class<T> requiredType) throws DataAccessException;public <T> T queryForObject (String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException;public <T> List<T> queryForList (String sql, Class<T> elementType) throws DataAccessException;public <T> List<T> queryForList (String sql, Class<T> elementType, @Nullable Object... args) throws DataAccessException;
示例:
java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int count () { String sql = "SELECT COUNT(*) FROM user" ; int result = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(result); return result; } public List<Integer> listId () { String sql = "SELECT id FROM user ORDER BY id" ; List<Integer> result = jdbcTemplate.queryForList(sql, Integer.class); System.out.println(result); return result; }
4.2.2 查询对象数据 常用方法:
java 1 2 3 4 5 6 7 8 public <T> T queryForObject (String sql, RowMapper<T> rowMapper) throws DataAccessException;public <T> T queryForObject (String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;public <T> List<T> query (String sql, RowMapper<T> rowMapper) throws DataAccessException;public <T> List<T> query (String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
使用RowMapper接口可以将结果集的每一行映射为一个Java对象。
开发者可以自定义RowMapper接口,也可以使用内置的BeanPropertyRowMapper接口通过属性名称自动映射。
示例:
java 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 public User findById (Integer id) { String sql = "SELECT * FROM user WHERE id = ?" ; User result = jdbcTemplate.queryForObject(sql, (resultSet, i) -> { User user = new User (); user.setId(resultSet.getInt("id" )); user.setName(resultSet.getString("name" )); user.setAge(resultSet.getInt("age" )); user.setEmail(resultSet.getString("email" )); return user; }, id); System.out.println(result); return result; } public List<User> list () { String sql = "SELECT * FROM user ORDER BY id" ; List<User> result = jdbcTemplate.query(sql, new BeanPropertyRowMapper <>(User.class)); System.out.println(result); return result; }
4.2.3 查询Map数据 常用方法:
java 1 2 3 4 5 6 7 8 public Map<String, Object> queryForMap (String sql) throws DataAccessException;public Map<String, Object> queryForMap (String sql, @Nullable Object... args) throws DataAccessException;public List<Map<String, Object>> queryForList (String sql) throws DataAccessException;public List<Map<String, Object>> queryForList (String sql, @Nullable Object... args) throws DataAccessException;
示例:
java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public Map<String, Object> findInfoById (Integer id) { String sql = "SELECT id, name FROM user WHERE id = ?" ; Map<String, Object> result = jdbcTemplate.queryForMap(sql, id); System.out.println(result); return result; } public List<Map<String, Object>> listInfo () { String sql = "SELECT id, name FROM user ORDER BY id" ; List<Map<String, Object>> result = jdbcTemplate.queryForList(sql); System.out.println(result); return result; }
4.3 批量更新 对应批量增删改操作。
常用方法:
java 1 2 public int [] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException;
示例:
java 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public int [] batchInsert(List<User> users) { String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)" ; int [] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter () { @Override public void setValues (PreparedStatement ps, int i) throws SQLException { User user = users.get(i); ps.setString(1 , user.getName()); ps.setInt(2 , user.getAge()); ps.setString(3 , user.getEmail()); } @Override public int getBatchSize () { return users.size(); } }); System.out.println(Arrays.stream(result).boxed().collect(Collectors.toList())); return result; } public int [] batchUpdate(List<User> users) { String sql = "UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?" ; int [] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter () { @Override public void setValues (PreparedStatement ps, int i) throws SQLException { User user = users.get(i); ps.setString(1 , user.getName()); ps.setInt(2 , user.getAge()); ps.setString(3 , user.getEmail()); ps.setInt(4 , user.getId()); } @Override public int getBatchSize () { return users.size(); } }); System.out.println(Arrays.stream(result).boxed().collect(Collectors.toList())); return result; } public int [] batchDelete(List<Integer> ids) { String sql = "DELETE FROM user WHERE id = ?" ; int [] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter () { @Override public void setValues (PreparedStatement ps, int i) throws SQLException { ps.setInt(1 , ids.get(i)); } @Override public int getBatchSize () { return ids.size(); } }); System.out.println(Arrays.stream(result).boxed().collect(Collectors.toList())); return result; }
4.2 复杂查询 可以使用NamedParameterJdbcTemplate具名参数模板实现复杂查询,需要配置NamedParameterJdbcTemplate具名参数模板。
4.2.1 配置具名参数模板 4.2.1.1 XML配置 修改服务类,增加具名参数模板:
java 1 2 3 4 5 6 7 8 9 10 11 public class UserService { private JdbcTemplate jdbcTemplate; private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void setJdbcTemplate (JdbcTemplate jdbcTemplate) { this .jdbcTemplate = jdbcTemplate; } public void setNamedParameterJdbcTemplate (NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this .namedParameterJdbcTemplate = namedParameterJdbcTemplate; } }
修改配置文件,增加具名参数模板:
spring.xml 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 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="userService" class ="com.example.service.UserService" > <property name ="jdbcTemplate" ref ="jdbcTemplate" /> <property name ="namedParameterJdbcTemplate" ref ="namedParameterJdbcTemplate" /> </bean > <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/test?useSSL=false& serverTimezone=GMT%2B8" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> <property name ="initialSize" value ="10" /> <property name ="minIdle" value ="10" /> <property name ="maxActive" value ="20" /> <property name ="maxWait" value ="60000" /> </bean > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <constructor-arg ref ="dataSource" /> </bean > <bean id ="namedParameterJdbcTemplate" class ="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" > <constructor-arg ref ="dataSource" /> </bean > </beans >
4.2.1.2 注解配置 修改服务类,增加具名参数模板:
java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Service public class UserService { private JdbcTemplate jdbcTemplate; private NamedParameterJdbcTemplate namedParameterJdbcTemplate; @Autowired public void setJdbcTemplate (JdbcTemplate jdbcTemplate) { this .jdbcTemplate = jdbcTemplate; } @Autowired public void setNamedParameterJdbcTemplate (NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this .namedParameterJdbcTemplate = namedParameterJdbcTemplate; } }
修改配置类,增加具名参数模板:
java 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 @Configuration @ComponentScan("com.example") public class DemoConfig { @Bean public DataSource dataSource () { DruidDataSource dataSource = new DruidDataSource (); dataSource.setDriverClassName("com.mysql.jdbc.Driver" ); dataSource.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=GMT%2B8" ); dataSource.setUsername("root" ); dataSource.setPassword("123456" ); dataSource.setInitialSize(10 ); dataSource.setMinIdle(10 ); dataSource.setMaxActive(20 ); dataSource.setMaxWait(60000 ); return dataSource; } @Bean public JdbcTemplate jdbcTemplate (DataSource dataSource) { return new JdbcTemplate (dataSource); } @Bean public NamedParameterJdbcTemplate namedParameterJdbcTemplate (DataSource dataSource) { return new NamedParameterJdbcTemplate (dataSource); } }
4.2.2 使用具名参数模板 复杂查询:
java 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 public List<User> listByConditions (String name, Integer minAge, Integer maxAge) { StringBuilder sql = new StringBuilder ("SELECT * FROM user WHERE 1=1 " ); MapSqlParameterSource params = new MapSqlParameterSource (); if (name != null && !name.isEmpty()) { sql.append("AND name LIKE :name " ); params.addValue("name" , "%" + name + "%" ); } if (minAge != null ) { sql.append("AND age >= :minAge " ); params.addValue("minAge" , minAge); } if (maxAge != null ) { sql.append("AND age <= :maxAge " ); params.addValue("maxAge" , maxAge); } sql.append("ORDER BY id" ); List<User> result = namedParameterJdbcTemplate.query(sql.toString(), params, new BeanPropertyRowMapper <>(User.class)); System.out.println(result); return result; } public List<User> listWithPagination (int offset, int limit) { String sql = "SELECT * FROM user ORDER BY id LIMIT :limit OFFSET :offset" ; MapSqlParameterSource params = new MapSqlParameterSource (); params.addValue("limit" , limit); params.addValue("offset" , offset); List<User> result = namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper <>(User.class)); System.out.println(result); return result; }
5 底层原理 JdbcTemplate是Spring框架中用于简化JDBC操作的核心类,其底层原理基于模板方法模式和回调机制,实现了对JDBC复杂操作的封装。
基本流程:
获取连接:从配置的DataSource中获取数据库连接。
创建语句:根据SQL语句创建PreparedStatement对象。
设置参数:通过PreparedStatementSetter设置SQL参数。
执行操作:执行SQL语句。
处理结果:通过RowMapper或ResultSetExtractor处理ResultSet结果集。
资源清理:自动关闭资源。
主要接口:
PreparedStatementCreator:用于创建PreparedStatement对象。
PreparedStatementSetter:用于设置PreparedStatement的参数。
ResultSetExtractor:用于提取ResultSet的结果。
RowMapper:用于将ResultSet的每一行映射为Java对象。
StatementCallback:用于执行任意SQL语句。
条