前言
最近工作中經常使用Spring JDBC操作資料庫,也斷斷續續的看了一些源碼,便有了寫一些總結的想法,希望在能幫助别人的同時,也加深一下自己對Spring JDBC的了解。
Spring JDBC 簡介
Spring JDBC 是spring 官方提供的一個持久層架構,對jdbc進行了抽象和封裝,消除了重複備援的jdbc重複性的代碼,使操作資料庫變的更簡單。
但Spring JDBC本身并不是一個orm架構,與hibernate相比,它需要自己操作sql,手動映射字段關系,在保持靈活性的同時,勢必造成了開發效率的降低。如果需要使用完整的orm架構操作資料庫,可以使用hibernate或者spring Data Jpa。
Spring JDBC不同版本的api會稍有變動,但總體的變化不大,以下測試代碼均使用4.3.11.RELEASE。
模闆類
Spring JDBC 提供了模闆類對資料庫簡化對資料庫的操作,其中JdbcTemplate是最常用的,如果需要使用命名參數可以使用NamedParameterJdbcTemplate,SimpleJdbcTemplate在3.1版本已經标記過時,在我使用的4.3版本中,已經被删除。
JdbcTemplate 入門示例
JdbcTemplate使用很簡單,注入一個資料源就可以使用了
public class A001SpringJdbcJdbcTemplateTest {
private JdbcTemplate jdbcTemplate;
@Before
public void init() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/blogsrc?useUnicode=true&characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("zhao");
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
public void queryTest() {
String sql = "select * from user";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
System.out.println(users);
}
}
對于參數指派,可以采用占位符的方式
@Test
public void queryByParameterTest() {
String sql = "select * from user where id =?";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql, 1L);
List<Map<String, Object>> users1 = jdbcTemplate.queryForList(sql, new Object[] {1L});
}
mapper映射
Spring JDBC 通過mapper接口把resultSet對象中的資料映射為java對象,例如上述例子中傳回
List<Map<String, Object>>
,其實使用的是
ColumnMapRowMapper
的mapper實作。我們自己可以通過實作
RowMapper
接口的方式自定義從resultSet到java對象的映射關系。
先建立一個table
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`password` varchar(255) NOT NULL,
`user_name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
User實體類
public class User implements Serializable {
private Long id;
private String userName;
private String password;
// 省略 getter setter
}
自定義mapper映射
@Test
public void simpleMapperTest() {
String sql = "select * from user";
List<User> users = jdbcTemplate.query(sql, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getObject("id") == null ? null : rs.getLong("id"));
user.setUserName(rs.getString("user_name"));
user.setPassword(rs.getString("password"));
return user;
}
});
}
調用存儲過程
對于資料庫資料的更新操作,可以直接調用update接口,如果是存儲過程,可以通過execute完成調用。
建立一個簡單的存儲過程test
DROP PROCEDURE IF EXISTS test;
CREATE PROCEDURE test (IN userId BIGINT ( 20 ),OUT userName VARCHAR ( 200 ) )
BEGIN
SET userName = ( SELECT user_name FROM USER WHERE id = userId );
END;
@Test
public void proTest() {
String sql = "insert into user (user_name,password) VALUES (?, ?)";
jdbcTemplate.update(sql, "趙孤鴻", "123456"); // 插入資料
String userName = jdbcTemplate.execute(new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException {
String proc = "{call test(?,?)}";
CallableStatement cs = con.prepareCall(proc);
cs.setLong(1, 1L);// 設定輸入參數的值 索引從1開始
cs.registerOutParameter(2, Types.VARCHAR);// 設定輸出參數的類型
return cs;
}
}, new CallableStatementCallback<String>() {
public String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.execute();
return cs.getString(2);// 傳回輸出參數
}
});
System.out.println(userName);
}
NamedParameterJdbcTemplate
NamedParameterJdbcTemplate的使用基本上和JdbcTemplate類似,隻不過參數的指派方式由占位符變成了命名參數,命名參數優勢在于,如果一個相同的參數出現了多次,隻需要進行一次指派即可。
建立NamedParameterJdbcTemplate對象的兩種方式
// 方式1
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
// 方式2
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
@Test
public void namedParameterJdbcTemplateTest() {
String sql = "select * from user where id =:id";
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("id", 1L);
List<Map<String, Object>> users = namedParameterJdbcTemplate.queryForList(sql, parameters);
System.out.println(users);
}
batchUpdate
對于大資料量的資料更新,可以采用batchUpdate接口
@Test
public void batchUpdateTest() {
String sql = "insert into user (user_name,password) VALUES (?, ?)";
List<User> users = Lists.newArrayList();
for (int i = 0; i <= 10; i++) {
User user = new User();
user.setUserName("xiaoming");
user.setPassword("123456");
users.add(user);
}
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
int count = 0;
ps.setString(++count, user.getUserName());// 索引從1開始
ps.setString(++count, user.getPassword());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
與spring整合
隻需要注入資料源即可
<!-- 省略dataSource相關配置 -->
<!-- 配置 Spirng 的 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置 NamedParameterJdbcTemplate -->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
如果是springboot項目,直接使用即可
@Autowired
private JdbcTemplate jdbcTemplate;
注意事項
關于queryForObject,如果沒有查詢到,或者查詢到多個,都會抛異常,看一下源碼
// 未查詢到,或者查詢到多個,都會抛異常
public static <T> T requiredSingleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
int size = (results != null ? results.size() : 0);
if (size == 0) {
throw new EmptyResultDataAccessException(1);
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, size);
}
return results.iterator().next();
}
queryForMap最終走的也是queryForObject方法,是以,使用時也要注意
在自定義mapper擷取ResultSet的值時,擷取基本類型資料會有預設值,解決辦法如下
Long id = rs.getLong("id");//如果id為null,會預設0
Long id1 = rs.getObject("id") == null ? null : rs.getLong("id");//先判斷是否為null
完整的 源碼 https://github.com/zhaoguhong/blogsrc
作者:zhaoguhong(趙孤鴻)
出處:http://www.cnblogs.com/zhaoguhong/
個人部落格:http://www.zhaoguhong.com
本文版權歸作者和部落格園共有,轉載請注明出處