第七章、JdbcTemplate

概述

  为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。 作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。 可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和我们之前使用过的DBUtils风格非常接近。

一、环境/数据准备

      <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
  •  数据脚本
CREATE DATABASE db1022;
USE db1022;
CREATE TABLE book (
isbn VARCHAR (50) PRIMARY KEY COMMENT '书号',
book_name VARCHAR (100) COMMENT '书名',
price INT COMMENT '价格'
) ;

CREATE TABLE book_stock (
isbn VARCHAR (50) PRIMARY KEY COMMENT '书号',
stock INT
) ;

CREATE TABLE account (
username VARCHAR (50) PRIMARY KEY COMMENT '用户名',
balance INT COMMENT '用户余额'
) ;

INSERT INTO account (`username`,`balance`) VALUES ('Tom',300);
INSERT INTO account (`username`,`balance`) VALUES ('Jerry',400);

INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-001','book01',100);
INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-002','book02',200);


INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-001',10);
INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-002',20);

二、创建连接数据库基本信息属性文件

  • 创建 jdbc.properties
user=root
password=root
jdbcUrl=jdbc:mysql:///db1022?useSSL=false&serverTimezone=UTC
driverClass=com.mysql.jdbc.Driver

initialPoolSize=30
minPoolSize=10
maxPoolSize=100
acquireIncrement=5
maxStatements=1000
maxStatementsPerConnection=10
  •  application_jdbcTemplate.xml
<context:property-placeholder location="classpath:jdbc.properties"/>

<bean id="dataSource" 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="template"  class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

三、JdbcTemplate的增、删、改、查

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Book {
    private String book_name;
    private String price;
}
  • 测试类
public class JDBCTemplate_Test {

    private ApplicationContext ctx = null;
    private JdbcTemplate jdbcTemplate = null;

    {
        ctx = new ClassPathXmlApplicationContext("application_jdbcTemplate.xml");
        jdbcTemplate = (JdbcTemplate) ctx.getBean("template");
    }


    /**
     * 执行 INSERT
     */
    @Test
    public void testInsert() {
        String sql = "INSERT INTO book (`isbn`,`book_name`,`price`) VALUES (?,?,?);";
        jdbcTemplate.update(sql, "ISBN", "ISBN", 100);
    }

    /**
     * 执行UPDATE
     */
    @Test
    public void testUpdate() {
        String sql = "UPDATE book SET book_name = ? WHERE price = ?";
        jdbcTemplate.update(sql,"book01",100);
    }

    /**
     * 执行 DELETE
     */
    @Test
    public void testDelete() {
        String sql = "DELETE from book WHERE book_name = ?";
        jdbcTemplate.update(sql, "book01");
    }

    /**
     * 测试批量更新操作 最后一个参数是 Object[] 的 List 类型:因为修改一条记录需要一个 Object 数组,修改多条记录就需要一个
     * List 来存放多个数组。
     */
    @Test
    public void testBatchUpdate() {
        String sql = "INSERT INTO book(isbn, book_name, price) VALUES(?, ?, ?)";
        List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[] { "ISBN-006", "book06", "600" });
        batchArgs.add(new Object[] { "ISBN-007", "book07", "700" });
        batchArgs.add(new Object[] { "ISBN-008", "book08", "800" });
        batchArgs.add(new Object[] { "ISBN-009", "book09", "900" });

        jdbcTemplate.batchUpdate(sql, batchArgs);
    }

    /**
     * 从数据库中获取一条记录,实际得到对应的一个对象 注意:不是调用 queryForObject(String sql,Class<Employee> requiredType, Object... args) 方法!
     * 而需要调用queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)
     * 1、其中的 RowMapper 指定如何去映射结果集的行,常用的实现类为 BeanPropertyRowMapper
     * 2、使用SQL中的列的别名完成列名和类的属性名的映射,例如 last_name lastName
     * 3、不支持级联属性。 JdbcTemplate只能作为一个 JDBC 的小工具, 而不是 ORM 框架
     */
    @Test
    public void testQueryForObject() {
        String sql = "SELECT * FROM book WHERE isbn=?";
        RowMapper<Book> rowMapper = new BeanPropertyRowMapper<>(Book.class);
        //在将数据装入对象时需要调用set方法。
        Book book = jdbcTemplate.queryForObject(sql, rowMapper, "ISBN-003");
        System.out.println(book);
    }

    /**
     * 一次查询多个对象
     * 注意:调用的不是 queryForList 方法
     */
    @Test
    public void testQueryForList() {
        String sql = "SELECT * FROM book WHERE isbn like ?";
         RowMapper<Book> rowMapper = new BeanPropertyRowMapper<>(Book.class);
         List<Book> bookList = jdbcTemplate.query(sql, rowMapper, "%ISBN%");
        if (!CollectionUtils.isEmpty(bookList)) {
            bookList.forEach(user -> {
                System.out.println(user);
            });
        }
    }

    /**
     * 获取单个列的值或做统计查询
     * 使用 queryForObject(String sql, Class<Long> requiredType)
     */
    @Test
    public void testQueryForCount() {
        String sql = "SELECT count(isbn) FROM book";
        long count = jdbcTemplate.queryForObject(sql, Long.class);
        System.out.println(count);
    }
}

四、使用具名参数的NamedParameterJdbcTemplate

关于具名参数

String sql = "SELECT * FROM book WHERE isbn=?";
由上面可以看出使用?作为展位符,在填充参数的时候要考虑问号的顺序。具名参数很好的解决了这个问题,是我们在填充参数时不用考虑参数顺序
在Spring中可以通过NamedParameterJdbcTemplate类的对象使用带有具名参数的SQL语句。

4.1、通过IOC容器创建NamedParameterJdbcTemplate对象

     <!-- 配置可以使用具名参数的JDBCTemplate类对象 -->
        <bean id="namedTemplate"  class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <!-- 没有无参构造器,必须传入数据源或JdbcTemplate对象 -->
        <constructor-arg ref="dataSource"/>

4.2、具名参数传入方式有两种

(1)、通过Map对象传入

NamedParameterJdbcTemplate.update(String sql, Map<String, ?> map)
Map的键是参数名,值是参数值

(2)、通过SqlParameterSource对象传入

public class NamedParameterJdbcTemplate_Test {
    private ApplicationContext ctx = null;
    private NamedParameterJdbcTemplate template = null;

    {
        ctx = new ClassPathXmlApplicationContext("application_jdbcTemplate.xml");
        template = (NamedParameterJdbcTemplate) ctx.getBean("namedTemplate");
    }

    /**
     * 通过Map对象传入
     */
    @Test
    public void test_Method01(){
        //:p、:bn 具名参数名字随便起,冒号+名字
        String sql = "UPDATE book SET price = :p WHERE book_name = :bn";
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("bn","book01");
        paramMap.put("p","1000");
        System.out.println(template.update(sql, paramMap));
    }

    /**
     * 通过SqlParameterSource对象传入
     */
    @Test
    public void test_Method02(){
        //模拟Service曾直接传递给Dao曾一个具体的对象。
        String sql = "UPDATE book SET price = :price WHERE book_name = :book_name";
        Book book = new Book("book01","10000");
        //此时剧名参数名称必须好对象的属性名称保持一致
        SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(book);
        System.out.println(template.update(sql, sqlParameterSource));
    }
}

五、使用JdbcTemplate实现Dao

通过IOC容器自动注入:JdbcTemplate类是线程安全的,所以可以在IOC容器中声明它的单个实例,并将这个实例注入到所有的Dao实例中。

@Repository
public class EmployeeDao {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public Employee get(Integer id){
        //
    }
}
原文地址:https://www.cnblogs.com/jdy1022/p/13678829.html