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 }

 

posted @ 2022-01-24 23:26  执笔未来  阅读(58)  评论(0编辑  收藏  举报