JdbcTemplate 的基本使用
写在前面:
当DDL操作时,一般是用execute方法,这也是一种规范吧,这个也可以运行DML但是通常来说我DML操作是需要返回值的,一般就是返回影响的行数。然后这篇文章主要介绍增删改查,因为表格的创建一般已经完成了。
查询
- query 方法
- 描述
从描述我们可以清楚的看到,普通的query 方法执行静态sql,使用的是Statement,如果需要使用PreparedStatement,可以看到还有一个方法query(String, Object[], ResultSetExtractor),只需要将数据参数传为null即可。 - code demo
使用ResultSetExtractor是一个函数式接口,每一行在内部进行处理,返回最终的处理结果,这一点与下面的行处理进行对比就可以很直观的看出来。
public List<Foo> demoQuery() {List<Foo> result = jdbcTemplate.query("SELECT ID,BAR FROM foo", (rs) -> {List<Foo> res = new ArrayList<>();while (rs.next()) {Long id = rs.getLong(1);String bar = rs.getString("BAR");Foo foo = new Foo(id, bar);res.add(foo);}return res;});return result;
}
使用行处理器RowCallbackHandler
public List<Foo> demoQuery() {List<Foo> res = new ArrayList<>();// 需要传一个行处理器,由于是函数式接口所以可以采用lamda表达式jdbcTemplate.query("SELECT ID,BAR FROM foo",(rs) -> {// 这里可以将对象进行映射,或者做其它的一些处理while (rs.next()) {Long id = rs.getLong(1);String bar = rs.getString("BAR");Foo foo = new Foo(id, bar);res.add(foo);}});return res;
}
使用PreparedStatement
正如上面描述所说,上面的两段代码使用的是Statement,下面演示一下PreparedStatement的用法:
public List<Foo> demoQuery1() {List<Foo> result = jdbcTemplate.query("SELECT ID,BAR FROM foo WHERE ID IN (?,?,?)" ,new Object[]{2,3,4}, (rs) -> {List<Foo> res = new ArrayList<>();while (rs.next()) {Long id = rs.getLong(1);String bar = rs.getString("BAR");Foo foo = new Foo(id, bar);res.add(foo);}return res;});return result;
}
List query(String sql, RowMapper rowMapper) 这个方法就是有一个RowMapper 将数据映射为我们需要的数据,比如映射为一个Map
// ColumnMapRowMapper Spring JDBC 提供的一个实现
// 这里的实现完全和public Map<String, Object> queryForMap(String sql); 这个方法一样,只是这个方法把转换的Mapper 写到了里面
List<Map<String, Object>> resultMap = jdbcTemplate.query("SELECT ID,BAR FROM foo", new ColumnMapRowMapper());
当然也可以自定义:
// 同样使用lamada 表达式
List<Foo> foos = jdbcTemplate.query("SELECT ID,BAR FROM foo", (rs, rowIndex) -> {long id = rs.getLong("ID");String bar = rs.getString("BAR");Foo foo = new Foo(id, bar);return foo;
});
这是第一部分,相当于是对于数据的映射需要我们自己来处理,jdbcTemplate提供了一写其它方法比如queryForObject,queryForLis见(2)
2) 不用自己处理映射关系的方法
T queryForObject(String sql, Class requiredType)
List queryForList(String sql, Object[] args, int[] argTypes, Class elementType)
可以看到里面都是用了SingleColumnRowMapper,所以这两个方法不太好用,如果列超过了1就会抛出异常,只适合返回一个字段的场景
List<String> foos = jdbcTemplate.queryForList("SELECT BAR FROM foo", String.class);
如果需要映射为对象还是需要用(1)中的方法映射,或者直接使用下面这个方法,返回一个Map的集合:
public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {return query(sql, getColumnMapRowMapper());
}
插入
1) 使用update 这一类方法进行插入或更新,文档描述直接写了的
普通的使用
public Object demoInsert() {String sql = "INSERT INTO FOO (ID,BAR) VALUES('100','HHH')";// 返回的是插入以后影响的行数int update = jdbcTemplate.update(sql);return update;
}
预编译SQL
public Object demoInsert() {String sql = "INSERT INTO FOO (ID,BAR) VALUES(?,?)";// 返回的是插入以后影响的行数int update = jdbcTemplate.update(sql, new Object[]{100, "hhh"});// 当然也是可以直接写参数的,这种直接写更常用int update = jdbcTemplate.update(sql, 200,"mmm");return update;}
也可以不通过参数列表的形式,而是直接传递一个PreparedStatementSetter
public Object demoInsert() {String sql = "INSERT INTO FOO (ID,BAR) VALUES(?,?)";Map<String, Object> map = new HashMap<String,Object>(){{put("ID", 100);put("BAR", "bar");}};// 返回的是插入以后影响的行数int update = jdbcTemplate.update(sql, (ps) -> {ps.setObject(1, map.get("ID"));ps.setObject(2, map.get("BAR"));});return update;
}
2)SimpleJdbcInsert 这是包里面提供了一个类也可以做插入的操作,某些场景下可能还是比较方便的
首先我们需要注入将这个bean注入到容器
@Bean
@Autowired
public SimpleJdbcInsert simpleJdbcInsert(JdbcTemplate jdbcTemplate) {return new SimpleJdbcInsert(jdbcTemplate)// 这里指定了bean 同时指定了主键自增策略.withTableName("FOO").usingGeneratedKeyColumns("ID");
}
插入后返回主键
public Number insertData() {Map<String, Object> row = new HashMap<>();row.put("bar", "c");// 插入后返回主键Number number = simpleJdbcInsert.executeAndReturnKey(row);return number;}
executeAndReturnKey 方法需要的参数是列名和值的map映射集合。
更新
更新用的方法依然是updata这一类函数,返回的同样是受影响的函数
public Object demoUpdate() {String sql = "UPDATE FOO SET BAR = ? WHERE ID = ?";// 返回的是插入以后影响的行数int update = jdbcTemplate.update(sql, "UP", 2);int update = jdbcTemplate.update(sql, new Object[]{2,"up"});return update;
}
和插入基本差不多的,也可以像下面这么写:
public Object demoUpdate() {String sql = "UPDATE FOO SET BAR = ? WHERE ID = ?";Map<String, Object> map = new HashMap<String,Object>(){{put("ID", 2);put("BAR", "TTT");}};// 返回的是插入以后影响的行数int update = jdbcTemplate.update(sql, (ps) -> {ps.setObject(2, map.get("ID"));ps.setObject(1, map.get("BAR"));});return update;
}
删除
删除操作
public void demoDelete() {String sql = "DELETE FROM foo WHERE ID= ?";int update = jdbcTemplate.update(sql, 2);System.out.println(update);
}
批量
对于批量操作来说,主要是批量更新和批量插入操作。
1)使用
@Bean
@Autowired
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {return new NamedParameterJdbcTemplate(dataSource);
}
插入注入NamedParameterJdbcTemplate 后可以批量插入
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
List<Foo> list = new ArrayList<>();list.add(Foo.builder().id(100L).bar("b-100").build());list.add(Foo.builder().id(101L).bar("b-101").build());namedParameterJdbcTemplate.batchUpdate("INSERT INTO FOO (ID, BAR) VALUES (:id, :bar)",SqlParameterSourceUtils.createBatch(list));
2 使用JdbcTemplate
public void batchInsert() {List<Foo> foos = new ArrayList<Foo>(){{add(new Foo(100L, "10t0"));add(new Foo(200L, "20t0"));add(new Foo(300L, "30t0"));}};jdbcTemplate.batchUpdate("INSERT INTO FOO (ID,BAR) VALUES (?,?)",new BatchPreparedStatementSetter() {@Overridepublic void setValues(PreparedStatement ps, int i) throws SQLException {ps.setObject(1, foos.get(i).getId());ps.setObject(2, foos.get(i).getBar());}@Overridepublic int getBatchSize() {return 3;}});
}