码迷,mamicode.com
首页 > 数据库 > 详细

Spring的JDBC详解

时间:2016-04-29 16:17:25      阅读:620      评论:0      收藏:0      [点我收藏+]

标签:

Spring的JDBC详解

一、引言

1.JDBC回顾

传统应用程序开发中,进行JDBC编程是相当繁琐的,但却是我们入门必须掌握的技能。

步骤如下:
1.获取JDBC连接
2.声明SQL
3.预编译SQL
4.执行SQL
5.处理结果集
6.释放结果集
7.释放Statement
8.提交事务
9.处理异常并回滚事务
10.释放JDBC连接

缺点:1.冗长、重复
2.显示事务控制
3.每个步骤不可获取
4.显示处理受检查异常
Spring JDBC
声明SQL
处理结果集
优点:
    1.简单、简洁
    2.Spring事务管理
    3.只做需要做的
    4.一致的非检查异常体系

2.Spring对JDBC的支持

Spring通过抽象JDBC访问并一致的API来简化JDBC编程的工作量。我们只需要声明SQL、调用合适的SpringJDBC框架API、处理结果集即可。事务由Spring管理,并将JDBC受查异常转换为Spring一致的非受查异常,从而简化开发。

Spring主要提供JDBC模板方式、关系数据库对象方式和SimpleJbbc方式三种

JDBC模板方式:Spring JDBC框架提供以下几种模板类来简化JDBC编程,实现GoF模板设计模式,将可变部分和非可变部分分离,可变部分采用回调接口方式由用户来实现:如JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcTemplate。

关系数据库操作对象化方式:
SpringJDBC框架提供了将关系数据库操作对象化的表示形式,从而使用户可以采用面向对象编程来完成对数据库的访问,如MappingSqlQuery、SqlUpdate、SqlCall、SqlFunction、StoredProcedure等类。这些类的实现一旦建立即可重用并且是线程安全的。

SimpleJdbc方式:Spring JDBC框架还提供了SimpleJdbc方式来简化JDBC编程,SimpleJdbcInsert、SimpleJdbcCall来简化数据库表插入、存储过程或函数访问。

Spring JDBC还提供了一些强大的工具类,如DataSourceUtils来再必要的好似好手工获取数据库连接等。

3.Spirng的JDBC架构

JDBC抽象框架由四部分组成:datasource support core object

support包:将JDBC异常转换为DAO非检查异常转换类、一些工具类如JdbcUtils等。

datasource包:提供简化访问JDBC数据源(javax.sql.DataSource实现)工具类,并提供了一些DataSource简单实现类从而能使从这些DataSource获取的连击能自动得到Spring管理事务支持

core包:提供JDBC模板类实现及可变部分的回调接口,还提供SimpleJdbcInsert等简单辅助类。

object包:提供关系数据库的对象表示形式,如MappingSqlQuery、SqlUpdate、SqlCall、SqlFunction、StoredProcedure等类,该包是基于core包JDBC模板类实现

1.准备需要的jar包并添加到类路径中

//JDBC抽象框架模块
org.springframework.jdbc-3.0.1.RELEASE-A.jar
//Spring事务管理及一致的DAO访问及非检查异常模块
org.springframework.transaction-3.0.1.RELEASE-A.jar
//mysql驱动
mysql-connector-java-5.1.7-bin.jar

2.准备数据库支持

create database study_db;

use study_db;

create table user(
    u_id int primary key auto_icrement, --用户编号
    u_name varchar(20),--用户名
    u_pass varchar(20),--密码
    type_id--用户类型
)

create table user_type(
    t_id int primary key auto_icrement,--用户类型编号
    t_name varchar(20)--类型名称
)

--为user表type_id列添加对应user_type表的外键约束
alter table user add
constraint FK_type_id foreign key (type_id) references user_type(t_id)

insert into user_type(t_name) values(‘系统管理员‘);
insert into user_type(t_name) values(‘网站管理员‘);
insert into user_type(t_name) values(‘普通用户‘);

insert into user(u_name,u_pass,type_id) values(‘Admin1‘,‘Admin1‘,1); 
insert into user(u_name,u_pass,type_id) values(‘Admin2‘,‘Admin2‘,2);
insert into user(u_name,u_pass,type_id) values(‘Users1‘,‘Users1‘,3);

传统JDBC编程替代方案
在使用JdbcTemplate模板类时必须通过DataSource获取数据库连接,Spring JDBC提供了DriverManagerDataSource实现,

3.编写数据库表对应实体类

package com.lizhenhua.test.pojo;

public class User {
    private Integer uid;
    private String username;
    private String password;
    private Integer typeId;

    @Override
    public String toString() {
        return "User [password=" + password + ", typeId=" + typeId + ", uid="
                + uid + ", username=" + username + "]";
    }
    public User() {
        super();
    }
    public User(Integer uid, String username, String password, Integer typeId) {
        super();
        this.uid = uid;
        this.username = username;
        this.password = password;
        this.typeId = typeId;
    }
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getTypeId() {
        return typeId;
    }
    public void setTypeId(Integer typeId) {
        this.typeId = typeId;
    }

}

这是一个普通的User类,在Spring中称之为POJO。

4.编写测试类

package com.lizhenhua.test;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.BeforeClass;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import com.lizhenhua.test.pojo.User;

public class Test {
    private static JdbcTemplate jdbcTemplate;
    @BeforeClass
    public static void setUpClass(){
        //url指定了数据库study_db
        String url="jdbc:mysql://localhost:3306/study_db";
        //数据库用户名
        String username = "root";
        //数据库密码
        String password = "root";
        //声明并初始化数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        //使用数据源对象创建JdbcTemplate对象,该对象是线程安全的
        jdbcTemplate =  new JdbcTemplate(dataSource);
    }
    @org.junit.Test
    public void test(){
        //1.声明SQL
        String sql = "SELECT * FROM USER";
        jdbcTemplate.query(sql, new RowCallbackHandler(){

            @Override
            public void processRow(ResultSet rs) throws SQLException {
                //2.处理结果集
                User user = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4));
                //3.打印数据库用户信息
                System.out.println(user);
            }

        });
    }
}

@BeforeClass:表示在所有测试方法之前执行,且只执行一次。

JdbcTemplate执行流程:首先定义SQL,其次调用JdbcTemplate方法执行SQL,最后通过RowCallbackHandler回调处理ResultSet结果集。

二、Spring的JDBC类

2.1JdbcTemplate

如何使用JdbcTemplate增删改查
package com.lizhenhua.test;

import java.sql.ResultSet;
import java.sql.SQLException;


import org.junit.BeforeClass;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import com.lizhenhua.test.pojo.User;

public class Test {
    private static JdbcTemplate jdbcTemplate;
    @BeforeClass
    public static void setUpClass(){
        String url="jdbc:mysql://localhost:3306/study_db";
        String username = "root";
        String password = "root";
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        jdbcTemplate =  new JdbcTemplate(dataSource);
    }

    public void test(){
        //1.声明SQL
        String sql = "SELECT * FROM USER";
        jdbcTemplate.query(sql, new RowCallbackHandler(){

            @Override
            public void processRow(ResultSet rs) throws SQLException {
                //2.处理结果集
                User user = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4));
                System.out.println(user);
            }

        });
    }
    @org.junit.Test
    public void testCURD(){
        insert();
        update();
        drop();
        select();

    }
    private void select() {

        //1.声明SQL
        String sql = "SELECT * FROM USER";
        jdbcTemplate.query(sql, new RowCallbackHandler(){

            @Override
            public void processRow(ResultSet rs) throws SQLException {
                //2.处理结果集
                User user = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4));
                System.out.println(user);
            }

        });
    }
    private void drop() {
        int i = jdbcTemplate.update("delete from user where u_id = ? ",9);
        //判断是否删除study03成功
        org.junit.Assert.assertEquals(1, i);
    }
    private void update() {
        int i = jdbcTemplate.update("update user set u_pass=? where u_id = ?",new Object[] {"study02000000",7});
        //判断是否修改成功
        org.junit.Assert.assertEquals(1, i);
    }
    private void insert() { 
        int   i = jdbcTemplate.update("insert into user(u_name,u_pass,type_id) values(‘study01‘,‘study01‘,1)");
        int j = jdbcTemplate.update("insert into user(u_name,u_pass,type_id) values(‘study02‘,‘study02‘,1)");
        int k = jdbcTemplate.update("insert into user(u_name,u_pass,type_id) values(?,?,?)",new Object[]{"study03","study03",1});
        //判断是否插入三条记录是否成功
        org.junit.Assert.assertEquals(3, i+j+k);
    }
}

JdbcTemplate主要提供的方法说明:

execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;
batchUpdate方法用于执行批处理相关语句;

query方法及queryForXXX方法:用于执行查询相关语句
call方法:用于执行存储过程、函数相关语句。



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

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

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

    PreparedStatementStetter:通过回调获取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)来处理结果集,用户必须处理整个结果集

接下来我们看下具体示例吧,在示例中不可能介绍到JdbcTemplate全部方法及回调类的使用方法,我们只介绍代表性的,其余的使用都是类似的。

1.预编译语句及预编译过程创建回调、自定义功能回调使用:

@Test
    public void testPrepareStatement1(){
        //以返回所有用户信息为例
        List<User> users = jdbcTemplate.execute(new PreparedStatementCreator() {//PreparedStatementCreaator通过回调获取JdbcTemplate提供的Connection,由用户使用该Connection创建相关的PreparedStatement
            //createPreparedStatement(Connection con)用于创建java.sql.PreparedStatement预处理对象
            @Override
            public PreparedStatement createPreparedStatement(Connection con)
                    throws SQLException {
                    //Connection是java.sql.Connection,因此我们就可以向使用原生JDBC连接对象
                    //返回user表中的记录数
                return con.prepareStatement("select * from user");
            }
        }, new PreparedStatementCallback<List<User>>() {//PreparedStatementCallback<T> 通过回调获取JdbcTemplate提供的PreparedStatement,用户可以在该PreparedStatement执行任何数量的操作
                                                        //注意<T>是回调返回的结果类型

            @Override
            public List<User> doInPreparedStatement(PreparedStatement pst)
                    throws SQLException, DataAccessException {//用户自行处理java.sql.PreparedStatement对象
                    //返回结果
                    List<User> users = new LinkedList<User>();
                    //执行预处理语句
                    pst.execute();
                    //获得预处理结果集
                    ResultSet rs = pst.getResultSet();
                    //处理结果集
                    while (rs.next()) {
                        User user = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4));
                        users.add(user);
                    }
                    return users;
            }
        });
        //打印所用用户信息
        for (User user : count) {
            System.out.println(user);
        }
    }

首先使用PreparedStatementCreator创建一个预编译语句,其次由JdbcTemplate通过PreparedStatementCallback回调传回,由用户决定该PreparedStatement。此处我们使用的是execute方法。

此种方式最好用来执行DLL语句,不然写法与JDBC编程并无太多差别。

2.预编译设值回调使用

@Test
    public void testPrepareStatement2(){
        String insertSql = "insert into user(u_name,u_pass,type_id) values(?,?,?)";
        //方式一:通过PreparedStatementSetter对预编译sql语句中的占位符进行设值。
        int count = jdbcTemplate.update(insertSql, new PreparedStatementSetter() {//预编译设值对象,用于设置预编译语句中的值        
            @Override
            public void setValues(PreparedStatement pst) throws SQLException {//设置预编译语句对象的值
                pst.setObject(1, "zhangsan");
                pst.setString(2,"zhangsan");
                pst.setInt(3, 1);
            }
        });
        Assert.assertEquals(1, count);
        //方式二:使用update(sql,object..),其中object..是对应语句中的占位符
        int count2 = jdbcTemplate.update(insertSql, new Object[] {"lisi","lisi",1});
        Assert.assertEquals(1, count2);
    }
    通过JdbcTemplate的int update(String sql,PreparedStatementSetter pss)执行预编译sql,其中sql参数为"insert into user(u_name,u_pass,type_id) values(?,?,?)",该sql有三个占位符需要在执行前设值,PreparedStatementSetter实现就是为了设值,使用setValues(PreparedStatement psstmet)回调方法设值相应的占位符位置的值。
    JdbcTemplate也提供了一种更简单的方式update(String sql,Object...args)来实现设值,所以只要当使用该种方式不满足需求时才应使用PreparedStatementSetter

3结果集处理回调

方式一:

@Test
    public void testResultSeti(){
        String listSql = "select * from user";  
        List<Map> result = jdbcTemplate.query(listSql,new RowMapper<Map>() {
            //RowMapper接口提供mapRow(ResultSet rs,int rowNum)方法将结果集的每一行转换为一个Map,当然可以转换为其他类,如表的对象化形式
            @Override
            public Map<Integer, User> mapRow(ResultSet rs, int rowNum) throws SQLException {
                //ResultSet结果集对象,rowNum代表每一行
                Map<Integer, User> row = new HashMap<Integer, User>();
                row.put(rs.getInt(1), new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4)));
                return row;
            }
        }); 
        //打印用户信息
        for (Map<Integer, User> map : result) {
            System.out.println(map);
        }
    }

RowMapper接口提供mapRow(ResultSet rs,int rowNum)方法将结果集的每一行转换为一个Map,当然可以转换为其他类,如表的对象化形式

方式二:

@Test
    public void testResultSet2(){
        String listSql = "select * from user";  
        final List result = new ArrayList<Map<Integer,User>>(); //需要的结果数据类型,必须是final 关键字修饰,否则在内部类中不能引用该常量。
        jdbcTemplate.query(listSql, new RowCallbackHandler() {//RowCallbackHandler接口也提供方法processRow(ResultSet rs),能将结果集的行转换为需要的形式

            @Override
            public void processRow(ResultSet rs) throws SQLException {//处理每一行的数据,并且将每一行数据转换为需要的数据类型。ResultSet结果集对象,
                Map<Integer, User> row = new HashMap<Integer, User>();//每一行记录需要的数据类型
                row.put(rs.getInt("u_id"), new User(rs.getInt("u_id"), rs.getString("u_name"), rs.getString("u_pass"), rs.getInt("type_id")));
                result.add(row);//直接添加到结果中

            }
        });
        for (Object object : result) {
            System.out.println(object);
        }
    }

RowCallbackHandler接口也提供方法processRow(ResultSet rs),能将结果集的行转换为需要的形式
方式三:

@Test
    public void testResultSet3(){
        String listSql = "select * from user";
        List result = jdbcTemplate.query(listSql, new ResultSetExtractor<List>(){
            @Override
            public List<User> extractData(ResultSet rs) throws SQLException,
                    DataAccessException {//用户自定义处理结果集
                List<User> result = new ArrayList<User>();
                while (rs.next()) {
                    result.add(new User(rs.getInt(1),rs.getString(2),rs.getString(3),rs.getInt(4)));
                }
                return result;
            }
        });

        for (Object object : result) {
            System.out.println(object);
        }
    }

ResultSet使用回调方法extractData(ResultSet rs)提供给用户整个结果集,让用户决定如何处理该结果集。

当然JdbcTemplate提供更简单的queryForXXX方法,来简化开发。

@Test
    public void testQuery(){
        //查询一行数据并返回int型结果
        int  userNubers = jdbcTemplate.queryForInt("select count(*) from user");
        System.out.println("用户总数:"+userNubers);
        //查询一行数据并将该行数据转换为Map返回
         Map map = jdbcTemplate.queryForMap("select * from user where u_id = 5");
        //查询一行任何类型的数据,最后一个参数指定返回结果类型
         Integer userNubers2 = jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
        //查询一批数据,默认将每行数据转换为Map
        List<Map<String, Object>> users =  jdbcTemplate.queryForList("select * from user");
    }

存储过程及函数回调

2.2NamedParameterJdbcTemplate

NamedParameterJdbcTemplate类是基于JdbcTemplate类,并对它进行了封装从而支持命名参数特性。

NamedParameterJdbcTemplate主要提供以下三类方法:execute方法、query及queryForXXX方法、update及batchUpadate方法。

首先让我们看个例子吧

如何获取NamedParameterJdbcTemplate
//1.获取NamedParameterJdbcTemplate对象,通过阅读文档可知该对象有两个构造方法。
        //方法一:使用已有的JdbcTemplate对象的构建NamedParameterJdbcTemplate对象
        String url="jdbc:mysql://localhost:3306/study_db";
        String username = "root";
        String password = "root";
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        jdbcTemplate =  new JdbcTemplate(dataSource);
        NamedParameterJdbcTemplate namedParameterJdbcTemplate
            = new NamedParameterJdbcTemplate(jdbcTemplate);
        //方法二:使用dataSource构建NamedParameterJdbcTemplate对象
        String url="jdbc:mysql://localhost:3306/study_db";
        String username = "root";
        String password = "root";
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        NamedParameterJdbcTemplate namedParameterJdbcTemplate2 = new NamedParameterJdbcTemplate(dataSource);

使用NamedParameterJdbcTemplate

@Test
    public void testNamedParameterJdbcTemplate(){
        //1.获取NamedParameterJdbcTemplate对象,通过阅读文档可知该对象有两个构造方法。
        //方法一:使用已有的JdbcTemplate对象的构建NamedParameterJdbcTemplate对象
        NamedParameterJdbcTemplate namedParameterJdbcTemplate
            = new NamedParameterJdbcTemplate(jdbcTemplate);
        //方法二:使用dataSource构建NamedParameterJdbcTemplate对象
        //NamedParameterJdbcTemplate namedParameterJdbcTemplate2 = new NamedParameterJdbcTemplate(dataSource);
        String insertSql = "insert into user(u_name) values(:name)";
        String selectSql = "select * from user where u_name=:name";
        String deleteSql = "delete from user where u_name=:name";
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("name", "Admin122");
        //执行插入语句
        namedParameterJdbcTemplate.update(insertSql, paramMap);
        final List<User> result = new ArrayList<User>();////需要的结果数据类型,必须是final 关键字修饰,否则在内部类中不能引用该常量。
        namedParameterJdbcTemplate.query(selectSql, paramMap, new RowCallbackHandler() {//RowCallbackHandler接口也提供方法processRow(ResultSet rs),能将结果集的行转换为需要的形式

            @Override
            public void processRow(ResultSet rs) throws SQLException {//处理每一行的数据,并且将每一行数据转换为需要的数据类型。ResultSet结果集对象,
                //处理每行数据
                result.add(new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4)));
            }
        });
        //执行删除语句
        int  count = namedParameterJdbcTemplate.update(deleteSql, paramMap);
        //删除成功
        Assert.assertEquals(1, count);

        //删除zhangsan的记录,与Bean相关联的操作方法
        String deleteSql1 = "delete from user where u_name=:username";
        User user = new User();
        user.setUsername("zhangsan");
        SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user);
        int count1 = namedParameterJdbcTemplate.update(deleteSql1, parameterSource);
        Assert.assertEquals(true, count1>0);
    }

NamedParameterJdbcTemplate初始化:可以使用DataSource或JdbcTemplate对象作为构造器参数初始化

insert into test(name) values(:name):其中“:name”就是命名参数
update(insertSql,paramMap):其中paramMap是一个Map类型,包含键位“name”对应命名参数,值为"value"的键值对,为命名参数设值的数据

query(selectSql,paramMap,new RowCallbackHandler()...):类似于JdbcTemplate中介绍的,唯一不同的是需要传入paramMap来为命名参数设值


update(deleteSql,paramSource):类似于update(insertSql,paramMap),但使用SqlParameterSource参数来为命名参数设值,此处使用MapSqlParameterSource实现,其实就是简单封装java.util.Map

NamedParameterJdbcTemplate类为命名参数设值有两种方式:java.util.Map和SqlParameterSource:

1.java.util.Map:使用Map键数据来对于命名参数,而Map值数据用于设值

2.SqlParameterSource:可以使用SqlParameterSource实现作为来实现为命名参数设值。默认有MapSqlParameterSource和BeanPropertySqlParameterSource实现;

MapSqlParameterSource实现非常简单,只是封装了java.util.Map;
而BeanPropertySqlParameterSource封装了一个JavaBean对象,通过JavaBean对象属性来决定命名参数的值
//删除zhangsan的记录,与Bean相关联的操作方法
        String deleteSql1 = "delete from user where u_name=:username";
        User user = new User();
        user.setUsername("zhangsan");
        SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user);
        int count1 = namedParameterJdbcTemplate.update(deleteSql1, parameterSource);
        Assert.assertEquals(true, count1>0);

可以看出BeanPropertySqlParameterSource使用能减少很多工作量,但命名参数必须和JavaBean属性名称相对应才可以。

2.3 SimpleJdbcTemplate

SimpleJdbcTemplate类也是基于JdbcTemplate类,但利用java5+的可变参数列表和自动装箱和拆箱从而获取更简洁的代码

SimpleJdbcTemplate 主要提供两类方法:query及queryForXXX方法、update及batchUpdate方法

首先创建数据库user表与User类JavaBean对应的模型类

package com.lizhenhua.test.pojo;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class UserRowMapper implements RowMapper<User> {

    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        //数据库底层转换为JavaBean模型类
        User user = new User();
        user.setUid(rs.getInt("u_id"));
        user.setUsername(rs.getString("u_name"));
        user.setPassword(rs.getString("u_pass"));
        user.setTypeId(rs.getInt("type_id"));
        return user;
    }

}

获取SimpleJdbcTemplate对象

/获取SimpleJdbcTemplate对象
        //一共有三种方式,因为有三个带参构造函数,还支持数据源构造、NamedParameterJdbcTemplate对象构造
        SimpleJdbcTemplate simpleJdbcTemplate = 
            new SimpleJdbcTemplate(jdbcTemplate);

        //使用数据源获取
        String url="jdbc:mysql://localhost:3306/study_db";
        String username = "root";
        String password = "root";
        //DriverManagerDataSource对象管理数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        SimpleJdbcTemplate simpleJdbcTemplate2 = 
            new SimpleJdbcTemplate(dataSource);


        //使用NamedParameterJdbcTemplate对象构造
        String url="jdbc:mysql://localhost:3306/study_db";
        String username = "root";
        String password = "root";
        //DriverManagerDataSource对象管理数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
        //使用NamedParameterJdbcTemplate对象构造
        SimpleJdbcTemplate simpleJdbcTemplate3 = 
            new SimpleJdbcTemplate(namedParameterJdbcTemplate);

使用SimpleJdbcTemplate对象

@Test
    public void testSimpleJdbcTemplate(){
        //获取SimpleJdbcTemplate对象
        //一共有三种方式,因为有三个带参构造函数,还支持数据源构造、NamedParameterJdbcTemplate对象构造
//      //使用数据源获取
//      String url="jdbc:mysql://localhost:3306/study_db";
//      String username = "root";
//      String password = "root";
//      //DriverManagerDataSource对象管理数据源
//      DriverManagerDataSource dataSource = new DriverManagerDataSource(url, username, password);
//      NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
//      SimpleJdbcTemplate simpleJdbcTemplate2 = 
//          new SimpleJdbcTemplate(dataSource);
//      //使用NamedParameterJdbcTemplate对象构造
//      SimpleJdbcTemplate simpleJdbcTemplate3 = 
//          new SimpleJdbcTemplate(namedParameterJdbcTemplate);
        //使用已有JdbcTemplate对象构造
        SimpleJdbcTemplate simpleJdbcTemplate = 
            new SimpleJdbcTemplate(jdbcTemplate);
        String insertSql = "insert into user(u_name,u_pass,type_id) values(?,?,?)";
        //插入数据
        int  flag = simpleJdbcTemplate.update(insertSql, "zhangjian","mingliu",1);
        //用来判断插入数据是否成功
        Assert.assertEquals(1,flag );
        String selectSql = "select * from user where u_name = ? and u_pass = ? and type_id = ?";
        List<Map<String,Object>> result = simpleJdbcTemplate.queryForList(selectSql,"zhangjian","mingliu",1 );
        //用来判断查询数据是否成功
        Assert.assertEquals(1,result.size() );

        //基于RowMapper<T> 模型的查询。很显然这种方式是我们所推荐的,但是他必须为每一个javabean编写映射关系
        RowMapper<User> rowMapper = new UserRowMapper();
        String sql = "select * from user where type_id = ?";
        //查询返回用户类型为1的所有用户信息
        List<User> users = simpleJdbcTemplate.query(sql, rowMapper,1);
        //打印查询出来结果信息
        for (User user : users) {
            System.out.println(user);
        }

    }
SimpleJdbcTemplate初始化:可以使用DataSource、JdbcTemplate或NamedParameterJdbcTemplate对象作为构造器参数初始化

update(insertSql, "zhangjian","mingliu",1):采用java5+可变参数列表从而替代new Object[] {"zhangjian","mingliu",1}方式

query(sql, rowMapper,1):使用java5+可变参数列表及RowMapper回调并利用个泛型特性来指定返回值类型List<User>.

SimpleJdbcTemplate还提供如下方法用于获取JdbcTemplate和NamedParameterJdbcTemplate:

1。获取JdbcTemplate对象方法:JdbcQperations是JdbcTemplate的接口

JdbcOperations.getJdbcOperations();

2.获取NamedParameterJdbcTemplate对象方法:NamedParameterJdbcOperations是NamedParameterJdbcTemplate的接口
NamedParameterJdbcOperations.getNamedParameterJdbcOperations();

三、关系数据库对象化操作

关系数据库对象化其实就是用面向对象的方式表示关系数据库操作,从而可以复用

SpringJDBC框架将数据库操作封装为一个RdbmsOperation,该对象是线程安全的、可复用的对象,是所有数据库对象的父类。而SqlOperation继承了RdbmsOperation,代表了数据库SQL操作,如select、update、call等


查询:将数据库操作select封装为对象,查询操作的基类是SqlQuery,所有查询都可以使用该类表示,SpringJDBC还提供了一些更容易使用的MappingSqlQueryWithParameters和MappingSqlQuery用于将结果集映射为Java对象,查询对象类还提供了两个扩展UpdatableSqlQuery和SqlFunction

更新:即增删改操作,将数据库操作insert、update、delete封装为对象,增删改基类是SqlUpdate,当然还提供了BatchSqlUpdate用于批处理。

存储过程及函数: 将存储过程及函数调用封装为对象,基类是SqlCall类,提供了StoredProcedure实现

一、查询

1.SqlQuery:需要覆盖如下方法来定义一个RowMapper,其中parameters参数表示命名参数或占位符参数值列表,而context是由用户传入的上下文数据

RowMapper<T> newRowMapper(Obect[] parameters , Map context)

SqlQuery提供两类方法

    execute及executeByNamedParam方法:用于查询多行数据,其中executeByNamedParam用于支持命名参数绑定参数

    findObject及findObjectByNamedParam方法:用于查询单行数据,其中findObjectByNamedParam用于支持命名参数绑定

演示一下SqlQuery如何使用:
首先,构建一个SqlQuery,编写UserModelSqlQuery查询模板类 必须继承SqlQuery<T> 
package com.lizhenhua.test.pojo;

import java.sql.Types;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlQuery;

public class UserModelSqlQuery extends SqlQuery<User> {
    public UserModelSqlQuery(JdbcTemplate jdbcTemplate){
        //首先设置数据源或JdbcTemplate
        //设置数据源
        //super.setDataSource(jdbcTemplate.getDataSource());
        //设置JdbcTemplate
        super.setJdbcTemplate(jdbcTemplate);
        //其次、定义sql语句,所定义的sql语句都将被编译为PreparedStatement
        super.setSql("select * from user where type_id = ?");
        //接着、对PreparedStatement参数描述,使用SqlParameter来描述参数类型,支持命名参数、占位符描述;
        //对于命名参数可以使用如new SqlParameter("name",Types.VARCHAR)描述,注意占位符参数描述必须按占位符参数列表的顺序进行描述
        super.declareParameter(new SqlParameter(Types.INTEGER));
        //最后编译:可选,当执行相应查询方法时会自动编译,用于将sql编译为PreparedStatement,对于编译的SqlQuery不能再对参数进行描述了。
        compile();
        //以上步骤是不可变的,必须按顺序执行
    }
    @Override
    protected RowMapper<User> newRowMapper(Object[] parameters, Map context) {//处理行映射
        //使用指定Mapper映射返回行记录
        return new UserRowMapper();
    }

}
    处理行映射规则时必须编写映射规则类,该类必须实现RowMapper<T>接口
package com.lizhenhua.test.pojo;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class UserRowMapper implements RowMapper<User> {

    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        //数据库底层转换为JavaBean模型类
        User user = new User();
        user.setUid(rs.getInt("u_id"));
        user.setUsername(rs.getString("u_name"));
        user.setPassword(rs.getString("u_pass"));
        user.setTypeId(rs.getInt("type_id"));
        return user;
    }

}

测试SqlQuery

@Test
    public void testSqlQuery(){
        SqlQuery<User> query = new UserModelSqlQuery(jdbcTemplate);
        //使用自定义查询模板进行查询数据,此处因返回用户类型为1的所有用户
        List<User> result = query.execute(1);
        for (User user : result) {
            System.out.println(user);
        }
    }

2.MappingSqlQuery:用于简化SqlQuery中RowMapper创建,可以直接在实现mapRow(ResultSet rs,int rowNum)来将行数据映射为需要的形式。

package com.lizhenhua.test.pojo;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.MappingSqlQuery;

public class UserModelMappingSqlQuery extends MappingSqlQuery<User> {

    public UserModelMappingSqlQuery(JdbcTemplate jdbcTemplate){
        //第一步:获取数据源
        super.setDataSource(jdbcTemplate.getDataSource());
        //定义语句所定义的sql语句都将被编译为PreparedStatement
        String sql = "select * from user where u_name = :username";
        super.setSql(sql);
        //为语句参数定义描述信息declareParameter描述对PreparedStatement参数描述,使用SqlParameter来描述参数类型,支持命名参数、占位符描述;
        super.declareParameter(new SqlParameter("username", Types.VARCHAR));
        ///最后编译:可选,当执行相应查询方法时会自动编译,用于将sql编译为PreparedStatement,对于编译的SqlQuery不能再对参数进行描述了。
        compile();
    }
    @Override
    protected User mapRow(ResultSet rs, int rowNum) throws SQLException {
        //为数据库中的表中的行进行JavaBean转换处理,即定义所需要的形式
        User user = new User();
        user.setUid(rs.getInt("u_id"));
        user.setUsername(rs.getString("u_name"));
        user.setPassword(rs.getString("u_pass"));
        user.setTypeId(rs.getInt("type_id"));
        return user;
    }

}

测试

@Test
    public void testMappingSqlQuery(){
        SqlQuery<User> query = new UserModelMappingSqlQuery(jdbcTemplate);
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("username", "zhangjian");
        //query.executeByNamedParam(paramMap)用于执行命名参数命名的方法
        List<User> users= query.executeByNamedParam(paramMap);
        //打印查询到的信息
        for (User user : users) {
            System.out.println(user);
        }

    }

[User [password=mingliu, typeId=1, uid=26, username=zhangjian], User [password=mingliu, typeId=1, uid=27, username=zhangjian], User [password=mingliu, typeId=1, uid=28, username=zhangjian], User [password=mingliu, typeId=1, uid=29, username=zhangjian]]
[User [password=mingliu, typeId=1, uid=26, username=zhangjian], User [password=mingliu, typeId=1, uid=27, username=zhangjian], User [password=mingliu, typeId=1, uid=28, username=zhangjian], User [password=mingliu, typeId=1, uid=29, username=zhangjian]]
[User [password=mingliu, typeId=1, uid=26, username=zhangjian], User [password=mingliu, typeId=1, uid=27, username=zhangjian], User [password=mingliu, typeId=1, uid=28, username=zhangjian], User [password=mingliu, typeId=1, uid=29, username=zhangjian]]
[User [password=mingliu, typeId=1, uid=26, username=zhangjian], User [password=mingliu, typeId=1, uid=27, username=zhangjian], User [password=mingliu, typeId=1, uid=28, username=zhangjian], User [password=mingliu, typeId=1, uid=29, username=zhangjian]]

4.UpdatableSqlQuery:提供可更新结果集查询支持,子类实现updateRow(ResultSet rs,int rowNum,Map context)对结果集进行更新

package com.lizhenhua.test.pojo;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.object.UpdatableSqlQuery;

public class UserModerUpdatabelSqlQuery extends UpdatableSqlQuery<User> {

    public UserModerUpdatabelSqlQuery(JdbcTemplate jdbcTemplate){
        //第一步:获取数据源
        super.setDataSource(jdbcTemplate.getDataSource());
        //第二步:定义语句
        super.setSql("select * from user");
        //第三步:处理语句参数描述
        //第四步:编译,可选,编译后语句不能被改变
        compile();

    }

    @Override
    protected User updateRow(ResultSet rs, int rowNum, Map map)
            throws SQLException {
            User user = new User();
            user.setUid(rs.getInt(1));
            user.setUsername(rs.getString(2));
            user.setPassword(rs.getString(3));
            user.setTypeId(rs.getInt(4));   
        System.out.println("rowNum:" + rowNum );
        System.out.println("Map" + map);        
        return user;
    }

}

    @Test
    public void testUpdatabelSqlQuery(){
        UserModerUpdatabelSqlQuery query = new UserModerUpdatabelSqlQuery(jdbcTemplate);
        List<User> users = query.execute();
        System.out.println(users);
    }

5.GenericSqlQuery:提供setRowMapperClass(Class rowMapperClass)方法用于指定RowMapper实现,在此就不演示了。

6.SqlFunction:SQL函数包装器,用于支持那些返回单行结果集的查询。该类主要用于返回单行单列结果集

@Test
    public void testSqlFunction(){
        String countSql = "select count(*) from user";
        //定义函数
        SqlFunction<Integer> sqlFunction = new SqlFunction<Integer>(jdbcTemplate.getDataSource(), countSql);
        //运行函数
        int result = sqlFunction.run();
        System.out.println(result);

        String sql = "select u_pass from user where u_name=?";
        //定义函数  设置数据源和定义sql语句
        SqlFunction<String> sqlFunction2 = new SqlFunction<String>(jdbcTemplate.getDataSource(), sql);
        //对语句中的参数进行描述
        sqlFunction2.declareParameter(new SqlParameter(Types.VARCHAR));
        //设置语句中的值并执行函数返回结果,返回的是一列数据。可以根据返回值类型不同选用不同的执行语句
        List<String> passwords =  sqlFunction2.execute("zhangjian");
        for (String password : passwords) {
            System.out.println(password);
        }
    }

二、更新

SqlUpdate类用于支持数据库更新操作,即增删改(insert\delete\update)操作,该方法类似于SqlQuery,只是职责不一样

SqlUpdate提供了update及updateByNamedParam方法用于数据库更新操作,其中updateByNamedParam用于命名参数类型更新

演示一下SqlUpdate如何使用
@Test
    public void testSqlUpdate(){
        //使用SqlUpdate子类自定义插入模板插入数据,
        SqlUpdate insert = new InsertUserModel(jdbcTemplate);
        int  count = insert.update("zhaozilong","qingyi",1);
        //判断是否插入数据成功
        Assert.assertEquals(count>0, true);

        //直接使用SqlUpdate对象修改数据
        String sql = "update user set u_pass = ? where u_name = ?";
        //直接使用构造函数创建SqlUpdate对象,并且设置执行的sql语句,
        SqlUpdate update = new SqlUpdate(jdbcTemplate.getDataSource(), sql, new int[] {Types.VARCHAR,Types.VARCHAR});
        //修改数据
        int count2 = update.update("zhaozilong","zhaozilong");
        //判断修改数据是否成功
        Assert.assertEquals(true, count2>0);

        //使用命名参数方式测试SqlUpdate的用法
        String deleteSql = "delete from user where u_name = :username";
        SqlUpdate delete = new SqlUpdate(jdbcTemplate.getDataSource(), deleteSql, new int[] {Types.VARCHAR});
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        //设置的key为sql预处理语句的命名参数, value值就是给对应命名参数设值。
        paramMap.put("username", "zhaozilong");
        int  count3 = delete.updateByNamedParam(paramMap);
        //判断删除数据是否成功
        Assert.assertEquals(true, count3>0);
    }

InsertUserModel类实现类似于SqlQuery实现,用于执行数据库插入操作,SqlUpdate还提供一种更简洁的构造器SqlUpdate(DataSource ds,String sql,int[] types),其中types用于指定占位符或命名参数类型;SqlUpdate还支持命名参数,使用updateByNamedParam方法来进行命名参数操作。

回顾总结:

//第一步:创建设备管理数据源对象
    DriverManagerDataSource datasource = DriverManagaerDataSource(url,username,password);

    //第二步:选用我们所需的JDBC模板对象:JdbcTemplate、NamedParementJdbcTemplate、SimpleTemplate

    //然后,定义sql语句

    //为sql语句中的占位符、命名参数、进行赋值。

    //为命名参数赋值使用HashMap对象进行赋值,或者使用NamedParementJdbcTemplate的符合JavaBean规范参数进行赋值

    //处理结果集:通过回调函数的形式处理结果集使用:
            方式一:
            query(sql,new RowCallbackHandler(){
                @Override
                public void processRow(ResultSet rs) throws SQLException {//按行处理
                    //2.处理结果集ResultSet只代表每一行数据
                    User user = new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4));
                    System.out.println(user);
                }
            })、
            方式二:
            query(listSql,new RowMapper<Map>() {
            //RowMapper接口提供mapRow(ResultSet rs,int rowNum)方法将结果集的每一行转换为一个Map,当然可以转换为其他类,如表的对象画形式
            @Override
            public Map<Integer, User> mapRow(ResultSet rs, int rowNum) throws SQLException {
                //ResultSet结果集对象只代表每一行数据,rowNum代表每一行
                Map<Integer, User> row = new HashMap<Integer, User>();
                row.put(rs.getInt(1), new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4)));
                return row;
                }
            });
            方式三:
            query(listSql, new ResultSetExtractor<List>(){
            @Override
            public List<User> extractData(ResultSet rs) throws SQLException,
                    DataAccessException {//用户自定义处理结果集.ResultSet返回的是整个结果集对象
                List<User> result = new ArrayList<User>();
                while (rs.next()) {
                    result.add(new User(rs.getInt(1),rs.getString(2),rs.getString(3),rs.getInt(4)));
                }
                return result;
            }
        });
            方式四:query(sql, RowMapper<T> rowMapper);
            步骤:新建一个类TRowMapper实现该接口RowMapper<T>
public class UserRowMapper implements RowMapper<User> {

    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {//将每一行的数据转换为需要的对象
        //数据库底层转换为JavaBean模型类
        User user = new User();
        user.setUid(rs.getInt("u_id"));
        user.setUsername(rs.getString("u_name"));
        user.setPassword(rs.getString("u_pass"));
        user.setTypeId(rs.getInt("type_id"));
        return user;
    }

}       
        然后使用:query(sql, rowMapper)方法进行掉用。

        RowMapper<User> rowMapper = new UserRowMapper();
        String sql = "select * from user where type_id = ?";
        //查询返回用户类型为1的所有用户信息
        List<User> users = simpleJdbcTemplate.query(sql, rowMapper,1);


            方式五:使用自定义模板处理结果集对象,原理与上面都是类似的。UserModelSqlQuery extends SqlQuery<User>
            步骤一:新建一类TModelSqlQuery类继承SqlQuery<T>,因为SqlQuery<T> 类中有一个处理结果集中的行的保护方法protected RowMapper<User> newRowMapper(Object[] parameters, Map context)需要实现,返回的结果集是RowMapper<T>。如何实现这个结果集有两种方式:第一种:与方式四是一样的。第二种:在该方法体类中实现。

public class UserModelSqlQuery extends SqlQuery<User> {
    public UserModelSqlQuery(JdbcTemplate jdbcTemplate){
        //首先设置数据源或JdbcTemplate
        //设置数据源
        //super.setDataSource(jdbcTemplate.getDataSource());
        //设置JdbcTemplate
        super.setJdbcTemplate(jdbcTemplate);
        //其次、定义sql语句,所定义的sql语句都将被编译为PreparedStatement
        super.setSql("select * from user where type_id = ?");
        //接着、对PreparedStatement参数描述,使用SqlParameter来描述参数类型,支持命名参数、占位符描述;
        //对于命名参数可以使用如new SqlParameter("name",Types.VARCHAR)描述,注意占位符参数描述必须按占位符参数列表的顺序进行描述
        super.declareParameter(new SqlParameter(Types.INTEGER));
        //最后编译:可选,当执行相应查询方法时会自动编译,用于将sql编译为PreparedStatement,对于编译的SqlQuery不能再对参数进行描述了。
        compile();
        //以上步骤是不可变的,必须按顺序执行
    }
    @Override
    protected RowMapper<User> newRowMapper(Object[] parameters, Map context) {
        //使用指定Mapper映射返回行记录
        return new UserRowMapper();
    }

}

        步骤二:调用方法
        SqlQuery<User> query = new UserModelSqlQuery(jdbcTemplate);
        //使用自定义查询模板进行查询数据,此处因返回用户类型为1的所有用户
        List<User> result = query.execute(1);


        注意:此方法是一个功能强大的方法,我在这里只是做一个简单的演示。不过实现该方法的基本步骤和我的演示是一样的。望读者在开发中能够灵活应用。


        方式六:与方式五是同理只不过编写一个新类继承的类是MappingSqlQuery<T>:public class UserModelMappingSqlQuery extends MappingSqlQuery<User>

        方式七:与方式五、方式六是同理只不过编写一个新类继承的类是UpdatableSqlQuery<T>:public class UserModerUpdatabelSqlQuery extends UpdatableSqlQuery<User>




        NamedParameterJdbcTemplate同样也和JdbcTemplate一样有四种处理结果集的方式:不一一列举

        String selectSql = "select * from user where u_name=:name";     
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("name", "Admin122");
        final List<User> result = new ArrayList<User>();////需要的结果数据类型,必须是final 关键字修饰,否则在内部类中不能引用该常量。
        namedParameterJdbcTemplate.query(selectSql, paramMap, new RowCallbackHandler() {//RowCallbackHandler接口也提供方法processRow(ResultSet rs),能将结果集的行转换为需要的形式

            @Override
            public void processRow(ResultSet rs) throws SQLException {//处理每一行的数据,并且将每一行数据转换为需要的数据类型。ResultSet结果集对象,
                //处理每行数据
                result.add(new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4)));
            }
        });


  可以处理Row数据,也可以采取返回全部数据ResultSet处理结果集


    //执行所选JDBC模板对象对应的query、update进行操作

3.4存储过程及函数

StoredProcedure用于支持存储过程及函数,该类的使用同样类似于SqlQuery
StoredProcedure提供execute方法用于执行存储过程及函数
一、StoredProcedure如何调用自定义函数:

@Test
    public void testStoredProcedure(){
        //创建MYSQL函数FUNCTION_TEST  输入参数str字符串 输出参数名return字符串长度
        String createFunctionSql = 
            "CREATE FUNCTION FUNCTION_TEST(str VARCHAR(100))"
            +"returns INT return LENGTH(str)";
        //删除MYSQL函数FUNCTION_TEST
        String dropFunctionSql = "DROP FUNCTION IF EXISTS FUNCTION_TEST";
        //执行定义删除MYSQL函数FUNCTION_TEST
        jdbcTemplate.update(dropFunctionSql);
        //执行定义MYSQL函数FUNCTION_TEST  输入参数str字符串 输出参数名return字符串长度
        jdbcTemplate.update(createFunctionSql);

    }

定义自定义函数类LengthFunction,该类继承StoredProcedure

package com.lizhenhua.test.pojo;

import java.sql.Types;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
/**
 * 此类定义一个MySQL的自定义函数FUNCTION_TEST,求通过输入一个字符串str,获得str的长度。
 * @author Administrator
 *
 */
public class LengthFunction extends StoredProcedure {
    public LengthFunction(JdbcTemplate jdbcTemplate){
        //设置数据源或JdbcTemplate
        super.setJdbcTemplate(jdbcTemplate);
        //定义sql语句
        super.setSql("FUNCTION_TEST");
        //声明语句是函数
        super.setFunction(true);
        //定义函数的输入和输出变量描述信息
        //定义输出变量的描述类型
        super.declareParameter(new SqlOutParameter("result", Types.INTEGER));
        //定义输入参数的描述类型
        super.declareParameter(new SqlParameter("str",Types.VARCHAR));
        //编译(可选)一经编译不可改变
        compile();
    }
}

测试自定义函数执行

@Test
    public void testStoredProcedure(){
        //创建MYSQL函数FUNCTION_TEST  输入参数str字符串 输出参数名return字符串长度
        String createFunctionSql = 
            "CREATE FUNCTION FUNCTION_TEST(str VARCHAR(100))"
            +"returns INT return LENGTH(str)";
        //删除MYSQL函数FUNCTION_TEST
        String dropFunctionSql = "DROP FUNCTION IF EXISTS FUNCTION_TEST";
        //执行定义删除MYSQL函数FUNCTION_TEST
        jdbcTemplate.update(dropFunctionSql);
        //执行定义MYSQL函数FUNCTION_TEST  输入参数str字符串 输出参数名return字符串长度
        jdbcTemplate.update(createFunctionSql);
        //创建自定义函数
        StoredProcedure lengthFunction = new LengthFunction(jdbcTemplate);  
        //执行自定义函数,jjjjj输入的参数,将结果集保存在Map<String,Object>中,其key中保存的是返回值的名称,而value是对应的值
        Map<String, Object> outValues = lengthFunction.execute("jjjjj");
        Assert.assertEquals(5, outValues.get("result")); 

    }

调用自定义存储过程与自定义函数步骤一样,只是在描述参数时,使用SqlInOutParameter描述INOUT类型参数,使用SqlOutParameter描述OUT类型参数,必须按顺序定义,不能颠倒

四、Spring提供的其他帮助

1.SimpleJdbc方式

Spring JDBC抽象框架提供SimpleJdbcInsert和SimpleJdbcCall类,这两个类通过利用JDBC驱动提供的数据库元数据来简化JDBC操作。

    SimpleJdbcInsert:用于插入数据,根据数据库元数据进行插入数据,本类用于简化插入操作,提供三种类型方法:execute方法用于普通插入、executeAndReturnKey及executeAndReturnKeyHolder方法用于插入时获取主键值、executeBatch方法用于批量处理
@Test
    public void testSimpleJdbcInsert(){
        SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate);
        //将插入数据关联数据库表user
        insert.withTableName("user");
        //定义数据key为表的列名,value为对应的值
        Map<String, Object> args = new HashMap<String, Object>();
        args.put("u_name", "username1");
        args.put("u_pass", "opass");
        args.put("type_id", 1);
        //编译语句
        insert.compile();
        //1.普通插入
        int count = insert.execute(args);
        //判断普通插入是否成功
        Assert.assertEquals(1, count);
        //2.插入时获取主键值
        insert = new SimpleJdbcInsert(jdbcTemplate);
        //关联表user
        insert.withTableName("user");
        //设置关联表中的主键名称
        insert.setGeneratedKeyName("u_id");
        //执行语句并返回插入数据成功的主键值
        Number id = insert.executeAndReturnKey(args);
        System.out.println(id);
        //3.批处理
        insert = new SimpleJdbcInsert(jdbcTemplate);
        //关联表user
        insert.withTableName("user");
        //设置关联表中的主键名称
        insert.setGeneratedKeyName("u_id");
        //插入一批数据,并返回对应的数据主键值
        int[] updateCount = insert.executeBatch(new Map[] {args,args,args});
        //判断是否成功插入3条记录
        Assert.assertEquals(3, updateCount.length);
    }
    new SimpleJdbcInsert(jdbcTemplate):首次通过DataSource对象或JdbcTemplate对象初始化SimplateJdbcInsert
insert.withTableName("user"):用于设置数据库表名
args:用于指定插入时列名及值。编译后的sql类似于“insert into user(u_name,u_pass,type_id) values(‘username1‘,‘opass‘,1)”;
insert.complie():可选的编译步骤,在调用执行方法时自动编译,
执行:
    execute方法用于执行普通插入;
    executeAndReturnKey用于执行并获取自动生成主键(注意是Number类型),必须首先通过setGenerateKeyName设置主键然后才能获取,如果想获取复合主键请使用setGeneratedKeyNames描述主键然后通过executeReturningKeyHolder获取复合主键KeyHolder对象;
    executeBatch用于批处理

SimpleJdbcCall:用于调用存储过程及自定义函数,本类用于简化存储过程及自定义函数调用

@Test
    public void testSimpleJdbcCall(){
        //定义简单模板对象
        SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate);
        //关联函数名称
        call.withFunctionName("FUNCTION_TEST");
        //定义函数输入输出参数描述
        call.declareParameters(new SqlOutParameter("result",Types.INTEGER));
        call.declareParameters(new SqlParameter("str",Types.VARCHAR));
        //执行函数其中参数为输入参数,并且将结果存到Map中,key为输出参数名称,value为对应的值
        Map<String, Object> outVlues = call.execute("nihao");
        Assert.assertEquals(5, outVlues.get("result"));
    }

2控制数据库连接

Spring JDBC通过DataSource控制数据库连接,即通过DataSource实现获取数据库连接

    Spring JDBC提供了以下DataSource实现:

        DriverManagerDataSource:简单封装了DriverManager获取数据库连接;通过DriverManger的getConnection方法获取数据库连接

        SingleConnectionDataSource:内部封装了一个连接,该连接使用后不会关闭,且不能在多线程环境中使用,一般用于测试

        LazyConnectionDataSourceProxy:包装一个DataSource,用于延迟获取数据库连接,只有在真正创建Statement时才获取连接,因此再说实际项目中最后使用该代理包装原始DataSource从而使得只有在真正需要连接时才去获取。
    第三方提供的DataSource实现主要有C3P0、Proxool、DBCP等,这些实现都具有数据库连接池能力

    DataSourceUtils:Spring JDBC抽象框架内部都是通过它的getConnection(DataSource dataSource)方法获取数据库连接,
    releaseConnection(Connection con,DataSource dataSource)用于释放数据库连接,DataSourceUtils用于支持Spring管理事务,只有使用DataSourceUtils获取的连接才具有Spring管理事务

3 获取自动生成的主键

有许多数据库提供自动生成主键的能力,因此我们可能需要获取这些自动生成的主键,JDBC3.0标准支持获取自动生成主键,且必须数据库支持自动生成主键获取

    JdbcTemplate获取自动生成主键方式:

        @Test
    public void testFetchKey(){
        final String insertSql = "insert into user(u_name) values(‘nihao1‘)";
        //generatedKeyHolder是KeyHolder类型,用于获取自动生成的主键或复合主键
        KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {//执行需要返回自动生成主键的插入语句,其中psc用于创建PreparedStatement并指定自动生成键

            @Override
            public PreparedStatement createPreparedStatement(Connection con)
                    throws SQLException {

                return con.prepareStatement(insertSql,new String[] {"u_id"});

            }
        },generatedKeyHolder);
        //打印自动生成的主键
        System.out.println(generatedKeyHolder.getKey());
    }
使用JdbcTemplate的update(final PreparedStatementCreator psc,final KeyHolder generatedKeyHolder)方法执行需要返回自动生成主键的插入语句,其中psc用于创建PreparedStatement并指定自动生成键,如prepareStatement(insertSql,new String[] {"u_id"});generatedKeyHolder是KeyHolder类型,用于获取自动生成的主键或复合主键,如使用getKey方法获取自动生成的主键

2.SqlUpdate获取自动生成主键方式

@Test
    public void testFetchKey2(){
        //定义sql语句
        final String insertSql = "insert into user(u_name) values(‘nihao1‘)";
        //定义用于获取主键的Holder对象
        KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
        //创建SqlUpdate对象
        SqlUpdate sqlUpdate = new SqlUpdate();
        //为对象设置数据源,没有数据源是不行的
        sqlUpdate.setJdbcTemplate(jdbcTemplate);
        //表示要获取自动生成键
        //sqlUpdate.setReturnGeneratedKeys(true);
        //第二种:指定自动生成键列名
        sqlUpdate.setGeneratedKeysColumnNames(new String[]{"u_id"});
        //设置要执行的语句
        sqlUpdate.setSql(insertSql);
        //执行语句并且设置用于获取主键的Holder对象
        sqlUpdate.update(null, generatedKeyHolder);
        //打印返回结果主键值
        System.out.println(generatedKeyHolder.getKey());
    }

3.SimpleJdbcInsert:

4JDBC批量操作

JDBC批处理用于减少与数据库交互的次数来提升性能,SpringJDBC抽象框架通过封装批处理操作来简化批处理操作
1.JdbcTemplate批处理:支持普通的批处理及占位符批处理:

直接调用batchUpdate(String[] batchSql);
public void testBatchUpdate(){
        String insertSql = "insert into user(u_name) values(‘hello1‘)";
        String[] batchSql = new String[] {insertSql ,insertSql};
        //返回的结果为int[] ,里面存放的是每一条语句执行影响的行数值,按顺序存放
        int[] values = jdbcTemplate.batchUpdate(batchSql);
    }
通过batchUpdate(String sql,final BatchPreparedStatementSetter pss)方法进行批处理,该方式使用预编译语句,然后通过BatchPreparedStatementSetter实现进行设置setValues及指定批处理大小getBatchSize。
public void testBatchUpdate2(){
        String insertSql = "insert into user(u_name) values(?)";
        final String[] batchValues = new String[] {"names1","namsese"};
        jdbcTemplate.batchUpdate(insertSql, new BatchPreparedStatementSetter() {

            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                //设置占位符的值
                ps.setString(1, batchValues[i]);
            }

            @Override
            public int getBatchSize() {
                //返回批处理语句长度
                return batchValues.length;
            }
        });

    }
2.NamedParameterJdbcTemplate批处理:支持命名参数批处理
@Test
    public void testBatchUpdate3(){
        //定义NamedParameterJdbcTemplate对象,我们需要执行批处理操作batchUpdate(sql,batchValues),有两个方法可以选择,Map<String,?>[]或SqlParameterSource[]
        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);

        String insertSql = "insert into user(u_name) values(:username)";
        User user = new User();
        user.setUsername("lizhenhua");
        //方式一:使用sqlParameterSource[]构成批处理语句 ,必须使用SqlParameterSourceUtils工具类进行创建createBatch方法进行创建,方法中有两种参数一个是Map[]一个是Objec[]
        SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(new Object[] {user,user});
        int[] counts = namedParameterJdbcTemplate.batchUpdate(insertSql, params);
        System.out.println(counts.length);

        //构造maps参数
        Map<String,Object> map1 = new HashMap<String, Object>();
        map1.put("username", "zhangsan");
        Map<String,Object> map2 = new HashMap<String, Object>();
        map2.put("username", "lisi");
        HashMap<String,Object>[] maps = new HashMap[2];
        maps[0] = (HashMap<String, Object>) map1;
        maps[1] = (HashMap<String, Object>) map2;   
        //方式二
        SqlParameterSource[] params2 = SqlParameterSourceUtils.createBatch(maps);
        int[] counts2 = namedParameterJdbcTemplate.batchUpdate(insertSql, params2);
        System.out.println(counts2.length); 
        //方式三
        int[] counts3 = namedParameterJdbcTemplate.batchUpdate(insertSql,maps);
        System.out.println(counts3.length);         
    }
通过batchUpdate(String sql,SqlParameterSource[] batchArgs)方法进行命名参数批处理,batchArgs指定批处理数据集。
SqlParameterSourceUtiles.createBatch用于根据javabean对象或者Map对象创建相应的BeanPropertySqlParameterSource或MapSqlParameterSource。

3.SimpleJdbcTemplate批处理:已更简单的方式进行批处理

@Test
    public void testBatchUpdate4(){
        SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(jdbcTemplate);
        String insertSql = "insert into user(u_name) values(?)";
        //使用List<Object[]>处理占位符
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        batchArgs.add(new Object[]{"name4"});
        batchArgs.add(new Object[]{"name5"});
        simpleJdbcTemplate.batchUpdate(insertSql, batchArgs);
    }
    本示例使用batchUpdate(String sql,List<Object[]> batchArgs)方法完成占位符批处理,当然也支持命名参数批处理


    4.SimpleJdbcInsert批处理

    @Test
    public void testBatchUpdate5(){
        SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        //设置关联表user 
        simpleJdbcInsert.withTableName("user");
        Map<String, Object> map = new HashMap<String, Object>();
        //设置关联列对应的值
        map.put("u_name", "nihao");
        map.put("u_pass", "nihaoa");
        //批量执行语句
        int[] count = simpleJdbcInsert.executeBatch(new Map[] {map,map});
        Assert.assertEquals(2, count.length);
    }

五、集成SpringJDBC及最佳实践

    大多数情况下Spring JDBC都是与IOC容器一起使用。通过配置方式使用SpringJDBC。
    而且大部分时间都是使用JdbcTemplate类(或SimpleJdbcTemplate和NamedParameterJdbcTemplate)进行开发,即可能80%时间使用JdbcTemplate类,而只有20%时间使用其他类开发,符合80/20法则。

    SpringJDBC通过实现DaoSupport来支持一致的数据库访问
    NamedParameterJdbcDaoSupport:继承JdbcDaoSupport,同时提供NamedParameterJdbcTemplate访问
    SimpleJdbcDaoSupport:继承JdbcDaoSupport,同时提供SimpleJdbcTemplate访问。

    由于JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcTemplate类使用DataSourceUtils获取及释放连接,而且连接是与线程绑定的,因此这些JDBC模板类是线程安全的,即JdbcTemplate对象可以在多线程中使用

1 首先定义Dao接口

package com.lizhenhua.test.dao;

import com.lizhenhua.test.pojo.User;

public interface IUserDao {
    public void save(User user);
    public int countAll();
}

2 定义Dao实现类,此处使用Spring JDBC实现

package com.lizhenhua.test.dao.jdbc;

import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

import com.lizhenhua.test.dao.IUserDao;
import com.lizhenhua.test.pojo.User;

public class UserJdbcDaoImpl extends SimpleJdbcDaoSupport implements IUserDao {
    private static final String INSERT_SQL = "INSERT INTO user(u_name,u_pass,type_id) values(:username,:password,:typeId)";
    private static final String COUNT_ALL_SQL = "SELECT count(*) from user";
    @Override
    public int countAll() {
        return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);
    }

    @Override
    public void save(User user) {
        getSimpleJdbcTemplate().update(INSERT_SQL, new BeanPropertySqlParameterSource(user));
    }

}

首先SpringJDBC实现放在dao.jdbc包里,如果有hibernate实现就放在dao.hibernate包里;其次实现类命名如XXXJdbcDaoImpl,。

3.进行资源配置 applicationContext-resources.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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <!--PropertyPlaceholderConfigurer用于替换元数据,如本示例中对bean定义中的${...}占位符资源用classpath:resources.properties中相应的元素替换-->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:resources.properties</value>
            </list>
        </property>
    </bean>
</beans>

PropertyPlaceholderConfigurer用于替换元数据,如本示例中对bean定义中的${…}占位符资源用classpath:resources.properties中相应的元素替换

<!-- 定义数据源:本示例使用proxool数据库连接池,并使用LazyConnectionDataSourceProxy包装它,从而延迟获取数据库连接;${db.driver.class}将被classpath:resources.properties中的db.driver.class替换 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <!-- 指定数据库驱动 -->
        <property name="driver" value="${db.driver.class}" />
        <!-- 数据库连接 -->
        <property name="driverUrl" value="${db.url}" />
        <!-- 用户名 -->
        <property name="user" value="${db.username}" />
        <!-- 密码 -->
        <property name="password" value="${db.password}" />
        <!-- 连接池最大连接数量 -->
        <property name="maximumConnectionCount" value="${proxool.maxConnCount}" />
        <!-- 连接池最小连接数量 -->
        <property name="minimumConnectionCount" value="${proxool.minConnCount}" />
        <!-- 连接池使用样本状况统计:1m,15m,1h,1d表示每1分钟、15分钟1小时、及一天进行一次样本统计 -->
        <property name="statistics" value="${proxool.statistics}" />
        <!-- 一次可以创建连接的最大数量 -->
        <property name="simultaneousBuildThrottle" value="${proxool.simultaneousBuildThrottle}" />
        <!-- true表示被执行的每个sql都将被记录(DEBUG级别时被打印到相应的日志文件) -->
        <property name="trace" value="${proxool.trace}" />
    </bean>
resources.properties

proxool.maxConnCount=10
proxool.minConnCount=5
proxool.simultaneousBuildThrottle=30
proxool.trace=false
proxool.statistics=1m,15,1h,1d
db.driver.class=com.mysql.jdbc.Driver
db.url=jdbc\:mysql\://localhost\:3306/study_db
db.username=root
db.password=root

dao定义配置applicationContext-jdbc.xml

<!-- 定义抽象Bean abstractDao,其中有一个dataSource属性,从而可以让继承的子类自动继承dataSource属性注入 -->
    <bean id="abstractDao" abstract="true">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 定义userDao,且继承abstractDao,从而继承dataSource注入 -->
    <bean id="userDao" class="com.lizhenhua.test.dao.jdbc.UserJdbcDaoImpl" parent="abstractDao">
    </bean>
@Test
    public void testDao(){
        String[] configLocations = new String[] {
                "classpath:applicationContext-resources.xml",
                "classpath:applicationContext-jdbc.xml"
        };
        ApplicationContext context = new ClassPathXmlApplicationContext(configLocations);
        IUserDao userDao = context.getBean(IUserDao.class);
        User user = new User();
        user.setUsername("zhaozilong");
        user.setPassword("qinglong");
        user.setTypeId(1);
        userDao.save(user);
        System.out.println(userDao.countAll());
    }

Spring的JDBC详解

标签:

原文地址:http://blog.csdn.net/li286487166/article/details/51263612

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!