jdbcTemplate.queryForObject方法入參不支持以Map的形式傳遞參數,需要按照sql語句中參數的順序組織入參的List。jdbcTemplate.queryForObject方法直接不支持的in查詢。只支持Integer.class String.class 這種單數據類型的入參。
- 例如:
List<Long> idList = ...;
String name = ...;
StringBuilder idListSb = new StringBuilder();
for(Long id : idList){
idListSb.append(id + ",");
}
String sql = "select count(*) from user where id in (?) and name = ?";
List<Object> objects = new ArrayList<>();
objects.add(idListSb.toString().RTrim(','));
object.add (name);
jdbcTemplate.queryForObject(sb.toString(),objects,Long.class);
而
NamedParameterJdbcTemplate時,其本來就支持in查詢和Map類型的入參。
List<Long> idList = ...;
String name = ...;
String sql = "select * from user where id in (:ids) and name = :name";
Map<String, Object> param = new HashMap<>();
param.put("ids", idList);
param.put("name", name);
namedParameterJdbcTemplate.queryForObject(sb.toString(),param,Long.class);
一、JdbcTemplate
Spring對資料庫的操作在jdbc上面做了深層次的封裝,提供了一個 Jdbc Template 類,我們可以直接使用。只需要把DataSource註冊到JdbcTemplate之中,其全限定命名為
org.springframework.jdbc.core.JdbcTemplate。要使用JdbcTemlate一般還需要事務和異常的控制。
JdbcTemplate的幾類方法
- execute方法:可以用於執行任何SQL語句,一般用於執行DDL語句;
- update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
- query方法及queryForXXX方法:用於執行查詢相關語句;
- call方法:用於執行存儲過程、函數相關語句。
JdbcTemplate的常用方法
//update操作單個增刪改
int update(String sql,Object[] args)
int update(String sql,Objcet... args)
//batchUpdate批量增刪改
int[] batchUpdate(String[] sql)
int[] batchUpdate(String sql,List<Object[]>)
//單個簡單查詢
T queryForObjcet(String sql,Class<T> type)
T queryForObjcet(String sql,Object[] args,Class<T> type)
T queryForObjcet(String sql,Class<T> type,Object... arg)
//獲取多個
List<T> queryForList(String sql,Class<T> type)
List<T> queryForList(String sql,Object[] args,Class<T> type)
List<T> queryForList(String sql,Class<T> type,Object... arg)
查詢複雜對象(封裝為Map)
//獲取單個
Map queryForMap(String sql)
Map queryForMap(String sql,Objcet[] args)
Map queryForMap(String sql,Object... arg)
//獲取多個
List<Map<String,Object>> queryForList(String sql)
List<Map<String,Object>> queryForList(String sql,Obgject[] args)
List<Map<String,Object>> queryForList(String sql,Obgject... arg)
查詢複雜對象(封裝為實體對象)
Spring JdbcTemplate是通過實現
org.springframework.jdbc.core.RowMapper這個介面來完成對entity對象映射。
//獲取單個
T queryForObject(String sql,RowMapper<T> mapper)
T queryForObject(String sql,object[] args,RowMapper<T> mapper)
T queryForObject(String sql,RowMapper<T> mapper,Object... arg)
//獲取多個
List<T> query(String sql,RowMapper<T> mapper)
List<T> query(String sql,Object[] args,RowMapper<T> mapper)
List<T> query(String sql,RowMapper<T> mapper,Object... arg)
Spring JDBC中目前有兩個主要的RowMapper實現,使用它們應該能解決大部分的場景了:SingleColumnRowMapper和BeanPropertyRowMapper。
SingleColumnRowMapper:返回單列數據
BeanPropertyRowMapper:當查詢資料庫返回的是多列數據,且需要將這些多列數據映射到某個具體的實體類上。
//示例:
String sql = "select name from test_student where id = ?";
jdbcTemplate.queryForObject(sql, new Object[]{id}, new SingleColumnRowMapper<>(String.class));
String sql = "select name, gender from test_student where name = ?";
jdbcTemplate.queryForObject(sql, new Object[]{name},new BeanPropertyRowMapper<>(Student.class));
- 定義自己的RowMapper
如果你SQL查詢出來的數據列名就是和實體類的屬性名不一樣,或者想按照自己的規則來裝配實體類,那麼就可以定義並使用自己的Row Mapper。
//自定義
public class StudentRowMapper implements RowMapper<Student> {
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
Student student = new Student();
student.setName(rs.getString("name"));
student.setGender(rs.getString("gender"));
student.setEmail(rs.getString("email"));
return student;
}
}
//使用
String sql = "select name, gender, email from test_student where name = ?";
jdbcTemplate.queryForObject(sql, new Object[]{name}, new StudentRowMapper());
批量修改
public int[] batchInsert(List<Map<String, Object>> dataList) {
String sql = "INSERT INTO test_info(name, create_time, age) VALUES(?, NOW(), ?)";
List<Object[]> paramArray = new ArrayList<>();
for (Map<String, Object> dataInfo : dataList) {
Object[] obj = new Object[2];
obj[0] = dataInfo.get("name");
obj[1] = dataInfo.get("age");
paramArray.add(obj);
}
if (CollectionUtils.isNotEmpty(paramArray)) {
return jdbcTemplate.batchUpdate(sql, paramArray);
}
return new int[0];
}
JdbcTemplate支持的回調類
預編譯語句及存儲過程創建回調:用於根據JdbcTemplate提供的連接創建相應的語句
PreparedStatementCreator:通過回調獲取JdbcTemplate提供的Connection,由用戶使用該Conncetion創建相關的PreparedStatement;
CallableStatementCreator:通過回調獲取JdbcTemplate提供的Connection,由用戶使用該Conncetion創建相關的CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) // 關聯Spring與Junit
@ContextConfiguration(locations = { "classpath:applicationContext.xml" }) // 載入配置spring配置文件
public class AppTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testPpreparedStatement1() {
int count = jdbcTemplate.execute(new PreparedStatementCreator() {
public java.sql.PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
return conn.prepareStatement("select count(*) from user");
}
}, new PreparedStatementCallback<Integer>() {
public Integer doInPreparedStatement(java.sql.PreparedStatement pstmt)
throws SQLException, DataAccessException {
pstmt.execute();
ResultSet rs = pstmt.getResultSet();
rs.next();
return rs.getInt(1);
}
});
System.out.println(count);
}
}
首先使用PreparedStatementCreator創建一個預編譯語句,其次由JdbcTemplate通過PreparedStatementCallback回調傳回,由用戶決定如何執行該PreparedStatement。此處我們使用的是execute方法。
預編譯語句設值回調:用於給預編譯語句相應參數設值
PreparedStatementSetter:通過回調獲取JdbcTemplate提供的PreparedStatement,由用戶來對相應的預編譯語句相應參數設值;
BatchPreparedStatementSetter:;類似於PreparedStatementSetter,但用於批處理,需要指定批處理大小;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) // 關聯Spring與Junit
@ContextConfiguration(locations = { "classpath:applicationContext.xml" }) // 載入配置spring配置文件
public class AppTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testPreparedStatement2() {
String insertSql = "insert into user(user_name) values (?)";
int count = jdbcTemplate.update(insertSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement pstmt) throws SQLException {
pstmt.setObject(1, "mmNN");
}
});
Assert.assertEquals(1, count);
String deleteSql = "delete from user where user_name=?";
count = jdbcTemplate.update(deleteSql, new Object[] { "mmNN" });
Assert.assertEquals(1, count);
}
}
通過JdbcTemplate的int update(String sql, PreparedStatementSetter pss)執行預編譯sql,其中sql參數為「insert into user(user_name) values (?) 」,該sql有一個佔位符需要在執行前設值,PreparedStatementSetter實現就是為了設值,使用setValues(PreparedStatement pstmt)回調方法設值相應的佔位符位置的值。JdbcTemplate也提供一種更簡單的方式「update(String sql, Object… args)」來實現設值,所以只要當使用該種方式不滿足需求時才應使用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)來處理結果集,用戶必須處理整個結果集;
@Test
public void testResultSet1() {
jdbcTemplate.update("insert into user(user_name) values('name7')");
String listSql = "select * from user where user_name=?";
List result = jdbcTemplate.query(listSql,new Object[]{"name7"}, new RowMapper<Map>() {
public Map mapRow(ResultSet rs, int rowNum) throws SQLException {
Map row = new HashMap();
row.put(rs.getInt("user_id"), rs.getString("user_name"));
return row;
}
});
Assert.assertEquals(1, result.size());//查詢結果數量為1才向下執行
jdbcTemplate.update("delete from user where user_name='name7'");
}
RowMapper介面提供mapRow(ResultSet rs, int rowNum)方法將結果集的每一行轉換為一個Map,當然可以轉換為其他類。
@Test
public void testResultSet2() {
jdbcTemplate.update("insert into user(user_name) values('name5')");
String listSql = "select * from user";
final List result = new ArrayList();
jdbcTemplate.query(listSql, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Map row = new HashMap();
row.put(rs.getInt("user_id"), rs.getString("user_name"));
result.add(row);
}
});
Assert.assertEquals(1, result.size());
jdbcTemplate.update("delete from user where user_name='name5'");
}
RowCallbackHandler介面也提供方法processRow(ResultSet rs),能將結果集的行轉換為需要的形式。
@Test
public void testResultSet3() {
jdbcTemplate.update("insert into test(name) values('name5')");
String listSql = "select * from test";
List result = jdbcTemplate.query(listSql, new ResultSetExtractor<List>() {
public List extractData(ResultSet rs) throws SQLException, DataAccessException {
List result = new ArrayList();
while (rs.next()) {
Map row = new HashMap();
row.put(rs.getInt("id"), rs.getString("name"));
result.add(row);
}
return result;
}
});
Assert.assertEquals(0, result.size());
jdbcTemplate.update("delete from test where name='name5'");
}
ResultSetExtractor使用回調方法extractData(ResultSet rs)提供給用戶整個結果集,讓用戶決定如何處理該結果集。
當然JdbcTemplate提供更簡單的queryForXXX方法,來簡化開發:
//1.查詢一行數據並返回int型結果
jdbcTemplate.queryForInt("select count(*) from test");
//2. 查詢一行數據並將該行數據轉換為Map返回
jdbcTemplate.queryForMap("select * from test where name='name5'");
//3.查詢一行任何類型的數據,最後一個參數指定返回結果類型
jdbcTemplate.queryForObject("select count(*) from test", Integer.class);
//4.查詢一批數據,默認將每行數據轉換為Map
jdbcTemplate.queryForList("select * from test");
//5.只查詢一列數據列表,列類型是String類型,列名字是name
jdbcTemplate.queryForList("
select name from test where name=?", new Object[]{"name5"}, String.class);
//6.查詢一批數據,返回為SqlRowSet,類似於ResultSet,但不再綁定到連接上
SqlRowSet rs = jdbcTemplate.queryForRowSet("select * from test");
返回新增的自增ID
@Test
public void test(){
String sql = "insert into sp_user(name) values (?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
// 設置返回的主鍵欄位名
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, "harvey");
return ps;
}
}, keyHolder);
// 獲取到插入數據生成的ID
int num = keyHolder.getKey().intValue();
System.out.println(num);
}
二、NamedParameterJdbcTemplate
什麼是具名參數
在經典的 JDBC 用法中, SQL 參數是用佔位符 ? 表示,並且受到位置的限制。定位參數的問題在於, 一旦參數的順序發生變化, 就必須改變參數綁定。
在 Spring JDBC 框架中, 綁定 SQL 參數的另一種選擇是使用具名參數(named parameter)。
具名參數:SQL 按名稱(以冒號開頭)而不是按位置進行指定,具名參數更易於維護,,也提升了可讀性。具名參數由框架類在運行時用佔位符取代,
具名參數只在
NamedParameterJdbcTemplate 中得到支持。
NamedParameterJdbcTemplate類拓展了JdbcTemplate類,可以使用全部jdbcTemplate方法。
NamedParameterJdbcTemplate主要提供以下三類方法:execute方法、query及queryForXXX方法、update及batchUpdate方法。
NamedParameterJdbcTemplate可以使用DataSource或JdbcTemplate 對象作為構造器參數初始化。
常用API傳入參數類型介紹
NamedParameterJdbcTemplate類為命名參數設值有兩種方式:java.util.Map 、RowMapper 和 SqlParameterSource。
Map<String,?> paramMap
就是一個hash表,好處是可以根據參數名傳參,paramMap.put(“sqlparamName”,value)。
RowMapper rowMapper
這個介面為了實現sql查詢結果和對象間的轉換,可以自己實現,也可以使用系統實現,主要實現類有:
- SingleColumnRowMapper ,sql結果為一個單列的數據,如List<String> , List<Integer>,String,Integer等
- BeanPropertyRowMapper, sql結果匹配到對象 List< XxxVO> , XxxVO
SqlParameterSource
其作用和Map一樣,就是為sql中的條件參數賦值,默認實現有 :
- MapSqlParameterSource實現非常簡單,只是封裝了java.util.Map。
- BeanPropertySqlParameterSource封裝了一個JavaBean對象,通過JavaBean對象屬性來決定命名參數的值。
- EmptySqlParameterSource 一個空的SqlParameterSource ,常用來佔位使用。
//BeanPropertySqlParameterSource傳參方式
SqlParameterSource sps = new BeanPropertySqlParameterSource(javaBean);
//MapSqlParameterSource傳參方式
SqlParameterSource sps = new MapSqlParameterSource();
//保證參數名和key相同
sps.addValue("key",value);
API介紹
查詢
返回單行單列數據
public < T > T queryForObject(String sql, Map<String, ?> paramMap, Class<T> requiredType)
public < T > T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)
Integer count = template.queryForObject("select count(*) from student", new HashMap<>(), Integer.class);
String name = template.queryForObject( "select name from student where home_address limit 1 ", EmptySqlParameterSource.INSTANCE, String.class);
返回 (多行)單列 數據
public < T> List< T> queryForList(String sql, Map<String, ?> paramMap, Class< T > elementType)
public < T> List< T> queryForList(String sql, SqlParameterSource paramSource, Class< T> elementType)
List< String> namelist = template.queryForList("select name from student", new HashMap<>(), String.class);
返回單行數據
public < T> T queryForObject(String sql, Map< String, ?> paramMap, RowMapper< T>rowMapper)
public < T> T queryForObject(String sql, SqlParameterSource paramSource, RowMapper< T> rowMapper)
Student stu = template.queryForObject(
"select * from student limit 1", new HashMap<>(), new BeanPropertyRowMapper<Student>(Student.class));
//BeanPropertyRowMapper會把下劃線轉化為駝峰屬性
//結果對象可比實際返回欄位多或者少
注意:這兩個API也可以使用SingleColumnRowMapper返回單行單列數據
String name = template.queryForObject(
"select name from student limit 1", EmptySqlParameterSource.INSTANCE, new SingleColumnRowMapper<>(String.class));
返回Map形式的單行數據
public Map< String, Object> queryForMap(String sql, Map< String, ?> paramMap)
public Map< String, Object> queryForMap(String sql, SqlParameterSource paramSource)
Map< String, Object> studentMap = template.queryForMap("select * from student limit 1", new HashMap<>());
注意:queryForMap這個方法要求結果必須是有數據的,否則會報錯。
返回多行數據
public < T> List< T> query(String sql, Map< String, ?> paramMap, RowMapper< T> rowMapper)
public < T> List< T> query(String sql, SqlParameterSource paramSource, RowMapper< T> rowMapper)
public < T> List< T> query(String sql, RowMapper< T> rowMapper)
List< Student> studentList = template.query(
"select * from student",
new BeanPropertyRowMapper<>(Student.class)
);
同理,也可以使用SingleColumnRowMapper返回單行列表List< String>,List< Integer>等。
返回多行數據(Map)
public List< Map< String, Object>> queryForList(String sql, Map< String, ?> paramMap)
public List< Map< String, Object>> queryForList(String sql, SqlParameterSource paramSource)
List<Map<String, Object>> mapList = template.queryForList(
"select * from student", new HashMap<>());
插入/修改/刪除數據,使用updateXXX方法
使用Map作為參數
int update(String sql, Map<String, ?> paramMap)
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", UUID.randomUUID().toString());
paramMap.put("name", "小明");
paramMap.put("age", 33);
paramMap.put("homeAddress", "樂山");
paramMap.put("birthday", new Date());
template.update(
"insert into student(id,name,age,home_address,birthday) values (:id,:name,:age,:homeAddress,:birthday)",
paramMap
);
使用SqlParameterSource作為參數
int update(String sql, SqlParameterSource paramSource)
//使用 BeanPropertySqlParameterSource作為參數
StudentDTO dto=new StudentDTO();//這個DTO為傳入數據
dto.setId(UUID.randomUUID().toString());
dto.setName("小紅");
dto.setHomeAddress("成都");
//------------------------------
template.update("insert into student(id,name,home_address) values (:id,:name,:homeAddress)",
new BeanPropertySqlParameterSource(dto));
//使用MapSqlParameterSource 作為參數
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource()
.addValue("id", UUID.randomUUID().toString())
.addValue("name", "小王")
.addValue("homeAddress", "美國");
template.update("insert into student(id,name,home_address) values
(:id,:name,:homeAddress)",mapSqlParameterSource);
開發中盡量使用
NamedParameterJdbcTemplate代替JdbcTemplate,如果想使用JdbcTemplate,也可以通過NamedParameterJdbcTemplate#getJdbcOperations()獲取不建議使用查詢結構為Map的API
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/268029.html