基于JdbcTemplate的数据查询
用于查询的回调接口定义主要有以下三种:
- ResultSetExtractor
public interface ResultSetExtractor { Object extractData(ResultSet rs) throws SQLException, DataAccessException; }
2. RowCallbackHandler 只关注单行结果的处理。
public interface RowCallbackHandler { void processRow(ResultSet rs) throws SQLException; }
3. RowMapper 只关注单行结果。
public interface RowMapper { Object mapRow(ResultSet rs, int rowNum) throws SQLException; }
Example:
package com.ivy.SpringJdbc; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.RowMapper; public class CallbackDemo { @Autowired private JdbcTemplate jdbcTemplate; public List<Customer> getCustomersByResultSetExtractor() { List<Customer> customerList = (List<Customer>)jdbcTemplate.query("select * from customer", new ResultSetExtractor<List<Customer>>() { @Override public List<Customer> extractData(ResultSet rs) throws SQLException, DataAccessException { List<Customer> customers = new ArrayList<Customer>(); while(rs.next()) { Customer customer = new Customer(); customer.setFirstName(rs.getString(1)); customer.setLastName(rs.getString(1)); customer.setAge(rs.getInt(3)); customers.add(customer); } return customers; } }); return customerList; } public List<Customer> getCustomersByRowCallbackHandler() { final List<Customer> customers = new ArrayList<Customer>(); jdbcTemplate.query("select * from customer", new RowCallbackHandler() { @Override public void processRow(ResultSet rs) throws SQLException { Customer customer = new Customer(); customer.setFirstName(rs.getString(1)); customer.setLastName(rs.getString(1)); customer.setAge(rs.getInt(3)); customers.add(customer); } }); return customers; } public List<Customer> getCustomersByRowMapper() { List<Customer> customers = jdbcTemplate.query("select * from customer", new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { Customer customer = new Customer(); customer.setFirstName(rs.getString(1)); customer.setLastName(rs.getString(1)); customer.setAge(rs.getInt(3)); return customer; } }); return customers; } }
基于JdbcTemplate的数据更新
int update(String sql)
int update(String sql, Object[] args)
int update(String sql, Object[] args, int[] argTypes)
批量更新数据
int[] batchUpdate(String[] sql)
int[] batchUpdate(String sql, BatchPreparedStatementSetter pss)
Example
package com.ivy.SpringJdbc; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; public class BatchUpdateDemo { @Autowired private JdbcTemplate jdbcTemplate; public int[] insertNewCustomers(final List<Customer> customers) { return jdbcTemplate.batchUpdate("insert into customer value(?,?,?)", new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Customer customer = (Customer)customers.get(i); ps.setString(1, customer.getFirstName()); ps.setString(2, customer.getLastName()); ps.setInt(3, customer.getAge()); } @Override public int getBatchSize() { return customers.size(); } }); } }
调用存储过程
存储过程是定义于数据库服务器端的计算单元。对于涉及多表数据而只使用SQL无法完成的计算,我们可以通过在数据库服务器端编写并部署存储过程的方式来实现。相对于将这些计算逻辑转移到客户端进行,使用存储过程的好处在于,可以避免像客户端计算那样在网络间来回传送数据导致的性能损失,因为存储过程的所有计算全部在服务器端完成。如果计算设计多个数据表,大量的数据查询和更新,那么使用存储过程代替客户端计算是比较合适的做法。
例如
CREATE PROCEDURE CountTable(IN tableName varchar(1000), OUT sqlStr varchar(1000), INOUT v INT) BEGIN set @flag = v; set @sql = CONCAT('select count(*) into @res from'', tableName, 'where ACTIVE_FLAG=?'); PREPARE stmt FROM @sql; EXECUTE stmt using @flag; DEALLOCATE PREPARE stmt; set v = @res; set sqlStr = @sql; END
Object result = jdbcTemplate.execute("call CountTable(?,?,?)", new CallableStatementCallback() { @Override public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { cs.setString(1, "tableName"); cs.setInt(3, 1); cs.registerOutParameter(2, Types.INTEGER); cs.registerOutParameter(3, Types.INTEGER); cs.execute(); Map resultMap = new HashMap(); resultMap.put("SQL", cs.getString(2)); resultMap.put("COUNT", cs.getInt(3)); return resultMap; } });
递增主键生成策略的抽象
1,基于独立主键表的DataFieldMaxValueIncrementer。它依赖于为每个数据表单独定义的主键表,主键表中定义的主键可以根据需要获取并递增,并且可以设置每次获取的CacheSize以减少访问数据库资源的频度。
2,基于数据库Sequence的DataFiledMaxValueIncrementer。数据库本身支持基于Sequence的主键生成。
String中的LOB类型处理
Spring提出一套基于LOB数据处理类,用于屏蔽各数据库驱动在处理LOB数据方式上的差异性。
public interface LobHandler { byte[] getBlobAsBytes(ResultSet rs, String columnName) throws SQLException; byte[] getBlobAsBytes(ResultSet rs, int columnIndex) throws SQLException; InputStream getBlobAsBinaryStream(ResultSet rs, String columnName) throws SQLException; InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) throws SQLException; String getBlobAsString(ResultSet rs, String columnName) throws SQLException; String getBlobAsString(ResultSet rs, int columnIndex) throws SQLException; InputStream getBlobAsAsciiStream(ResultSet rs, String columnName) throws SQLException; InputStream getBlobAsAsciiStream(ResultSet rs, int columnIndex) throws SQLException; Reader getBlobAsCharStream(ResultSet rs, String columnName) throws SQLException; Reader getBlobAsCharStream(ResultSet rs, int columnIndex) throws SQLException; LobCreator getLobCreator(); }
LobCreator的职责主要在于LOB数据的创建,它让我们能够以统一的方式创建LOB数据。我们将在插入或者更新LOB数据的时候使用它。
DefaultLobHandlerDemo.java
package com.ivy.SpringJdbc; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.sql.PreparedStatement; import java.sql.SQLException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobCreator; import org.springframework.jdbc.support.lob.LobHandler; public class DefaultLobHandlerDemo { @Autowired private JdbcTemplate jdbcTemplate; public void saveImage() throws FileNotFoundException { final File imageFile = new File("snow_image.jpg"); final InputStream ins = new FileInputStream(imageFile); LobHandler lobHandler = new DefaultLobHandler(); jdbcTemplate.execute("insert into images(id, filename, entity, description) values(?,?,?,?)", new AbstractLobCreatingPreparedStatementCallback(lobHandler) { @Override protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException { ps.setInt(1, 2); ps.setString(2, "snow_image.jpg"); lobCreator.setBlobAsBinaryStream(ps, 3, ins, (int)imageFile.length()); ps.setString(4, "nothing to say"); } }); IOUtils.closeQuietly(ins); } }
NamedParameterJdbcTemplate
example 1 :
DataSource dataSource = ...; NamedParameterJdbcTemplate npJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); SqlParameterSource parameterSource = new MapSqlParameterSource("filename","snow_image.jpg"); int count = npJdbcTemplate.queryForInt("select count(*) from images where filaname=:filename", parameterSource);
example 2 :
DataSource dataSource = ...; NamedParameterJdbcTemplate npJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); Image image = new Image(); image.setFilename("snow_image.jpg"); SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(image); int count = npJdbcTemplate.queryForInt("select count(*) from images where filaname=:filename", parameterSource);
SimpleJdbcTemplate
SimpleJdbcTemplate集JdbcTemplate和NamedParameterJdbcTemplate的功能于一身。
example:
package com.ivy.SpringJdbc; import java.awt.Image; import java.sql.ResultSet; import java.sql.SQLException; import javax.activation.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobHandler; public class SimpleJdbcTemplateDemo { @Autowired private DataSource dataSource; public void test() { SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); final LobHandler lobHandler = new DefaultLobHandler(); String SQL = "select count(*) from images where filename = ? and description = ?"; ParameterizedRowMapper<Image> rowMapper = new ParameterizedRowMapper<T>() { @Override public T mapRow(ResultSet rs, int rowNum) throws SQLException { Image image = new Image(); image.setId(rs.getInt(1)); image.setFilename(rs.getString(2)); image.setEntity(lobHandler.getBlobAsBytes(rs, 3)); image.setDescription(rs.getString(4)); return image; } };
Image image = simpleJdbcTemplate.queryForObject(SQL, rowMapper, "snow_image.jpg", "nothing to say");
} }
DataSource访问方式
1, 本地DataSource访问
在容器的配置文件中进行简单的配置即可。
2,远程DataSource访问
对于运行于应用服务器的程序或者分布式应用来说,通过JNDI访问DataSource是最常见方式
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jdniName"> <value>java:env/myDataSource</value> </property> </bean>