004Spring事务001JdbcTemplate
1 概述
1.1 简介
为了使JDBC更加易于使用,Spring在JDBC的API上定义了一个抽象层,以此建立一个JDBC存取框架。
JdbcTemplate类对可变部分采用CallBack回调接口方式实现,在CallBack接口实现类的Connection处于自动提交状态,在CallBack接口实现类的方法中不能进行事物管理。
1.2 目的
JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。
可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和DBUtils的风格非常接近。
2 导包
2.1 IOC容器需要的包
1 commons-logging-1.1.1.jar 2 spring-beans-4.0.0.RELEASE.jar 3 spring-context-4.0.0.RELEASE.jar 4 spring-core-4.0.0.RELEASE.jar 5 spring-expression-4.0.0.RELEASE.jar
2.2 AOP需要的包
1 spring-aop-4.0.0.RELEASE.jar 2 spring-aspects-4.0.0.RELEASE.jar
2.3 JdbcTemplate需要的包
1 spring-jdbc-4.0.0.RELEASE.jar 2 spring-orm-4.0.0.RELEASE.jar 3 spring-tx-4.0.0.RELEASE.jar
2.4 数据库驱动和数据源需要的包
1 c3p0-0.9.1.2.jar 2 mysql-connector-java-5.1.7-bin.jar
3 使用
3.1 数据库连接信息属性文件
在类路径下创建jdbc.properties文件。
1 user=root 2 password=root 3 jdbcUrl=jdbc:mysql:///query_data 4 driverClass=com.mysql.jdbc.Driver 5 initialPoolSize=30 6 minPoolSize=10 7 maxPoolSize=100 8 acquireIncrement=5 9 maxStatements=1000 10 maxStatementsPerConnection=10
3.2 配置XML文件
将数据库连接属性文件引入到XML文件中,并注册数据源Bean,使用SpEL表达式将外部文件中的配置信息赋值给数据源Bean。
还需要注册一个Spring框架中JDBC的核心组件JdbcTemplate的Bean,并设置dataSource属性引用数据源Bean。
1 <context:property-placeholder location="classpath:jdbc.properties"/> 2 <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 3 <property name="user" value="${user}"/> 4 <property name="password" value="${password}"/> 5 <property name="jdbcUrl" value="${jdbcUrl}"/> 6 <property name="driverClass" value="${driverClass}"/> 7 <property name="initialPoolSize" value="${initialPoolSize}"/> 8 <property name="minPoolSize" value="${minPoolSize}"/> 9 <property name="maxPoolSize" value="${maxPoolSize}"/> 10 <property name="acquireIncrement" value="${acquireIncrement}"/> 11 <property name="maxStatements" value="${maxStatements}"/> 12 <property name="maxStatementsPerConnection" value="${maxStatementsPerConnection}"/> 13 </bean> 14 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 15 <constructor-arg ref="comboPooledDataSource" /> 16 </bean>
3.3 获取JdbcTemplate组件
获取到组件之后就可以进行增删改查操作了。
1 public class JdbcTemplateTest { 2 ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); 3 4 @Test 5 public void test() throws Exception { 6 // 获取容器的JdbcTemplate组件。 7 JdbcTemplate jdbcTemplate = (JdbcTemplate) ioc.getBean(JdbcTemplate.class); 8 } 9 }
4 详细说明
4.1 回调类
4.1.1 预编译语句创建回调及存储过程创建回调
用于根据JdbcTemplate提供的Connection创建语句。
1 PreparedStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建PreparedStatement。 2 CallableStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建CallableStatement。
4.1.2 预编译语句设值回调
用于给预编译语句的参数设值。
1 PreparedStatementSetter:通过回调获取JdbcTemplate提供的PreparedStatement,由用户来给预编译语句的参数设值。 2 BatchPreparedStatementSetter:类似于PreparedStatementSetter,但用于批处理,需要指定批处理大小。
4.1.3 自定义功能回调
提供给用户一个扩展点,用户可以在指定类型的扩展点执行需要的操作,包括将预设值参数设置到预编译语句中。
1 ConnectionCallback:通过回调获取JdbcTemplate提供的Connection,用户可在该Connection执行操作。 2 StatementCallback:通过回调获取JdbcTemplate提供的Statement,用户可以在该Statement执行操作。 3 PreparedStatementCallback:通过回调获取JdbcTemplate提供的PreparedStatement,用户可以在该PreparedStatement执行操作。 4 CallableStatementCallback:通过回调获取JdbcTemplate提供的CallableStatement,用户可以在该CallableStatement执行操作。
4.1.4 结果集处理回调
通过回调处理ResultSet或将ResultSet转换为需要的形式。
1 RowMapper:用于封装结果集数据到指定类型,需要重写mapRow方法,每次封装一行数据,无需执行rs.next()。 2 RowCallbackHandler:用于处理结果集数据而无需保存到内存,需重写processRow方法,每次处理一行数据,无需执行rs.next()。 3 ResultSetExtractor:用于结果集数据提取,需要重写extractData方法,一次处理整个结果集,需要执行rs.next()。
4.2 主要方法
4.2.1 增加、删除、修改
执行一条语句:
1 // int update(String sql, Object... args); 2 String sql = "update bs_book set title=? where id=?"; 3 jdbcTemplate.update(sql, "新书", 10);
执行一条语句并返回自增主键:
1 // int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder); 2 String sql = "insert into bs_book(title, author) values(?, ?)"; 3 KeyHolder keyHolder = new GeneratedKeyHolder(); 4 jdbcTemplate.update(new PreparedStatementCreator() { 5 @Override 6 public PreparedStatement createPreparedStatement(Connection con) 7 throws SQLException { 8 PreparedStatement prepareStatement = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 9 prepareStatement.setString(1, "平凡的世界"); 10 prepareStatement.setString(2, "路遥"); 11 return prepareStatement; 12 } 13 }, keyHolder); 14 System.out.println("id=" + keyHolder.getKey().intValue());
批量执行多条语句:
1 // int[] batchUpdate(String sql, List<Object[]> batchArgs); 2 String sql = "insert into bs_book(title, author) values(?, ?)"; 3 List<Object[]> books = new ArrayList<Object[]>(); 4 books.add(new Object[]{"梦里花落知多少", "三毛"}); 5 books.add(new Object[]{"雨季不再来", "三毛"}); 6 int[] counts = jdbcTemplate.batchUpdate(sql, books); 7 for (int i : counts) { 8 System.out.println("count=" + i); 9 }
4.2.2 查询
查询一条数据并封装到基本类型中返回:
1 // <T> T queryForObject(String sql, Class<T> requiredType, Object... args); 2 String sql = "select count(id) from bs_book where author=?"; 3 int count = jdbcTemplate.queryForObject(sql, Integer.class, "三毛"); 4 System.out.println("count=" + count);
查询一条数据并封装到指定类型中返回:
1 // <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args); 2 String sql = "select * from bs_book where id=?"; 3 Book book = jdbcTemplate.queryForObject(sql, new RowMapper<Book>() { 4 @Override 5 public Book mapRow(ResultSet rs, int rowNum) throws SQLException { 6 System.out.println("rowNum=" + rowNum); 7 Book book = new Book(); 8 book.setId(rs.getInt("id")); 9 book.setTitle(rs.getString("title")); 10 book.setAuthor(rs.getString("author")); 11 return book; 12 } 13 }, "10"); 14 // 也可以使用RowMapper的实现类BeanPropertyRowMapper来自动封装结果。 15 // Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), "10"); 16 System.out.println("book=" + book);
查询一条数据并封装到Map中返回:
1 // Map<String,Object> queryForMap(String sql, Object... args); 2 String sql = "select * from bs_book where id=?"; 3 Map<String, Object> map = jdbcTemplate.queryForMap(sql, 10); 4 map.forEach((key, value) -> System.out.println(key + "=" + value));
查询多条数据并封装到基本类型中最后以List类型返回:
1 // <T> List<T> queryForList(String sql, Class<T> elementType, Object... args); 2 String sql = "select title from bs_book where id<?"; 3 List<String> list = jdbcTemplate.queryForList(sql, String.class, 10); 4 list.forEach(title -> System.out.println(title));
查询多条数据并封装到Map中最后以List类型返回:
1 // List<Map<String,Object>> queryForList(String sql, Object... args); 2 String sql = "select title, author from bs_book where id<?"; 3 List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, 10); 4 list.forEach(map -> System.out.println(map));
查询多条数据并封装到指定类型中最后以List类型返回:
1 // <T> List<T>query(String sql, RowMapper<T> rowMapper, Object... args); 2 String sql = "select * from bs_book where id<?"; 3 List<Book> books = jdbcTemplate.query(sql, new RowMapper<Book>(){ 4 @Override 5 public Book mapRow(ResultSet rs, int rowNum) throws SQLException { 6 Book book = new Book(); 7 book.setId(rs.getInt("id")); 8 book.setTitle(rs.getString("title")); 9 book.setAuthor(rs.getString("author")); 10 return book; 11 } 12 }, 10); 13 //也可以使用RowMapper的实现类BeanPropertyRowMapper来自动封装结果。 14 // List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class), 10); 15 books.forEach(book -> System.out.println(book));
查询多条数据并封装到指定类型中最后以自定义类型返回:
1 // <T> T query(String sql, ResultSetExtractor<T> rse, Object... args); 2 String sql = "select * from bs_book where id<?"; 3 Book book = jdbcTemplate.query(sql, new ResultSetExtractor<Book>() { 4 @Override 5 public Book extractData(ResultSet rs) throws SQLException, 6 DataAccessException { 7 Book book = new Book(); 8 while (rs.next()) { 9 book.setId(rs.getInt("id")); 10 book.setTitle(rs.getString("title")); 11 book.setAuthor(rs.getString("author")); 12 if ("百年孤独".equals(book.getTitle())) { 13 break; 14 } 15 } 16 return book; 17 } 18 }, 10);
查询多条数据并封装到指定类型中处理最后不返回:
1 // void query(String sql, RowCallbackHandler rch, Object... args); 2 String sql = "select * from bs_book where id<?"; 3 jdbcTemplate.query(sql, new RowCallbackHandler() { 4 @Override 5 public void processRow(ResultSet rs) throws SQLException { 6 Book book = new Book(); 7 book.setId(rs.getInt("id")); 8 book.setTitle(rs.getString("title")); 9 book.setAuthor(rs.getString("author")); 10 System.out.println(book); 11 } 12 }, 10);
5 使用具名参数
5.1 关于具名参数
相对于基于位置的参数,具名参数具有更好的可维护性,在SQL语句中参数较多时可以考虑使用具名参数。
在Spring中可以通过NamedParameterJdbcTemplate类的对象使用带有具名参数的SQL语句。
5.2 通过IOC容器创建NamedParameterJdbcTemplate对象
NamedParameterJdbcTemplate类没有提供无参的构造器,不能使用属性方式只能通过构造器方式创建Bean。
1 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 2 <constructor-arg ref="comboPooledDataSource" /> 3 </bean>
5.3 获取IOC容器的NamedParameterJdbcTemplate对象
1 NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class);
5.4 具名参数在SQL语句中的格式
可以在SQL语句中使用“:属性名”的方式进行参数传递。
1 INSERT INTO depts (id, name) VALUES (:id, :name)
5.5 具名参数传入并执行
5.5.1 使用Map
通过Map对象传入,Map的键是参数名,值是参数值。
1 // NamedParameterJdbcTemplate.update(String sql, Map<String, ?> paramMap); 2 public void test() { 3 String sql = "insert into employee(id, name) values(:id, :name)"; 4 HashMap<String, Object> map = new HashMap<>(); 5 map.put("id", 100); 6 map.put("name", "Tom"); 7 namedParameterJdbcTemplate.update(sql, map); 8 }
5.5.2 使用SqlParameterSource
通过SqlParameterSource对象传入,使用该接口下的BeanPropertySqlParameterSource的实例。
1 // NamedParameterJdbcTemplate.update(String sql, SqlParameterSource paramSource); 2 public void test() { 3 String sql = "insert into employee(id, name) values(:id, :name)"; 4 Employee employee = new Employee(100, "Sam"); 5 int update = namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee)); 6 System.out.println(update); 7 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)