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,