前面一篇介绍如何使用JdbcTemplate实现插入数据,接下来进入实际业务中,最多见的查询篇。因为查询的姿式实在太多,对内容进行了拆分,本篇主要介绍几个基本的使用姿式java
环境依然借助前面一篇的配置,连接如: 190407-SpringBoot高级篇JdbcTemplate之数据插入使用姿式详解mysql
或者直接查看项目源码: github.com/liuyueyi/sp…git
咱们查询所用数据,正是前面一篇插入的结果,以下图github
queryForMap,通常用于查询单条数据,而后将db中查询的字段,填充到map中,key为列名,value为值spring
最基本的使用姿式,就是直接写完整的sql,执行sql
String sql = "select * from money where id=1";
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
System.out.println("QueryForMap by direct sql ans: " + map);
复制代码
这种用法的好处是简单,直观;可是有个很是致命的缺点,若是你提供了一个接口为数据库
public Map<String, Object> query(String condition) {
String sql = "select * from money where name=" + condition;
return jdbcTemplate.queryForMap(sql);
}
复制代码
直接看上面代码,会发现问题么???数组
有经验的小伙伴,可能一会儿就发现了sql注入的问题,若是传入的参数是 '一灰灰blog' or 1=1 order by id desc limit 1
, 这样输出和咱们预期的一致么?bash
正是由于直接拼sql,可能到只sql注入的问题,因此更推荐的写法是经过占位符 + 传参的方式mybatis
// 使用占位符替换方式查询
sql = "select * from money where id=?";
map = jdbcTemplate.queryForMap(sql, new Object[]{1});
System.out.println("QueryForMap by ? ans: " + map);
// 指定传参类型, 经过传参来填充sql中的占位
sql = "select * from money where id =?";
map = jdbcTemplate.queryForMap(sql, 1);
System.out.println("QueryForMap by ? ans: " + map);
复制代码
从上面的例子中也能够看出,占位符的使用很简单,用问好(?
)来代替具体的取值,而后传参
传参有两种姿式,一个是传入Object[]
数组;另一个是借助java的不定长参数方式进行传参;两个的占位替换都是根据顺序来的,也就是若是你有一个值想替换多个占位符,那就得血屡次
如:
sql = "select * from money where (name=? and id=?) or (name=? and id=?)";
map = jdbcTemplate.queryForMap(sql, "一灰灰blog", 1, "一灰灰blog", 2);
复制代码
使用queryForMap有个不得不注意的事项,就是若是查不到数据时,会抛一个异常出来,因此须要针对这种场景进行额外处理
// 查不到数据的状况
try {
sql = "select * from money where id =?";
map = jdbcTemplate.queryForMap(sql, 100);
System.out.println("QueryForMap by ? ans: " + map);
} catch (EmptyResultDataAccessException e) {
e.printStackTrace();
}
复制代码
前面针对的主要是单个查询,若是有多个查询的场景,可能就须要用到queryForList
了,它的使用姿式和上面其实差异不大;
最基本的使用姿式固然是直接写sql执行了
System.out.println("============ query for List! ==============");
String sql =
"select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 3;";
// 默认返回 List<Map<String, Object>> 类型数据,若是一条数据都没有,则返回一个空的集合
List<Map<String, Object>> res = jdbcTemplate.queryForList(sql);
System.out.println("basicQueryForList: " + res);
复制代码
注意返回的结果是List<Map<String, Object>>
, 若是一条都没有命中,会返回一个空集合, 和 QueryForMap
抛异常是不同的
直接使用sql的查询方式,依然和前面同样,可能有注入问题,固然优先推荐的使用经过占位来传参方式
String sql2 = "select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, " +
"unix_timestamp(update_at) as updated from money where id=? or name=?;";
res = jdbcTemplate.queryForList(sql2, 2, "一灰灰2");
System.out.println("queryForList by template: " + res);
复制代码
若是是简单查询,直接用上面两个也就够了,可是对于使用过mybatis,Hibernate的同窗来讲,每次返回Map<String, Object>
,就真的有点蛋疼了, 对于mysql这种数据库,表的结构基本不变,彻底能够和POJO进行关联,对于业务开发者而言,固然是操做具体的POJO比Map要简单直观多了
下面将介绍下,如何使用 queryForObject
来达到咱们的目标
首先介绍下利用 RowMapper
来演示下,最原始的使用姿式
第一步是定义对应的POJO类
@Data
public static class MoneyPO implements Serializable {
private static final long serialVersionUID = -5423883314375017670L;
private Integer id;
private String name;
private Integer money;
private boolean isDeleted;
private Long created;
private Long updated;
}
复制代码
而后就是使用姿式
// sql + 指定返回类型方式访问
// 使用这种sql的有点就是方便使用反射方式,实现PO的赋值
String sql =
"select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 1;";
// 须要注意,下标以1开始
MoneyPO moneyPO = jdbcTemplate.queryForObject(sql, new RowMapper<MoneyPO>() {
@Override
public MoneyPO mapRow(ResultSet rs, int rowNum) throws SQLException {
MoneyPO po = new MoneyPO();
po.setId(rs.getInt(1));
po.setName(rs.getString(2));
po.setMoney(rs.getInt(3));
po.setDeleted(rs.getBoolean(4));
po.setCreated(rs.getLong(5));
po.setUpdated(rs.getLong(6));
return po;
}
});
System.out.println("queryFroObject by RowMapper: " + moneyPO);
复制代码
从使用姿式上看,RowMapper
就是一个sql执行以后的回调,实现结果封装,这里须要注意的就是 ResultSet
封装了完整的返回结果,能够经过下标方式指定,下标是从1开始,而不是咱们常见的0,须要额外注意
这个下标从1开始,感受有点蛋疼,总容易记错,因此更推荐的方法是直接经过列名获取数据
// 直接使用columnName来获取对应的值,这里就能够考虑使用反射方式来赋值,减小getter/setter
moneyPO = jdbcTemplate.queryForObject(sql, new RowMapper<MoneyPO>() {
@Override
public MoneyPO mapRow(ResultSet rs, int rowNum) throws SQLException {
MoneyPO po = new MoneyPO();
po.setId(rs.getInt("id"));
po.setName(rs.getString("name"));
po.setMoney(rs.getInt("money"));
po.setDeleted(rs.getBoolean("isDeleted"));
po.setCreated(rs.getLong("created"));
po.setUpdated(rs.getLong("updated"));
return po;
}
});
System.out.println("queryFroObject by RowMapper: " + moneyPO);
复制代码
当sql返回的列名和POJO的属性名能够彻底匹配上的话,上面的这种写法就显得很是冗余和麻烦了,我须要更优雅简洁的使用姿式,最好就是直接传入POJO
类型,自动实现转换
若是但愿获得这个效果,你须要的就是下面这个了: BeanPropertyRowMapper
// 更简单的方式,直接经过BeanPropertyRowMapper来实现属性的赋值,前提是sql返回的列名能正确匹配
moneyPO = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(MoneyPO.class));
System.out.println("queryForObject by BeanPropertyRowMapper: " + moneyPO);
复制代码
查看JdbcTemplate提供的接口时,能够看到下面这个接口
@Override
public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException {
return queryForObject(sql, args, getSingleColumnRowMapper(requiredType));
}
复制代码
天然而然的想到,直接传入POJO的类型进去,是否是就能够获得咱们预期的结果了?
String sql =
"select id, `name`, money, is_deleted as isDeleted, unix_timestamp(create_at) as created, unix_timestamp(update_at) as updated from money limit 1;";
try {
MoneyPO po = jdbcTemplate.queryForObject(sql, MoneyPO.class);
System.out.println("queryForObject by requireType return: " + po);
} catch (Exception e) {
e.printStackTrace();
}
复制代码
执行上面的代码,抛出异常
从上面的源码也能够看到,上面的使用姿式,适用于sql只返回一列数据的场景,即下面的case
// 下面开始测试下 org.springframework.jdbc.core.JdbcTemplate.queryForObject(java.lang.String, java.lang.Class<T>, java.lang.Object...)
// 根据测试,这个类型,只能是基本类型
String sql2 = "select id from money where id=?";
Integer res = jdbcTemplate.queryForObject(sql2, Integer.class, 1);
System.out.println("queryForObject by requireId return: " + res);
复制代码
上面全部代码能够查看: github.com/liuyueyi/sp…
简单的继承调用下上面的全部方法
@SpringBootApplication
public class Application {
private QueryService queryService;
public Application(QueryService queryService) {
this.queryService = queryService;
queryTest();
}
public void queryTest() {
queryService.queryForMap();
queryService.queryForObject();
queryService.queryForList();
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
复制代码
输出结果以下
本篇博文主要介绍了JdbcTemplate查询的简单使用姿式,主要是queryForMap
, queryForList
, queryForObject
三种方法的调用
单条记录查询
queryForMap
: 返回一条记录,返回的结果塞入Map<String, Object>
, key为固定的String对应查询的列名;value为实际值queryForObject
:一样返回一条数据,与上面的区别在于能够借助RowMapper
来实现返回结果转换为对应的POJO须要注意的是,上面的查询,必须有一条记录返回,若是查不到,则抛异常
批量查询
queryForList
:一次查询>=0条数据,返回类型为 List<Map<String, Object>>
有两种sql传参方式
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激