(IV)JdbcTemplate

一、概述

  为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。

  作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低

  可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和我们之前使用过的DBUtils风格非常接近。

二、环境准备

  1、导入 jar 包

    ① IOC 容器所需要的 jar 包

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

    ② JdbcTemplate 所需要的 jar 包

spring-jdbc-4.0.0.RELEASE.jar

spring-orm-4.0.0.RELEASE.jar

spring-tx-4.0.0.RELEASE.jar

    ③ 数据库驱动和数据源

c3p0-0.9.5.jar

mysql-connector-java-5.1.7-bin.jar

  2、连接数据库基本信息属性文件

jdbc_username=root
jdbc_password=root
jdbc_url=jdbc:mysql://localhost:3306/jdbc_template
jdbc_driverClassName=com.mysql.jdbc.Driver

initialPoolSize=30
minPoolSize=10
maxPoolSize=100
acquireIncrement=5
maxStatements=1000
maxStatementsPerConnection=10

  3、在Spring配置文件中配置相关的bean

    ① 数据源对象

    <!--引入外部配置文件-->
    <context:property-placeholder location="db.properties"></context:property-placeholder>

    <!--
        ${} :取出配置文件总的值
        #{} :Spring 表达式的值
    -->
    <!-- 测试数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

    ② JdbcTemplate 对象

    <!--  Spring 提供了一个类 JdbcTemplate,我们用它操作数据库,导入 Spring 的数据库模块  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

三、持久化操作

  1、增删改操作

    方法:

JdbcTemplate.update(sql)            //执行 SQL 语句
JdbcTemplate.update(sql,Object... args)  //执行可以赋值参数的SQL语句

  

    案例:

    @Test
     public void testInsert() {
         //插入
         //jdbcTemplate.update("insert into emp  values(null,'张三',23,'男')");
         String sql = "insert into emp  values(null,?,?,?)";
         jdbcTemplate.update(sql, "李四",18,"女");
         
     }
     
     @Test
     public void testUpdate() {
         //更新
         //jdbcTemplate.update("update emp set  ename='王五',age=17,sex='男' where eid=1");
         String sql = "update emp set  ename=?,age=?,sex=? where eid=?";
         jdbcTemplate.update(sql, "张三",22,"女",1);
     }
     
     @Test
     public void testDelete() {
         //删除
         //jdbcTemplate.update("delete from emp where  eid = 3");
         String sql = "delete from emp where eid=?";
         jdbcTemplate.update(sql, 3);
     }

  2、批量增删改操作

    方法:

JdbcTemplate.batchUpdate(sql, List<Object[]>)
    Object[]封装了SQL语句每一次执行时所需要的参数
    List集合封装了SQL语句多次执行时的所有参数

  

    案例:

    @Test
     public void testBatchInsert() {
         //批量添加
         String sql = "insert into emp  values(null,?,?,?)";
         List<Object[]> list = new ArrayList<>();
         list.add(new Object[] {"a1",1,"男"});
         list.add(new Object[] {"a2",2,"女"});
         list.add(new Object[] {"a3",3,"女"});
         list.add(new Object[] {"a4",4,"男"});
         jdbcTemplate.batchUpdate(sql, list);
     }
     
     @Test
     public void testBatchUpdate() {
         //批量更新
         String sql = "update emp set  ename=?,age=?,sex=? where eid=?";
         List<Object[]> list = new ArrayList<>();
         list.add(new Object[] {"b1",11,"男",4});
         list.add(new Object[] {"b2",12,"男",5});
         jdbcTemplate.batchUpdate(sql, list);
     }
     
     @Test
     public void testBatchDelete() {
         //批量删除
         String sql = "delete from emp where eid = ?";
         List<Object[]> list = new ArrayList<>();
         list.add(new Object[] {6});
         list.add(new Object[] {7});
         jdbcTemplate.batchUpdate(sql, list);
     }

  3、查询单行

    方法:

jdbcTemplate.queryForObject(sql, RowMapper)用来获取单条数据
jdbcTemplate.queryForObject(sql, new  Object[]{}, RowMapper<T>)用来获取单条数据
jdbcTemplate.queryForObject(sql, RowMapper<T>,  Object... args)用来获取单条数据

  

    

    案例:

    @Test
     public void testQueryForObject() {
         //jdbcTemplate.queryForObject(sql,  RowMapper)用来获取单条数据
         //jdbcTemplate.queryForObject(sql, new  Object[]{} RowMapper)用来获取单条数据
         //jdbcTemplate.queryForObject(sql, RowMapper,  Object... args)用来获取单条数据
         
         RowMapper<Emp> rowMapper = new  BeanPropertyRowMapper<>(Emp.class); //将列名(字段名或字段名的别名)与属性名进行映射
         
         String sql = "select eid,ename,age,sex from  emp where eid=1";
         Emp emp = jdbcTemplate.queryForObject(sql,  rowMapper);
         System.out.println(emp);
         
         String sql2 = "select eid,ename,age,sex from  emp where eid=?";
         Emp emp2 = jdbcTemplate.queryForObject(sql2,  new Object[] {4}, rowMapper);
         System.out.println(emp2);
         
         String sql3 = "select eid,ename,age,sex from  emp where eid=?";
         Emp emp3 = jdbcTemplate.queryForObject(sql3,  rowMapper, 5);
         System.out.println(emp3);
     }

  4、查询多行

    方法:

jdbcTemplate.query(String sql,  RowMapper<T>);
jdbcTemplate.query(String sql, Object[]  args, RowMapper<T>);
jdbcTemplate.query(String sql, RowMapper<T>,  Object... args);

      RowMapper对象依然可以使用BeanPropertyRowMapper

    案例:

   @Test
     public void testQuery() {
         //查询多行记录
         //jdbcTemplate.query(String sql,  RowMapper<T>);
         //jdbcTemplate.query(String sql, Object[]  args, RowMapper<T>);
         //jdbcTemplate.query(String sql, RowMapper<T>,  Object... args);
         RowMapper<Emp> rowMapper = new  BeanPropertyRowMapper<>(Emp.class);
         
         String sql = "select eid,ename,age,sex from  emp";
         List<Emp> list = jdbcTemplate.query(sql,  rowMapper);
         for (Emp emp : list) {
              System.out.println(emp);
         }
         System.out.println("-----------");
         String sql2 = "select eid,ename,age,sex from  emp where eid > ?";
         List<Emp> list2 = jdbcTemplate.query(sql2, new  Object[] {2}, rowMapper);
         for (Emp emp : list2) {
              System.out.println(emp);
         }
         System.out.println("-----------");
         String sql3 = "select eid,ename,age,sex from  emp where eid > ?";
         List<Emp> list3 = jdbcTemplate.query(sql3,  rowMapper, 2);
         for (Emp emp : list3) {
              System.out.println(emp);
         }
     }

  5、查询单个值

    方法:

jdbcTemplate.queryForObject(sql, Class, Object... args);
jdbcTemplate.queryForObject(sql,  requiredType);
jdbcTemplate.queryForObject(sql,  requiredType, args);

    案例:

     @Test
     public void testQueryForSingleValue() {
         //jdbcTemplate.queryForObject(sql,  requiredType);
         //jdbcTemplate.queryForObject(sql,  requiredType, args);
         String sql = "select count(*) from emp";
         Integer count =  jdbcTemplate.queryForObject(sql, Integer.class);
         System.out.println(count);
         
         String sql2 = "select count(*) from emp where  eid > ?";
         Integer count2 =  jdbcTemplate.queryForObject(sql2, Integer.class, 2);
         System.out.println(count2);
     }

四、使用具名参数的JdbcTemplate

  1、关于具名参数

    在Hibernate的HQL查询中我们体验过具名参数的使用,相对于基于位置的参数,具名参数具有更好的可维护性,在SQL语句中参数较多时可以考虑使用具名参数。

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

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

    <!-- 配置一个具有 具名参数 供的 JdbcTemplate NamedParameterJdbcTemplate-->
    <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <!--使用构造器注入一个数据源-->
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

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

INSERT INTO employee(emp_name, salary) VALUES(:empName, :salary)

  

  4、具名参数传入

    ① 通过 Map 对象传入

    @Test
    public void test8() throws Exception {
        String sql = "INSERT INTO employee(emp_name, salary) VALUES(:empName, :salary)";

        //将所有具名参数的值放在 map 中
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("empName", "田七");
        paramMap.put("salary", "9987.89");

        namedParameterJdbcTemplate.update(sql, paramMap);
    }

      Map 的键是参数名,值是参数值;

    ② 通过 SqlParameterSource 对象传入

      

       代码示例:

    @Test
    public void test() throws Exception {
        String sql = "INSERT INTO employee(emp_name, salary) VALUES(:empName, :salary)";

        Employee employee = new Employee();
        employee.setEmpName("哈哈");
        employee.setSalary(998.98);

        int update = namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
        System.out.println("update = " + update);
    }

    bean 的属性名要与具名参数一致。

五、使用JdbcTemplate实现Dao

  通过 IOC 容器自动注入:

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

    代码示例:

@Repository
public class EmpDao {
     
     @Autowired
     private JdbcTemplate jdbcTemplate;
     
     public Emp getEmp(Integer id) {
         
     }
}

六、注意

  在给 SQL 赋值时,如果使用PreparedStatement给通配符赋值时,如果给赋值的是字符串类型,那它就会默认给所赋的值添加一对单引号。

  示例1:

     @Test
     public void testDel() {
         //错误的,不能通过 PreparedStatement 赋值,在批量删除,批量修改时
         //调用PreparedStatement中 setString()方法时,因为字符串传到SQL语句中去自动加单引号的,所以不能使用通配符赋值
         String sql = "delete from emp where eid in  (?)";
         String eids = "9,10,11";
         jdbcTemplate.update(sql, eids);  //delete from  emp where eid in ('9,10,11'); //只删除eid为9的记录
         
         //修改,使用拼接,不适用通配符赋值
         String sql2 = "delete from emp where eid in ("  + eids + ")";
         jdbcTemplate.update(sql2);
     }

  示例2:

     @Test
     public void testSelect() {
         //会使用PreparedStatement的 setString() 方法,会有单引号的拼接问题
         String sql = "select * from emp where ename  like '%?%'";
         //如果问号处传入的 是 字符串 "a",SQL 语句就变成 :select * from emp where ename like '%'a'%' ,发生错误!!!
         
         
         //修改,使用concat() 函数
         String sql2 = "select * from emp where ename  like concat('%', ?, '%')";
         jdbcTemplate.update(sql2, "a");  // SQL 语句:select * from emp where ename like '%a%'
     }
原文地址:https://www.cnblogs.com/niujifei/p/15483949.html