JdbcTemplate 的基本使用
写在前面:
当DDL操作时,一般是用execute方法,这也是一种规范吧,这个也可以运行DML但是通常来说我DML操作是需要返回值的,一般就是返回影响的行数。然后这篇文章主要介绍增删改查,因为表格的创建一般已经完成了。
查询
- query 方法
从描述我们可以清楚的看到,普通的query 方法执行静态sql,使用的是Statement,如果需要使用PreparedStatement,可以看到还有一个方法query(String, Object[], ResultSetExtractor),只需要将数据参数传为null即可。
使用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> result = new ArrayList<>();
jdbcTemplate.query("SELECT ID,BAR FROM foo", (rs) -> {
Long id = rs.getLong(1);
String bar = rs.getString("BAR");
Foo foo = new Foo(id, bar);
result.add(foo);
}
);
return result;
}
使用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,queryForList 但是不太好用,因为默认映射是单列的如下:
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);
如果需要映射为对象还是需要用ResultSetExtractor或RowCallbackHandler,或者直接使用jdbcTemplate提供的下面这个方法,返回一个Map的集合,它使用的就是ColumnMapRowMapper:
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)使用NamedParameterJdbcTemplate
@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() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setObject(1, foos.get(i).getId());
ps.setObject(2, foos.get(i).getBar());
}
@Override
public int getBatchSize() {
return 3;
}
});
}