Spring JDBC是Spring所提供的持久层技术,它以一种更直接、更简单的方式使用JDBC API。在Spring JDBC里,用户仅需要做那些必不可杀的事儿,而将资源获取、Statement创建、异常处理、资源释放等繁杂的工作交给Spring。
虽然ORM的框架已经很成熟,但是JDBC灵活直接的特性依旧让它有自己的用武之地。
本节的主要内容:使用JdbcTemplate模板类进行CRUD数据操作、BLOB和CLOB类型数据的操作,支持命名参数绑定NamedParameterJdbcTemplate模拟类的使用。
1.在DAO中使用JdbcTemplate
一般情况下,在XML中配置好JdbcTemplate,然后在DAO中注入即可
Spring配置文件中定义的JdbcTemplate并注入DAO中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-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">
<!-- 注解扫描包 -->
<context:component-scan base-package="com.online"/>
<context:property-placeholder location="classpath:/jdbc.properties"/>
<!-- 配置数据源信息 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${dataSource.driverClassName}"/>
<property name="url" value="${dataSource.url}"/>
<property name="username" value="${dataSource.username}"></property>
<property name="password" value="${dataSource.password}"></property>
</bean>
<!-- 定义JDBC模板Bean -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
//@Repository声明一个DAO
@Repository
public class AdminDao implements IAdminDao {
private static Logger logger=Logger.getLogger(AdminDao.class);
private static final long serialVersionUID = 1L;
//@Autowired注入JdbcTemplate实例声明一个DAO
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Admin getAdminByNamAndPassword(String adminId, String password) {
// TODO Auto-generated method stub
String sql="select adminId,adminName,adminPassword,adminHead,MAX(FROM_UNIXTIME(lastvisittime,'%Y-%m-%d %H:%m:%S')),lastvisitip from zzia_admin left join zzia_login_log on adminId=userId "
+ "where adminName='"+adminId+"' and adminPassword='"+password+"' ";
final Admin a=new Admin();
this.jdbcTemplate.query(sql, new RowCallbackHandler() {
@Override
public void proce***ow(ResultSet rs) throws SQLException {
// TODO Auto-generated method stub
a.setAdminId(rs.getInt(1));
a.setAdminName(rs.getString(2));
a.setAdminPassword(rs.getString(3));
a.setAdminHead(rs.getString(4));
a.setLastVisitTime(rs.getString(5));
a.setLastVisitIp(rs.getString(6));
}
});
return a;
}
2.基本的数据操作
数据的增删查改以及存储过程调用是最基本的数据库操作,JdbcTemplate提供了众多的方法,通过JdbcTemplate用户可以用简单的方法完成这些数据操作
2.1更改数据
JdbcTemplate提供了若干update()方法,允许用户对数据表记录进行更改和删除操作
update示例:
public class TestJdbcTemplate {
...//这里省略jdbcTemplate的定义
private static final String sql = "insert into jf_user(user_name,user_pwd,user_age) values(?,?,?)";
public static void addUser(User user) {
Object[] params = new Object[]{user.getUsername(),user.getUserpwd(),user.getUserage()};
int update = jdbcTemplate.update(sql, params);
if(update > 0){
System.out.println("插入成功");
}
}
}
2.2、返回数据库的表自增主键值
用户经常使用数据的子增字段作为表主键,即主键值不在应用层产生,而是在新增记录时,由数据库产生。这样,应用层在保存对象前并不知道对象主键值,而只有在保存数据后才能从数据库中返回主键值。在很多情况下,需要获取新对象持久化后的主键值。在Hibernate、JPA等ORM框架中,新对象在持久化后,主键值会自动绑定到对象上,给程序的开发带来了很多方便。
Spring提供了一个可以返回新增记录对应主键值的方法。int update(PreparedStatementCreator psc,KeyHolder generatedKeyHolder);org.springframework.jdbc.support.KeyHolder是一个回调接口,spring使用它保存新增记录对应的主键。Spring为KeyHolder接口指代了一个通用的实现类GeneratedKeyHolder,该类返回新增记录时的自增长主键值。假设希望在新增对象后,将主键值加载到对象中,则代码示例
public static void addUserWithKeyHolder(final User user){
final String sql = "insert into jf_user(user_name,user_pwd,user_age) values(?,?,?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getUserpwd());
ps.setString(3, user.getUserage());
return ps;
}
}, keyHolder);
user.setSerialNo(keyHolder.getKey().intValue());
}
如果数据库并发率比较高,如在插入记录后执行查询主键之前,数据库又执行了如果条插入记录,那么通过数据查询语句新增主键值将会只返回最后一条插入的主键值。因此,使用查询语句获取的主键值是不安全的,这也是为什么有些数据库(如Oracle)故意不提供自增键,而是只提供序列的原因,序列强制要求用户在新增记录前,先获取主键值。Oracle通过SELECT <SEQUENCE_NAME>.nextval FROM DUAL获取序列的下一个值。
2.3、批量更改数据
如果需要一次性插入或更新多条记录,可以使用jdbcTemplate的batchUpdate方法
batchUpdate示例:
public static void addUsers(final List<User> users){
final String sql = "insert into jf_user(user_name,user_pwd,user_age) values(?,?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
// TODO Auto-generated method stub
User user = users.get(i);
ps.setString(1, user.getUsername());
ps.setString(2, user.getUserpwd());
ps.setString(3, user.getUserage());
}
@Override
public int getBatchSize() {
// TODO Auto-generated method stub
return users.size();
}
});
}
2.4、查询数据
2.4.1、使用RowCallbackHandler处理结果集
Spring提供了org.springframework.jdbc.core.RowCallbackHandler回调接口,通过该接口可以定义如何从结果集中获取数据。
RowCallbackHandler示例:
public User getUserBySerialNo(final int serialNo){
String sql = "SELECT serial_no,user_name,user_pwd,user_age FROM jf_user.jf_users WHERE serial_no = ?";
final User user = new User();
jdbcTemplate.query(sql, new Object[]{serialNo},new RowCallbackHandler() {
@Override
public void proce***ow(ResultSet rs) throws SQLException {
user.setSerialNo(serialNo);
user.setUsername(rs.getString("user_name"));
user.setUserpwd(rs.getString("user_pwd"));
user.setUserage(rs.getString("user_age"));
}
});
return user;
}
public List<User> getUsers(final int fromSerialNo,final int toSerialNo){
String sql = "SELECT serial_no,user_name,user_pwd,user_age FROM jf_user.jf_users WHERE serial_no between ? and ?";
final List<User> users = new ArrayList<>();
jdbcTemplate.query(sql, new Object[]{fromSerialNo,toSerialNo}, new RowCallbackHandler() {
@Override
public void proce***ow(ResultSet rs) throws SQLException {
// TODO Auto-generated method stub
User user = new User();
user.setSerialNo(rs.getInt("serial_no"));
user.setUsername(rs.getString("user_name"));
user.setUserpwd(rs.getString("user_pwd"));
user.setUserage(rs.getString("user_age"));
users.add(user);
}
});
return users;
}
2.4.2、使用RowMapper<T>处理结果集
Spring也提供了一个和RowCallbackHandler功能类似的RowMapper<T>接口,它也可以使用RowMapper<T>定义结果集逻辑映射,在结果集为多行记录时,该接口更容易使用。
RowMapper<T>代码示例
public List<User> getUsersOnRowMapper(final int fromSerialNo,final int toSerialNo){
String sql = "SELECT serial_no,user_name,user_pwd,user_age FROM jf_user.jf_users WHERE serial_no between ? and ?";
return jdbcTemplate.query(sql, new Object[]{fromSerialNo,toSerialNo},new RowMapper<User>(){
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
// TODO Auto-generated method stub
User user = new User();
user.setSerialNo(rs.getInt("serial_no"));
user.setUsername(rs.getString("user_name"));
user.setUserpwd(rs.getString("user_pwd"));
user.setUserage(rs.getString("user_age"));
return user;
}
});
}
2.5、查询单值数据
如果查询的结果集仅有一个值,如SELECT COUNT(*) FROM jf_users等,就可以使用更简单的方式获取结果的值。JdbcTemplate为获取结果集中的单值数据提供了3组方法,分别用于获取int、long的单值,其他类型的单值以Object类型返回
2.6、调用存储过程
JdbcTemplate提供了2个调用存储过程的接口方法:
<T> T execute(String callString,CallableStatementCallback<T> action);
<T> T execute(CallableStatementCreator csc,CallableStatementCallback<T> cs);
调用存储过程示例:
CREATE PROCEDURE P_GET_VIEW_POINT_NUM(IN in_serialNO,INT,OUT out_num INT)
BEGIN
SELECT COUNT(*) INTO out_num FROM jf_user WHERE serialNO = in_serialNo;
END;
public int getUserCounts(final int serailNo){
String sql = "{call P_GET_VIEW_POINT_NUM(?,?)}";
String execute = jdbcTemplate.execute(sql, new CallableStatementCallback() {
@Override
public Object doInCallableStatement(final CallableStatement cs) throws SQLException, DataAccessException {
// TODO Auto-generated method stub
cs.setInt(1, serailNo);
cs.registerOutParameter(2, Types.INTEGER);
cs.execute();
return cs.getInt(2);
}
});
return Integer.valueOf(execute);
}