最近工做中常常使用Spring JDBC操做数据库,也断断续续的看了一些源码,便有了写一些总结的想法,但愿在能帮助别人的同时,也加深一下本身对Spring JDBC的理解。java
Spring JDBC 是spring 官方提供的一个持久层框架,对jdbc进行了抽象和封装,消除了重复冗余的jdbc重复性的代码,使操做数据库变的更简单。mysql
但Spring JDBC自己并非一个orm框架,与hibernate相比,它须要本身操做sql,手动映射字段关系,在保持灵活性的同时,势必形成了开发效率的下降。若是须要使用完整的orm框架操做数据库,可使用hibernate或者spring Data Jpa。git
Spring JDBC不一样版本的api会稍有变更,但整体的变化不大,如下测试代码均使用4.3.11.RELEASE。github
Spring JDBC 提供了模板类对数据库简化对数据库的操做,其中JdbcTemplate是最经常使用的,若是须要使用命名参数可使用NamedParameterJdbcTemplate,SimpleJdbcTemplate在3.1版本已经标记过期,在我使用的4.3版本中,已经被删除。spring
JdbcTemplate使用很简单,注入一个数据源就可使用了sql
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}); }
Spring JDBC 经过mapper接口把resultSet对象中的数据映射为java对象,例如上述例子中返回 List<Map<String, Object>>
,其实使用的是ColumnMapRowMapper
的mapper实现。咱们本身能够经过实现RowMapper
接口的方式自定义从resultSet到java对象的映射关系。api
先建立一个tablespringboot
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实体类app
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的使用基本上和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接口
@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(); } }); }
只须要注入数据源便可
<!-- 省略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