Spring JDBC是Spring所提供的持久层技术。主要目的是降低使用JDBC API的门槛,以一种更直接,更简洁的方式使用JDBC API。在Spring JDBC里用户仅需要做哪些比不可少的事,而将资源获取,Statement创建,异常处理,资源释放等繁杂而乏味的工作交交给Spring.
一、使用Spring JDBC
使用JDBC编写数据库的时候,由于JDBC API过于底层,开发者不但需要编写数据操作代码,还需要编写获得JDBC连接、异常处理、释放资源等。而Spring JDBC通过模板和回调机制大大降低了使用JDBC的复杂度,借助JdbcTemplate用户只需要编写实际操作的代码就可以了、
1、使用JdbcTemplate创建一张表
DriverManagerDataSource ds=new DriverManagerDataSource(); //创建数据源
ds.setDriverClassName(com.mysql.jdbc.Driver);
ds.setUrl("jdbc://localhost:3309/sampledb");
ds.setUsername("root");
ds.setPassword("1234");
JdbcTemplate jdbcTemplate=new JdbcTemplate();//生成一个JdbcTemplate的实例
jdbc.setDataSource(ds);
String sql="create table t_user(user_id int primary key,user_name varchar(60))";//创建一张表
jdbcTemplate.execute(sql);
在上述中,使用DriverManagerDataSource创建一个数据源,紧接着,创建了一个JdbcTemplate对象,然后使用该对象执行SQL语句。JdbcTemplate是线程安全的所有的DAO都共享一个JdbcTemplate实例。因此上述中创建数据源和生成JdbcTemplate都可以在Spring配置文件中统一定义了。
2、在DAO中使用JdbcTemplate
@Repository
public class ViewSpaceDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void initDb() {
String sql = "create table t_user(user_id int primary key,user_name varchar(60))";
jdbcTemplate.execute(sql);
}}
在Spring配置文件中定义JdbcTemplate并注入每个DAO中。
<!--扫描包以注册注解声明的Bean-->
<context:component-scan base-package="com.smart"/>
<!--配置数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!--声明JdbcTemplate-->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"/>
在Spring配置文件中配置DAO一般分为4个步骤:
1)定义DataSource
2)定义JdbcTemplate
3)声明一个抽象的Bean,以便所有DAO复用配置JdbcTemplate属性的配置
4)配置具体的DAO
实战经验:
Spring几乎为所有的模板类都提供相应的支持类,与JdbcTemplate对应的支持类为JdbcDaoSupport.JdbcDaoSupport内部定义了JdbcTemplate的成员变量,开发者可以通过扩展JdbcDaoSupport定义自己的Dao,但是随着Bean的注解配置逐渐成为主流配置方式。这种方法便不再可用,因为直接继承JdbcDaoSupport无法对JdbcTemplate成员变量应用@Autowired注解,所以,一般是自己定义一个BaseDao,在BaseDao中定义JdbcTemplate成员变量,并使用@Autowired注解。还会在BaseDao中定义一些通用的功能,如声明JdbcTemplate,分页查询等、
二、基本的数据操作
数据库的增删改查以及存储过程调用时最基本的数据库操作,JdbcTemplate提供了众多的方法,通过JdbcTemplate用户可以用最简单的方法完成这些数据操作。
1、更改数据
JdbcTemplate提供了若干update()方法,允许用户对数据表记录进行更改和删除操作。
@Repository
public class ViewSpaceDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addViewSpace(final ViewSpace viewSpace) {
final String sql = "INSERT INTO t_view_space(space_name,description,address) VALUES(?,?,?)";
Object[] params = new Object[] { viewSpace.getSpaceName(),viewSpace.getDescription(),viewSpace.getAddress() };
// 方式1
jdbcTemplate.update(sql, params);}
}
JdbcTemplate在内部通过PreparedStatement执行SQL语句,左移可以使用绑定参数的SQL语句。在sql中?代表一个占位符,而param中的是用于填充占位符的参数数组,在最后直接通过int update(String sql,Object[]args)方法进行表数据的更新
通过update(String sql,Object[]args)方法为SQL语句的占位符绑定参数时,并没有显示指定对应字段的数据类型。Spring直接让PreparedStatement进行猜测。更好的方法是使用int update(String sql,Object[]args,int[]argTypes)显示指定每个占位符所对应的字段数据类型
此时上述的 jdbcTemplate.update(sql, params);}就会变为jdbcTemplate.update(sql, params,new int[]{Types.VARCHAR,Types.VARCHAR});
除了以上的两个update()方法,JdbcTemplate还提供了以下几个功能相似的重载方法
int update(String sql):为不带占位符的SQL语句所提供的方法
int update(String sql,Object...args):使用不定参数的方法
int update(String sql,PreparedStatementSetterpss):PreparedStatementSetter是一个回调接口,他定义了一个void setValues(PreparedStatement ps)接口方法。JdbcTemplate使用SQL语句创建出PreparedStatement实例后,将调用接口执行绑定参数的操作。
jdbcTemplate.update(sql, new PreparedStatementSetter() { public void
setValues(PreparedStatement ps) throws SQLException { ps.setString(1,forum.getForumName()); ps.setString(2, forum.getForumDesc()); } });
int update(PreparedStatementCreator psc):这也是一个回调接口,他负责创建一个PreparedStatement实例,该接口回调定义了一个PreparedStatement createPrepareStatement(Connection con)方法
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, viewSpace.getSpaceName());
ps.setString(2, viewSpace.getDescription());
ps.setString(3, viewSpace.getAddress());
return ps;
}
}, keyHolder);
viewSpace.setSpaceId(keyHolder.getKey().intValue());
protected int update(PreparedStatementCreator psc,PreparedStatementSetter pss):联合使用PreparedStatementCreator和PreparedStatementSetter回调。
2、返回数据库的表自增主键值
用户经常使用数据的自增字段作为表主键,即主键值不在应用层产生,而是在新增记录时数据库产生。
当新增记录时允许将数据库自动产生的主键值绑定到Statement或PreparedStatement中,使用Statement时,可以通过如下方法绑定主键值。
int executeUpdate(String sql,int autoGenerateKeys)
也可以通过Connection创建绑定自增值的PreparedStatement
PreparedStatement prepareStatement(String sql,int autoGenerateKeys)
当autoGeneratedKeys参数设置为Statement.RETURN_GENERATED_KEYS值时即可绑定数据库产生的主键值,设置为Statement._NO_GENERATED_KEYS不绑定主键值
Statement stmt=conn.createStatement();
String sql="INSERT INTO T_VIEW(space_name,description)VALUES('拨浪鼓','*旅游度假区')";
stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);//指定绑定自增主键值
if(rs.next()){int key=rs.getInt();}//获取对应的表自增主键值
3、批量更改数据
public int[] batchUpdate(String[]sql):多条SQL语句组成一个数组,该方法以批量方式执行这些SQL语句,Spring在内部使用JDBC提供的批量更新API完成操作,如果底层的Jdbc Driver不支持批量更新操作,Spring将采用逐条更新的方式模拟批量更新
int[] batchUpdate(String sql,BatcgPreparedStatementSetter pss):使用本方法对于统一结构的带参SQL语句多次进行数据更次年操作。通过BatchPreparedStatementSETTER回调接口进行批量参数的绑定工作。而BatchPreparedStatementSetter又定义了两个方法
int getBatchSize():指定本批次的大小
void setValues(PreparedStatement ps ,int i):为指定的PreparedStatement设置参数
public void addViewSpaces(final List<ViewSpace> viewSpaces) {
final String sql = "INSERT INTO t_view_space(space_name,description,address) VALUES(?,?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public int getBatchSize() {
return viewSpaces.size();
}
public void setValues(PreparedStatement ps, int index)
throws SQLException {
ViewSpace viewSpace = viewSpaces.get(index);
ps.setString(1, viewSpace.getSpaceName());
ps.setString(2, viewSpace.getDescription());
ps.setString(3, viewSpace.getAddress());
}
});
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public int getBatchSize() {
return viewSpaces.size();
}
public void setValues(PreparedStatement ps, int index)
throws SQLException {
ViewSpace viewSpace = viewSpaces.get(index);
ps.setString(1, viewSpace.getSpaceName());
ps.setString(2, viewSpace.getDescription());
ps.setString(3, viewSpace.getAddress());
}
});
}
BatchPreparedStatementSetter是一次性批量提交数据,而不会分批提交,getBatchSize()是整批的大小,所以,如果希望一个LIST钟的数据通过BatchPreparedStatementSETTER批量更新到数据库中getBatchSize()就应该设置为List的大小。
4、数据查询
使用RowCallbackHandler处理结果集
Spring提供了org.springframework.jdbc.core.RowCallbackHandler回调接口,通过该接口可以定义如何从结果集中获取数据。该接口只有一个方法void processRow(ResultSet rs) throw SQLException.Spring会遍历结果集,对结果集中的每一行调用RowCallbackHandler回调接口处理数据,所以我们无须调用ResultSet的next()方法,而只是定义如何获取结果行中的数据的逻辑就可以了。
示例:通过id获取数据
public ViewSpace getViewSpace(final int spaceId) {
String sql = "SELECT space_name,description,address FROM t_view_space WHERE space_id=?";
final ViewSpace viewSpace = new ViewSpace();
jdbcTemplate.query(sql, new Object[] { spaceId },
new RowCallbackHandler() {//将结果集数据航中的数据抽取到viewSpace对象中
public void processRow(ResultSet rs) throws SQLException {
viewSpace.setSpaceId(spaceId);
viewSpace.setSpaceName(rs.getString("space_name"));
viewSpace.setDescription(rs.getString("description"));
viewSpace.setAddress(rs.getString("address"));
}
});
return viewSpace;
}
示例:返回多条数据
public List<ViewSpace> getViewSpaces(final int fromId, final int toId) {
String sql = "SELECT space_id, SPACE_NAME,description,address FROM t_view_space WHERE space_id between ? and ?";
// 方法1:使用RowCallbackHandler接口
final List<ViewSpace> viewSpaces = new ArrayList<ViewSpace>();
jdbcTemplate.query(sql,new Object[]{fromId,toId},new
RowCallbackHandler(){ public void processRow(ResultSet rs) throws//将结果集中的数据映射到List中
SQLException {
viewSpace = new ViewSpace();
viewSpace.setSpaceId(rs.getInt("space_id"));
viewSpace.setSpaceName(rs.getString("space_name"));
viewSpace.setDescription(rs.getString("description"));
viewSpace.setAddress(rs.getString("address"));
viewSpaces.add(viewSpace);
}}); return forums;
}
5、查询单值数据
如果查询的结果仅有一个值,可以用更简单的方式获得结果的值。JdbcTemplate为获取结果集的单值数据提供了3个方法,分别用于获取int long 的单值,其他类型的单值以Object类型返回。
int类型的单值查询接口:
int queryForInt(String sql)
int queryForInt(String sql,Object...args)
int queryForInt(String sql,Object[]args,int[]argTypes)
示例:
public int getViewSpaceNum(){
String sql="select count(*) from t_view_space ";
return getJdbcTemplate。queryForInt(sql);
}
long类型的单值查询接口:
long queryForLongString sql)
long queryForLong(String sql,Object...args)
long queryForLong(String sql,Object[]args,int[]argTypes)
其他类型的单值查询接口:
<T> queryForObject(String sql,Class<T>requiredType)
<T> queryForObject(String sql,Object[]args,Class<T>requiredType)
<T> queryForObject(String sql,Object[]args,int[]argTypes,Class<T>requiredType)
<T> queryForObject(String sql,Object[]args,int[]argTypes,RowMapper<T>rowMapper)
<T> queryForObject(String sql,Object[]args,RowMapper<T>rowMapper)
<T> queryForObject(String sql,RowMapper<T>rowMapper)
6、调用存储过程
JdbcTemplate提供了两个调用存储过程的接口方法
1)<T>T execute(String callString,CallableStatementCallback<T>action)
用户同callString参数指定调用存储过程的SQL语句。第二个参数CallableStatementCallback<T>是一个回调接口,该接口只有一个方法T DoInCallableStatement(CallableStatement cs)影虎可以在该方法中进行输入参数绑定,输出参数注册一级返回数据处理等操作
2)<T> T execute(CallableStatementCreator csc,CallableStatementCallback<T>action)
该接口方法使用CallableStatementCreator创建CallableStatement,CallableStatementCreator定义了一个方法:CallableStatement createCallableStatement(Connection con),他使用Connection实例创建CallableStatement对象。CallableStatementCallable<T>负责处理存储过程的返回结果。
Spring提供了创建CallableStatementCreator的工厂类CallableStatementCreatorFactory,通过该工厂类可以简化CallableStatementCreator的实例的创建工作。
例如创建一个存出过场一个入参in_space_id一个出参out_num
public int getViewPointNum(final int spaceId){
String sql="{call P_GET_VIEW_POINT_NUM(?,?)}";//调用存储过程的SQL语句
Integet num=jdbcTemplate.execute(sql,new CallableStatementCallback<Integer>(){
public Integer doInCallableStatement(CallableStatement cs) throws SQLException,DataAccessException{
cs.setInt(1,spaceId);//绑定入参
cs.registerOutParameter(2,Types.INTEGER);//注册输出参数
cs.execute();
return cs.getInt(2); }//获取输出参数的值
});
return num;
}
或者:
public int getSpaceViewPointNum(final int spaceId) {
String sql = "{call P_GET_VIEW_POINT_NUM(?,?)}";
CallableStatementCreatorFactory fac = new CallableStatementCreatorFactory(sql);//以一个SQL语句为入参创建CallableStatementCreator工厂实例
fac.addParameter(new SqlParameter("spaceId", Types.INTEGER));//设置存储过程的入参
fac.addParameter(new SqlOutParameter("pointNum", Types.INTEGER));//设置存储过程的出参
Map<String, Integer> paramsMap = new HashMap<String, Integer>();
paramsMap.put("spaceId", spaceId);//为前面的入参指定参数值
CallableStatementCreator csc = fac.newCallableStatementCreator(paramsMap);
Integer num = jdbcTemplate.execute(csc, new CallableStatementCallback<Integer>() {//通过new CallableStatementCallback<Integer>创建一个CallableStatementCreator
public Integer doInCallableStatement(CallableStatement cs)
throws SQLException, DataAccessException {
cs.execute();
return cs.getInt(2);
}
});
return num;
}
三、BLOB/CLOB类型数据的操作
1、插入Lob类型的数据
用于保存数据的表有两个Lob字段,其中description是CLOB类型,而imageFile是BLOB类型
package com.smart.dao;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.smart.domain.ViewPoint;
import oracle.jdbc.OracleConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.LobRetrievalFailureException;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.CallableStatementCreatorFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Repository;
import org.springframework.util.FileCopyUtils;
@Repository
public class ViewPointDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private LobHandler lobHandler;//定义LobHandler属性
@Autowired
private DataFieldMaxValueIncrementer incre;
public void addViewPoint(final ViewPoint viewPoint) {
String sql = " INSERT INTO t_view_point(point_id,space_id,point_name,ticket_price,img_file,description)"
+ " VALUES(?,?,?,?,?,?)";
jdbcTemplate.execute(sql, new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException {
//1:固定主键
//ps.setInt(1,1);
//2:通过自增键指定主键值LobHandler属性
ps.setInt(1, incre.nextIntValue());
ps.setInt(2, viewPoint.getSpaceId());
ps.setString(3, viewPoint.getPointName());
ps.setDouble(4, viewPoint.getTicketPrice());
lobCreator.setClobAsString(ps, 6, viewPoint.getDescription());//设置Clob字段
lobCreator.setBlobAsBytes(ps, 5, viewPoint.getImgFile());//设置Blob字段
}
});
}}
首先在ViewPointDao中引入一个LobHandler属性,并通过jdbcTemplate.execute(sql, new AbstractLobCreatingPreparedStatementCallback(this.lobHandler)方法完成插入Lob数据的操作,通过匿名内部类的方式定义LobCreatingPreparedStatementCallback抽象类的子类,其构造函数需要一个LobHandler入参。
在匿名类中实现了父类的抽象方法setValues(PreparedStatement ps,LobCreator lobCreator)在该方法中,通过lobCreator操作Lob对象。
配置文件中做更改:
<bean id="nativeJdbcExtractor"
class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
lazy-init="true"/>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler"<!--使用设置本地JDBC对象抽取只要不是oracle9i则可以将OracleLobHandler改为DefaultLobHandler而且也不需要 <bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler"<!--使用设置本地JDBC对象抽取只要不是oracle9i则可以将OracleLobHandler改为DefaultLobHandler而且也不需要-p:nativeJdbcExtractor-ref="nativeJdbcExtractor"->-->
lazy-init="true" p:nativeJdbcExtractor-ref="nativeJdbcExtractor"/>
设置为lazy=true是因为nativeJdbcExtractor需要通过运行期的反射机制获取底层的jdbc对象,所以需要避免让Spring容器启动时就实例化这两个Bean
LobHandler需要访问本地Jdbc对象,因此需要通过一个NativeJdbcExtractor Bean来完成任务
2、以块数据方式获取Lob数据
用户可以直接以数据块的方式读取Lob数据:以String读取Clob字段的数据,而以byte[]读取Blob字段的数据。
public List<ViewPoint> getImgFiles(final int spaceId) {
String sql = " SELECT point_id,img_file FROM t_view_point where point_id =? and img_file is not null ";
return jdbcTemplate.query(sql, new Object[]{spaceId},
new RowMapper<ViewPoint>() {
public ViewPoint mapRow(ResultSet rs, int rowNum)throws SQLException {
int pointId = rs.getInt(1);
byte[] attach = lobHandler.getBlobAsBytes(rs, 2);//以二进制的方式获取Blob数据
ViewPoint viewPoint = new ViewPoint();
viewPoint.setPointId(pointId);
viewPoint.setImgFile(attach);
return viewPoint;
}
});
}
通过JdbcTemplate的List<T>query(String sql;Object[]args,RowMapper rowMapper)接口处理行数据的映射,在RowMapper回调的mapRow()接口方法中,通过LobHandler以byte[]获取Blob字段的数据
3、以流数据方式读取Lob数据
由于Lob数据的体积可能很大,如果直接以块的方式操作Lob数据,需要消耗大量的内存,直接影响到程序的整体运行,对于体积很大的Lob数据,可以使用流的方式进行访问,减少内存的占用,JdbcTemplate为此提供了一个Object query(String sql,Object[]args,ResultSetExtractor rse)fangfa
ResultSetExtractor接口拥有一个处理流数据的抽象类org.springframework.jdbc.core.supportAbstractLobStreamingResultS,可以通过扩展此类用流的方式操作Lob字段的数据
public void getImgFile(final int pointId, final OutputStream os) {//用于接收Lob数据的输出流
String sql = " SELECT img_file FROM t_view_point where point_id =? ";
jdbcTemplate.query(sql, new Object[]{pointId},
new AbstractLobStreamingResultSetExtractor() {//匿名内部类
protected void handleNoRowFound() throws LobRetrievalFailureException {//处理未找到数据行的情况
System.out.println("Not Found result!");
}
public void streamData(ResultSet rs) throws SQLException, IOException {//以流的方式处理Lob字段
InputStream is = lobHandler.getBlobAsBinaryStream(rs, 1);
if (is != null) {
FileCopyUtils.copy(is, os);
}
}
}
);
}
通过扩展 AbstractLobStreamingResultSetExtractor抽象类在streamData(ResultSet rs)方法中以流的方式读取Lob字段数据。这里又利用了Spring的工具类FileCopyUtils,将输入流的数据复制到输出流中。在getImgFile方法中通过入参OutputStream接收Lob的数据
四、其他类型的JdbcTemplate
除了标准的JdbcTemplate之外,Spring还提供了两个好用的JDBC模板类,他们分别是NamedParameterJdbcTemplate和SimpleJdbcTemplate.
1、NamedParameterJdbcTemplate
在低版本的Spring中,用户只能使用?占位符声明参数,并使用索引号绑定参数,使用这种方法绑定参数时,必须足够小心,以确保正确匹配。
@Repository
public class ViewSpaceDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void addViewSpaceByNamedParams(final ViewSpace viewSpace) {
final String sql = "INSERT INTO t_view_space(space_name,description,address) VALUES(:spaceName,:description,:address)";//定义参数源
SqlParameterSource sps = new BeanPropertySqlParameterSource(viewSpace);//定义命名参数
namedParameterJdbcTemplate.update(sql, sps);//使用模板方法
}
}
需要在Spring的配置文件中添加
<bean id="namedParamJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
在SQL语句中,声明命名参数的格式是:paramName,在这个示例中使用BeanPropertySqlParameterSource提供参数源,他接受一个JavaBean作为构造函数的入参,和标准的JdbcTemplate一样,NamedParameterJdbcTemplate提供了很多的数据访问方法,这些方法大多都拥有一个SqlParameterSource入参,用来提供参数源
上述中的spaceName等都是和Sql语句中命名参数匹配,如果数据表记录没有对应的领域对象
MapSqlParameterSource sps=new MapSqlParameterSource().addValue("spaceName",viewSpace.getSpaceName()).addValue("address",viewSpace.getAddress());
五、使用OO方式访问数据库
org.springframework.jdbc.object包下的类允许用户以更加面向对象的方式访问数据库,用户通常也可以直接使用JdbcTemplate完成类似的操作,相对于把一个查询操作封装成一个类而言,我们直接调用JdbcTemplate方法更加简洁,更容易理解。
1、使用MappingSqlQuery查询数据
SqlQuery是一个可重用,线程安全的类,它封装了一个SQL查询。用户很少需要直接使用SqlQuery,因为其子类MappingSqlQuery作为一个更加易用的实现类能够将结果集中的行映射为Java对象,SqlQuery还有两个扩展类,分别是MappingSqlQueryWithParameters和UpdateableSqlQuery
@Repository
public class ViewSpaceOODao {
@Autowired
private DataSource dataSource;
private ViewSpaceQuery viewSpaceQuery;//①声明ViewSpaceQuery变量
@PostConstruct
public void init() {
this.viewSpaceQuery = new ViewSpaceQuery(this.dataSource); //②初始化ForumQuery对象
this.viewSpaceInsert = new ViewSpaceInsert(this.dataSource);
this.viewPointNum = new GetViewPointNum(this.dataSource);
this.viewSpaceNumCount = new SqlFunction<Integer>(dataSource, "SELECT COUNT(*) FROM t_view_space");
this.viewSpaceNumCount.compile();
}
public ViewSpace getViewSpace(int spaceId) {
return viewSpaceQuery.findObject(spaceId); //③执行查询并返回结果
}
//④定义MappingSqlQuery
private class ViewSpaceQuery extends MappingSqlQuery<ViewSpace> {
public ViewSpaceQuery(DataSource ds) {//⑤定义查询语句并预编译
super(ds, "SELECT space_id,space_name, description FROM t_view_space WHERE space_id=?");
declareParameter(new SqlParameter(Types.INTEGER));
compile();//⑤-1不能忘记这行编译语句,否则会发生错误
}
public ViewSpace mapRow(ResultSet rs, int rownum) throws SQLException {//⑥
ViewSpace viewSpace = new ViewSpace();
viewSpace.setSpaceId(rs.getInt("space_id"));
viewSpace.setSpaceName(rs.getString("space_name"));
viewSpace.setDescription(rs.getString("description"));
return viewSpace;
}
}
使用MappingSqlQuery一般分为以下3个步骤:
1)定义子类,在子类中声明SQL语句并定义行数据映射逻辑
2)声明子类来的变量并实例化该类
3)使用MappingSqlQuery的方法执行数据查询操作
2、使用SqlUpdate更新数据
@Repository
public class ViewSpaceOODao {
@Autowired
private DataSource dataSource;
private ViewSpaceQuery viewSpaceQuery;//①声明ViewSpaceQuery变量
private ViewSpaceInsert viewSpaceInsert;
@PostConstruct
public void init() {
this.viewSpaceQuery = new ViewSpaceQuery(this.dataSource); //②初始化ForumQuery对象
this.viewSpaceInsert = new ViewSpaceInsert(this.dataSource);
}
//①新增Forum对象
public void addViewSpace(ViewSpace viewSpace) {
viewSpaceInsert.insert(viewSpace);
}
//②扩展SqlUpdate定义子类
private class ViewSpaceInsert extends SqlUpdate {
public ViewSpaceInsert(DataSource ds) {//③定义SQL语句并预编译
super(ds, "INSERT INTO t_view_space(space_name, description) VALUES(:spaceName,:description)");
declareParameter(new SqlParameter("spaceName", Types.VARCHAR));
declareParameter(new SqlParameter("description", Types.VARCHAR));
compile();
}
}
3、使用StoredProcedure执行存储过程
@Repository
public class ViewSpaceOODao {
@Autowired
private DataSource dataSource;
private ViewSpaceQuery viewSpaceQuery;//①声明ViewSpaceQuery变量
private ViewSpaceInsert viewSpaceInsert;
private GetViewPointNum viewPointNum;
@PostConstruct
public void init() {
this.viewSpaceQuery = new ViewSpaceQuery(this.dataSource); //②初始化ForumQuery对象
this.viewSpaceInsert = new ViewSpaceInsert(this.dataSource);
this.viewPointNum = new GetViewPointNum(this.dataSource);
}
public int getViewPointNum(int userId) {
return viewPointNum.getViewPointNum(userId);
}
//①扩展StoredProcedure
private class GetViewPointNum extends StoredProcedure {
private static final String SQL = "P_GET_VIEW_POINT_NUM";//②定义存储过程名
public GetViewPointNum(DataSource ds) {
setDataSource(ds);
setSql(SQL);
//③声明入参
declareParameter(new SqlParameter("spaceId", Types.INTEGER));
//④声明出参
declareParameter(new SqlOutParameter("outNum", Types.INTEGER));
compile();
}
public int getViewPointNum(int userId) {//⑤执行存储过程并返回结果
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("userId", userId);
Map<String, Object> outMap = execute(map);
return (Integer) outMap.get("outNum");
}
}
}
通过StoredProcedure执行存储过程SQL语句只需指定存储过程的名,不通过占位符声明入参和出参。而通过declareParameter()方法声明入参合成出参。
4、SqlFunction类
SqlFunction操作类封装了一个SQL函数包装器,该包装器适用于查询并返回一个单行结果集的访问操作。默认返回的是一个int值,不过更推荐使用JdbcTemplate中的queryForXxx()方法返回单值数据
@Repository
public class ViewSpaceOODao {
@Autowired
private DataSource dataSource;
private SqlFunction<Integer> viewSpaceNumCount;
@PostConstruct
public void init() {
this.viewSpaceNumCount = new SqlFunction<Integer>(dataSource, "SELECT COUNT(*) FROM t_view_space");
this.viewSpaceNumCount.compile();
}
public int getViewSpaceNum() {
return viewSpaceNumCount.run();
}
}