标签:
传统应用程序开发中,进行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.一致的非检查异常体系
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来再必要的好似好手工获取数据库连接等。
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模板类实现
//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
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实现,
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。
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结果集。
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);
}
}
execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;
batchUpdate方法用于执行批处理相关语句;
query方法及queryForXXX方法:用于执行查询相关语句
call方法:用于执行存储过程、函数相关语句。
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全部方法及回调类的使用方法,我们只介绍代表性的,其余的使用都是类似的。
@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编程并无太多差别。
@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
方式一:
@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");
}
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);
@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);
}
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
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属性名称相对应才可以。
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对象
//一共有三种方式,因为有三个带参构造函数,还支持数据源构造、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);
@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实现
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);
}
}
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]]
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);
}
@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进行操作
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 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方法获取自动生成的主键
@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());
}
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);
}
大多数情况下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());
}
标签:
原文地址:http://blog.csdn.net/li286487166/article/details/51263612