Spring JdbcTemplate使用

目录

1 简介

2 创建JdbcTemplate对象

3 测试使用


1 简介

Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDBC模板类是第一种工作模式。

       JdbcTemplate类通过模板设计模式帮助我们消除了冗长的代码,只做需要做的事情(即可变部分),并且帮我们做哪些固定部分,如连接的创建及关闭。

       JdbcTemplate类对可变部分采用回调接口方式实现,如ConnectionCallback通过回调接口返回给用户一个连接,从而可以使用该连接做任何事情、StatementCallback通过回调接口返回给用户一个Statement,从而可以使用该Statement做任何事情等等,还有其他一些回调接口

Spring除了提供JdbcTemplate核心类,还提供了基于JdbcTemplate实现的NamedParameterJdbcTemplate类用于支持命名参数绑定、 SimpleJdbcTemplate类用于支持Java5+的可变参数及自动装箱拆箱等特性。

JdbcTemplate类支持的回调类:

  • 预编译语句及存储过程创建回调:用于根据JdbcTemplate提供的连接创建相应的语句;

PreparedStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建相关的PreparedStatement;

CallableStatementCreator:通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建相关的CallableStatement;

  • 预编译语句设值回调:用于给预编译语句相应参数设值;

         PreparedStatementSetter:通过回调获取JdbcTemplate提供的PreparedStatement,由用户来对相应的预编译语句相应参数设值;

         BatchPreparedStatementSetter:;类似于PreparedStatementSetter,但用于批处理,需要指定批处理大小;

  • 自定义功能回调:提供给用户一个扩展点,用户可以在指定类型的扩展点执行任何数量需要的操作;

         ConnectionCallback:通过回调获取JdbcTemplate提供的Connection,用户可在该Connection执行任何数量的操作;

         StatementCallback:通过回调获取JdbcTemplate提供的Statement,用户可以在该Statement执行任何数量的操作;

         PreparedStatementCallback:通过回调获取JdbcTemplate提供的PreparedStatement,用户可以在该PreparedStatement执行任何数量的操作;

         CallableStatementCallback:通过回调获取JdbcTemplate提供的CallableStatement,用户可以在该CallableStatement执行任何数量的操作;

  • 结果集处理回调:通过回调处理ResultSet或将ResultSet转换为需要的形式;

         RowMapper:用于将结果集每行数据转换为需要的类型,用户需实现方法mapRow(ResultSet rs, int rowNum)来完成将每行数据转换为相应的类型。

         RowCallbackHandler:用于处理ResultSet的每一行结果,用户需实现方法processRow(ResultSet rs)来完成处理,在该回调方法中无需执行rs.next(),该操作由JdbcTemplate来执行,用户只需按行获取数据然后处理即可。

         ResultSetExtractor:用于结果集数据提取,用户需实现方法extractData(ResultSet rs)来处理结果集,用户必须处理整个结果集;

2 创建JdbcTemplate对象

Spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类

  • 模板(template) :模板管理过程中固定的部分
  • 回调(callback) :回调处理自定义的数据访问代码

API介绍

JdbcTemplate构造方法说明
public JdbcTemplate(DataSource dataSource)构造方法,传递数据源做为参数

常用方法

方法名作用
execute()用于执行DDL语句,如:建表
update()用于执行DML语句,实现增删改操作
queryXxx()用于执行DQL语句,实现各种查询的操作

2.1 测试数据源

1、首先导入JAR

主要是不要忘了c3p0连接池的jar和数据库的jar

2、src下创建属性配置文件db.properties

基本信息配置

jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc\:mysql\:///test

其他信息配置

 initialPoolSize:初始化连接数量
 minPoolSize:最小连接数量
 maxPoolSize:最大连接数量
 acquireIncrement: 当连接池用完之后一次性获取的连接数量
 idleConnectionTestPeriod:根据一定的时间间隔检查连接池的连接数量 单位为秒
 maxIdleTime:最大空闲时间 单位为秒
 maxStatements:最大的maxStatements连接数量
 maxStatementsPerConnection:最大语句缓存

 3、配置XML(已经写注释了,就不多解释了)

配置Spring配置文件applicationContext.xml 

我们通常将数据库的配置信息单独放到一个文件中,这样也为了方便后期维护

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 导入资源文件
        读取db.properties文件中的数据 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- 配置C3P0数据源 -->
    <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>

        <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
    </bean>

    <!-- 配置Spring的jdbcTemplate 
        并注入一个dataSource数据源-->
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>    
    </bean>
</beans>

 

<context:property-placeholder location="classpath:db.properties"/>
<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="driverClass" value="${jdbc.driverClass}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

第一行代码:用来读取db.properties文件中的数据。

第二行代码:用来配置一个数据源,这里数据实现类来自C3P0中的一个属性类。其中属性的值就是来自于db.properties

第九行代码:配置一个JdbcTemplate实例,并注入一个dataSource数据源

4、测试是否已经连接数据库

package com.linjie.jdbctemplate;
/**
* @author LinJie E-mail:ash-ali@163.com
* @version 创建时间:2018年5月11日 下午4:42:55
* 测试
*/

import java.sql.SQLException;

import javax.sql.DataSource;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class testJDBC {
    private JdbcTemplate jdbcTemplate;
    private ApplicationContext context = null;

    //初始化连接池
    {
        context = new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
    }

    //测试是否连接数据库
    @Test
    public void testIsConnect() throws SQLException {
        DataSource dataSource = context.getBean(DataSource.class);
        System.out.println("连接成功"+dataSource.getConnection());
    }
}

结果(连接成功)

3 测试使用

1 增加、删除、修改操作:

1)增加、更新、删除(一条sql语句)(sql固定,不需要参数):

    (a) int update(final String sql)

      其中sql参数为需要传入的插入sql语句。

    (b)int update(PreparedStatementCreator psc)

public void test() {
        jdbcTemplate.update(new PreparedStatementCreator() {
            
            @Override
            public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                return conn.prepareStatement("insert into test(name) values('name1')");   
            }
        });
    }

 (c)如果需要返回新插入数据的主键,采用如下方法(使用KeyHolder keyholder=new GeneratedKeyHolder();获得主键,jdbcTemplate和NamedParameterJdbcTemplate都可以通过此方法获得主键):

       int update(PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)

public void test() {
        KeyHolder keyHolder = new GeneratedKeyHolder(); 
        jdbcTemplate.update(new PreparedStatementCreator() {
            
            @Override
            public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                
                return conn.prepareStatement("insert into test(name) values('name1')");  
                 
            }
        },keyHolder);
        int i = keyHolder.getKey().intValue();//这就是刚插入的数据的主键
    }

2)增加、更新、删除(一条sql语句)(sql需要注入参数填充‘?’):

  (a)int update(String sql, PreparedStatementSetter pss)

public void test() {
        String sql = "insert into test(name) values (?)";
        //返回的是更新的行数
        int count = jdbcTemplate.update(sql, new PreparedStatementSetter(){

            @Override
            public void setValues(PreparedStatement pstmt)
                    throws SQLException {
                pstmt.setObject(1, "name4"); 
            }
        });
    }

  (b)int update(String sql, Object[] args, int[] argTypes)

其中参数含义:   sql:预处理sql语句; args:sql需要注入的参数; argTypes:需要注入的sql参数的JDBC类型(java.sql.Types中来获取类型的常量);

public void test() {
        String sql = "insert into test(name,age,create_date) values (?,?,?)";
        Date now = new Date(System.currentTimeMillis());
        //返回的是更新的行数
        int count = jdbcTemplate.update(sql, new Object[]{"小明",14,now}, new int[]{Types.VARCHAR,Types.INTEGER,Types.DATE});
    }

(c)int update(String sql, Object... args)

其实内部还是调用方法a实现的,JdbcTemplate提供这种更简单的方式“update(String sql, Object... args)”来实现设值,所以只要当使用该种方式不满足需求时才应使用PreparedStatementSetter(上面方法a)。

public void test() {
        String sql = "insert into test(name,age,create_date) values (?,?,?)";
        Date now = new Date(System.currentTimeMillis());
        //返回的是更新的行数
        int count = jdbcTemplate.update(sql, "小明", 14, now);
    }
public void test() {
        String sql = "insert into test(name,age,create_date) values (?,?,?)";
        Date now = new Date(System.currentTimeMillis());
        //返回的是更新的行数
        int count = jdbcTemplate.update(sql, new Object[]{"小明",14,now});
    }

这两种实际上调用的都是该方法,由此可见Object...args实际上就是可变的数组,而数组长度是固定的,必须先定义一个数组,而Object...args在传递时参数可以任意,所以也可以传递一个固定的Object数组。

    (d)int update(PreparedStatementCreator psc)

使用该方法可以自己使用原始jdbc方式给预编译sql注入参数,来进行增加、删除、更新操作:

public void test(final Customer customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
        //方法局部必须是final的,匿名内部类中才能引用
        final String sql = "insert into test(name,age,create_date) values (?,?,?)";
        Date now = new Date(System.currentTimeMillis());
        //返回的是更新的行数
        int count = jdbcTemplate.update(new PreparedStatementCreator() {
            
            @Override
            public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                PreparedStatement ps = conn.prepareStatement(sql);
                ps.setString(1, customer.getName());
                ps.setInt(2, customer.getAge());
                ps.setDate(3, customer.getCreateDate());
                
                return ps;
            }
        });
        
    }

如果需要返回插入的主键,只能用此方法,增加KeyHolder参数:

public void test(final Customer customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
        KeyHolder keyHolder = new GeneratedKeyHolder(); 
        //方法局部必须是final的,匿名内部类中才能引用
        final String sql = "insert into test(name,age,create_date) values (?,?,?)";
        Date now = new Date(System.currentTimeMillis());
        //返回的是更新的行数
        int count = jdbcTemplate.update(new PreparedStatementCreator() {
            
            @Override
            public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //有的数据库版本不一样,需要添加第二个参数,不然会报错;          ps.setString(1, customer.getName());           ps.setInt(2, customer.getAge());           ps.setDate(3, customer.getCreateDate());           return ps;         }     },keyHolder);         int i = keyHolder.getKey().intValue();//这就是刚插入的数据的主键  }

3)批量增加、删除、更新数据(多条sql语句)

  (a)批量执行多条sql(固定的sql,不需要注入参数,但是sql格式不固定)

      int[] batchUpdate(final String[] sql)

参数是一个String数组,存放多条sql语句;返回值是int数组,即每条sql更新影响的行数。

  (b)批量执行多条sql(预处理sql,需要注入参数)

    int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)

参数sql:一条预处理sql(如果是批量处理预处理sql,那么sql的格式就是固定的,只填充参数而已);第二个参数就是回调类,前面有统一介绍回调类。

举两个例子,一个更新,一个插入:

批量插入:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
        
        String sql = "insert into test(name,age,create_date) values (?,?,?)";
        //返回的是更新的行数
        int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            
            @Override
            public void setValues(PreparedStatement ps, int i)
                    throws SQLException {
                //注入参数值
                ps.setString(1, customer.get(i).getName());
                ps.setInt(2, customer.get(i).getAge());
                ps.setDate(3, customer.get(i).getCreateDate());
            }
            
            @Override
            public int getBatchSize() {
                //批量执行的数量
                return customer.size();
            }
        });
        
    }
批量更新:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
        String sql = "update test set name = ?,age = ? where id = ?";
        //返回的是更新的行数
        int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            
            @Override
            public void setValues(PreparedStatement ps, int i)
                    throws SQLException {
                //注入参数值
                ps.setString(1, customer.get(i).getName());
                ps.setInt(2, customer.get(i).getAge());
                ps.setInt(3, customer.get(i).getId());
            }
            
            @Override
            public int getBatchSize() {
                //批量执行的数量
                return customer.size();
            }
        });
        
    }

(c)批量处理多条预处理sql语句还有下面几种简单方法(参数和前面类似,这里就不详解):

         int[] batchUpdate(String sql, List<Object[]> batchArgs);

         int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);

前面讲了增加、删除、更新操作,这节讲一下查询。

查询操作:

(一)查询一个值(不需要注入参数)

queryForObject(String sql, Class<T> requiredType);

注意:参数requiredType只能是String,Integer这种类型,不能是自定义的实体类型,只能返回一个值,不能映射对象(映射对象下面会说);

  sql:预处理sql;requiredType:查询单列结果的类型;

public void test() {
        String sql = "select count(*) from test";
        int count = jdbcTemplate.queryForObject(sql, Integer.class);
        
    }

(二)查询一个值(使用预处理sql,需要注入参数)

queryForObject(String sql, Object[] args, Class<T> requiredType);

public void test(Integer id) {
        String sql = "select name from test where id = ?";
        String name = jdbcTemplate.queryForObject(sql, new Object[]{id}, String.class);
    }

还有如下方式:queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType);

(三)查询单行记录,转换成一个对象(固定sql,不需要参数)

<T> T queryForObject(String sql, RowMapper<T> rowMapper)

public void test() {
        String sql = "select name,age from test where id = 10";
        Customer customer = jdbcTemplate.queryForObject(sql, new RowMapper<Customer>() {

            @Override
            public Customer mapRow(ResultSet rs, int i)
                    throws SQLException {
                Customer c = new Customer();
                c.setName(rs.getString("name"));
                c.setAge(rs.getInt("age"));
                return c;
            }
            
        });
    }

四)查询单行记录,转换成一个对象(预处理sql,需要注入参数)

<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)

public void test(Integer id) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
        String sql = "select name,age from test where id = ?";
        Customer customer = jdbcTemplate.queryForObject(sql, new Object[]{id}, new RowMapper<Customer>() {

            @Override
            public Customer mapRow(ResultSet rs, int paramInt)
                    throws SQLException {
                Customer c = new Customer();
                c.setName(rs.getString("name"));
                c.setAge(rs.getInt("age"));
                return c;
            }
            
        });
    }

也可以使用如下方式:(1)<T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);

          (2)<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);

(五)查询数据库中一列多行数据,即查询数据库中单列数据存入一个list中,方式如下:(固定sql,没参数)

  (a)List<Map<String, Object>> queryForList(String sql)

           这个方法封装成map放入list中,key:列名(Oracle数据库sql执行结果列名默认为大写,需要小写用as取别名,别名用双引号)  value:列的值

public void test() {//如果Oracle用这个sql查询,返回的列名就是NAME(大写的),对应Map里面的key就是NAME
        String sql = "select name from test where id > 0";
        //如果用这个sql查询,返回的列名就是name(小写的),对应Map里面的key就是name
        String sql2 = "select name as \"name\" from test where id > 0";
        
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);//这里用的是第一个sql
    }

(b)<T> List<T> queryForList(String sql, Class<T> elementType)

           这个方法就是直接将单类型数据存入List中。

      注意:这个T虽然是泛型,但是只支持Integer.class String.class 这种单数据类型的,自己定义的Bean不支持。(所以用来查询单列数据)

public void test() {
        
        String sql = "select name from test where id > 0";
        List<String> list = jdbcTemplate.queryForList(sql, String.class);
    }

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值