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容器需要的包

commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2.2 AOP需要的包

spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar

2.3 JdbcTemplate需要的包

spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar

2.4 数据库驱动和数据源需要的包

c3p0-0.9.1.2.jar
mysql-connector-java-5.1.7-bin.jar

3 使用

3.1 数据库连接信息属性文件

在类路径下创建jdbc.properties文件。

user=root
password=root
jdbcUrl=jdbc:mysql:///query_data
driverClass=com.mysql.jdbc.Driver
initialPoolSize=30
minPoolSize=10
maxPoolSize=100
acquireIncrement=5
maxStatements=1000
maxStatementsPerConnection=10

3.2 配置XML文件

将数据库连接属性文件引入到XML文件中,并注册数据源Bean,使用SpEL表达式将外部文件中的配置信息赋值给数据源Bean。

还需要注册一个Spring框架中JDBC的核心组件JdbcTemplate的Bean,并设置dataSource属性引用数据源Bean。

<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${user}"/>
    <property name="password" value="${password}"/>
    <property name="jdbcUrl" value="${jdbcUrl}"/>
    <property name="driverClass" value="${driverClass}"/>
    <property name="initialPoolSize" value="${initialPoolSize}"/>
    <property name="minPoolSize" value="${minPoolSize}"/>
    <property name="maxPoolSize" value="${maxPoolSize}"/>
    <property name="acquireIncrement" value="${acquireIncrement}"/>
    <property name="maxStatements" value="${maxStatements}"/>
    <property name="maxStatementsPerConnection" value="${maxStatementsPerConnection}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="comboPooledDataSource" />
</bean>

3.3 获取JdbcTemplate组件

获取到组件之后就可以进行增删改查操作了。

public class JdbcTemplateTest {
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

    @Test
    public void test() throws Exception {
        // 获取容器的JdbcTemplate组件。
        JdbcTemplate jdbcTemplate = (JdbcTemplate) ioc.getBean(JdbcTemplate.class);
    }
}

4 详细说明

4.1 回调类

4.1.1 预编译语句创建回调及存储过程创建回调

用于根据JdbcTemplate提供的Connection创建语句。

PreparedStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建PreparedStatement。
CallableStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建CallableStatement。

4.1.2 预编译语句设值回调

用于给预编译语句的参数设值。

PreparedStatementSetter:通过回调获取JdbcTemplate提供的PreparedStatement,由用户来给预编译语句的参数设值。
BatchPreparedStatementSetter:类似于PreparedStatementSetter,但用于批处理,需要指定批处理大小。

4.1.3 自定义功能回调

提供给用户一个扩展点,用户可以在指定类型的扩展点执行需要的操作,包括将预设值参数设置到预编译语句中。

ConnectionCallback:通过回调获取JdbcTemplate提供的Connection,用户可在该Connection执行操作。
StatementCallback:通过回调获取JdbcTemplate提供的Statement,用户可以在该Statement执行操作。
PreparedStatementCallback:通过回调获取JdbcTemplate提供的PreparedStatement,用户可以在该PreparedStatement执行操作。
CallableStatementCallback:通过回调获取JdbcTemplate提供的CallableStatement,用户可以在该CallableStatement执行操作。

4.1.4 结果集处理回调

通过回调处理ResultSet或将ResultSet转换为需要的形式。

RowMapper:用于封装结果集数据到指定类型,需要重写mapRow方法,每次封装一行数据,无需执行rs.next()。
RowCallbackHandler:用于处理结果集数据而无需保存到内存,需重写processRow方法,每次处理一行数据,无需执行rs.next()。
ResultSetExtractor:用于结果集数据提取,需要重写extractData方法,一次处理整个结果集,需要执行rs.next()。

4.2 主要方法

4.2.1 增加、删除、修改

执行一条语句:

// int update(String sql, Object... args);
String sql = "update bs_book set title=? where id=?";
jdbcTemplate.update(sql, "新书", 10);

执行一条语句并返回自增主键:

// int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder);
String sql = "insert into bs_book(title, author) values(?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con)
            throws SQLException {
        PreparedStatement prepareStatement = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        prepareStatement.setString(1, "平凡的世界");
        prepareStatement.setString(2, "路遥");
        return prepareStatement;
    }
}, keyHolder);
System.out.println("id=" + keyHolder.getKey().intValue());

批量执行多条语句:

// int[] batchUpdate(String sql, List<Object[]> batchArgs);
String sql = "insert into bs_book(title, author) values(?, ?)";
List<Object[]> books = new ArrayList<Object[]>();
books.add(new Object[]{"梦里花落知多少", "三毛"});
books.add(new Object[]{"雨季不再来", "三毛"});
int[] counts = jdbcTemplate.batchUpdate(sql, books);
for (int i : counts) {
    System.out.println("count=" + i);
}

4.2.2 查询

查询一条数据并封装到基本类型中返回:

// <T> T queryForObject(String sql, Class<T> requiredType, Object... args);
String sql = "select count(id) from bs_book where author=?";
int count = jdbcTemplate.queryForObject(sql, Integer.class, "三毛");
System.out.println("count=" + count);

查询一条数据并封装到指定类型中返回:

// <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
String sql = "select * from bs_book where id=?";
Book book = jdbcTemplate.queryForObject(sql, new RowMapper<Book>() {
    @Override
    public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
        System.out.println("rowNum=" + rowNum);
        Book book = new Book();
        book.setId(rs.getInt("id"));
        book.setTitle(rs.getString("title"));
        book.setAuthor(rs.getString("author"));
        return book;
    }
}, "10");
// 也可以使用RowMapper的实现类BeanPropertyRowMapper来自动封装结果。
// Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), "10");
System.out.println("book=" + book);

查询一条数据并封装到Map中返回:

// Map<String,Object> queryForMap(String sql, Object... args);
String sql = "select * from bs_book where id=?";
Map<String, Object> map = jdbcTemplate.queryForMap(sql, 10);
map.forEach((key, value) -> System.out.println(key + "=" + value));

查询多条数据并封装到基本类型中最后以List类型返回:

// <T> List<T> queryForList(String sql, Class<T> elementType, Object... args);
String sql = "select title from bs_book where id<?";
List<String> list = jdbcTemplate.queryForList(sql, String.class, 10);
list.forEach(title -> System.out.println(title));

查询多条数据并封装到Map中最后以List类型返回:

// List<Map<String,Object>> queryForList(String sql, Object... args);
String sql = "select title, author from bs_book where id<?";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, 10);
list.forEach(map -> System.out.println(map));

查询多条数据并封装到指定类型中最后以List类型返回:

// <T> List<T>query(String sql, RowMapper<T> rowMapper, Object... args);
String sql = "select * from bs_book where id<?";
List<Book> books = jdbcTemplate.query(sql, new RowMapper<Book>(){
    @Override
    public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
        Book book = new Book();
        book.setId(rs.getInt("id"));
        book.setTitle(rs.getString("title"));
        book.setAuthor(rs.getString("author"));
        return book;
    }
}, 10);
//也可以使用RowMapper的实现类BeanPropertyRowMapper来自动封装结果。
// List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class), 10);
books.forEach(book -> System.out.println(book));

查询多条数据并封装到指定类型中最后以自定义类型返回:

// <T> T query(String sql, ResultSetExtractor<T> rse, Object... args);
String sql = "select * from bs_book where id<?";
Book book = jdbcTemplate.query(sql, new ResultSetExtractor<Book>() {
    @Override
    public Book extractData(ResultSet rs) throws SQLException,
            DataAccessException {
        Book book = new Book();
        while (rs.next()) {
            book.setId(rs.getInt("id"));
            book.setTitle(rs.getString("title"));
            book.setAuthor(rs.getString("author"));
            if ("百年孤独".equals(book.getTitle())) {
                break;
            }
        }
        return book;
    }
}, 10);

查询多条数据并封装到指定类型中处理最后不返回:

// void query(String sql, RowCallbackHandler rch, Object... args);
String sql = "select * from bs_book where id<?";
jdbcTemplate.query(sql, new RowCallbackHandler() {
    @Override
    public void processRow(ResultSet rs) throws SQLException {
        Book book = new Book();
        book.setId(rs.getInt("id"));
        book.setTitle(rs.getString("title"));
        book.setAuthor(rs.getString("author"));
        System.out.println(book);
    }
}, 10);

5 使用具名参数

5.1 关于具名参数

相对于基于位置的参数,具名参数具有更好的可维护性,在SQL语句中参数较多时可以考虑使用具名参数。

在Spring中可以通过NamedParameterJdbcTemplate类的对象使用带有具名参数的SQL语句。

5.2 通过IOC容器创建NamedParameterJdbcTemplate对象

NamedParameterJdbcTemplate类没有提供无参的构造器,不能使用属性方式只能通过构造器方式创建Bean。

<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg ref="comboPooledDataSource" />
</bean>

5.3 获取IOC容器的NamedParameterJdbcTemplate对象

NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class);

5.4 具名参数在SQL语句中的格式

可以在SQL语句中使用“:属性名”的方式进行参数传递。

INSERT INTO depts (id, name) VALUES (:id, :name)

5.5 具名参数传入并执行

5.5.1 使用Map

通过Map对象传入,Map的键是参数名,值是参数值。

// NamedParameterJdbcTemplate.update(String sql, Map<String, ?> paramMap);
public void test() {
    String sql = "insert into employee(id, name) values(:id, :name)";
    HashMap<String, Object> map = new HashMap<>();
    map.put("id", 100);
    map.put("name", "Tom");
    namedParameterJdbcTemplate.update(sql, map);
}

5.5.2 使用SqlParameterSource

通过SqlParameterSource对象传入,使用该接口下的BeanPropertySqlParameterSource的实例。

// NamedParameterJdbcTemplate.update(String sql, SqlParameterSource paramSource);
public void test() {
    String sql = "insert into employee(id, name) values(:id, :name)";
    Employee employee = new Employee(100, "Sam");
    int update = namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
    System.out.println(update);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值