Spring中的JdbcTemplate的使用
在最近的一个工作中,为了简单方便我就是用了Spring自带的JdbcTemplate来访问数据库,我以为之前自己很熟练的掌握,后来才发现我太天真了,踩了很多坑。
基本方法
JdbcTemplate自带很多方法可以执行SQL语句,以下我主要列举,比较常用的方法
//执行SQL,返回一个对象
@Override
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args)throws DataAccessException {List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));return DataAccessUtils.requiredSingleResult(results);
}//同上,不过多要传入返回值的对象的Class
@Override
public <T> T queryForObject(String sql, Class<T> requiredType)throws DataAccessException {return queryForObject(sql, getSingleColumnRowMapper(requiredType));
}//执行SQL,返回一个Map对象
@Override
public Map<String, Object> queryForMap(String sql, Object... args)throws DataAccessException {return queryForObject(sql, args, getColumnMapRowMapper());
}//执行SQL,返回一个List对象
@Override
public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {return query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper));
}//执行SQL,返回一个List对象
@Override
@Override
public <T> List<T> queryForList(String sql, Class<T> elementType, Object... args)throws DataAccessException {return query(sql, args, getSingleColumnRowMapper(elementType));
}//执行一条SQL,主要用于更新、新增数据
@Override
public int update(String sql, Object... args) throws DataAccessException {return update(sql, newArgPreparedStatementSetter(args));
}//执行SQL,返回一个List对象,List里是Map对象
@Override
public List<Map<String, Object>> queryForList(String sql, Object... args) throws DataAccessException {return query(sql, args, getColumnMapRowMapper());
}
注意点
至少返回一个对象
@Override
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args)throws DataAccessException {List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));return DataAccessUtils.requiredSingleResult(results);
}public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {Assert.notNull(rowMapper, "RowMapper is required");this.rowMapper = rowMapper;this.rowsExpected = rowsExpected;
}
以上代码就是可以知道,必须返回一个对象,不能返回null,如果从数据库查找发现没有一条信息吻合就会报错,报以下的错误
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 0
所以如果不确定是否有信息,就使用query、queryForList来避免错误。返回单对象一般会有这样的限制,如果自己不确定可以使用该方法的时候看一下源码,设置了1就代表必须要有一个值。
返回指定对象
JdbcTemplate里面这样的方法,就是可以传入了一个对象的Class值,就可以返回该对象的值,但是需要注意,它只支持基础类型
//例如,它支持以下的写法
public Integer getCourseCount(String sql){return (Integer) jdbcTemplate.queryForObject(sql,java.lang.Integer.class);
}
通过源代码发现Class<T> requiredType这个参数支持以下类型,源代码就是从primitiveWrapperTypeMap查找是否是一下类型:
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
如果需要返回自定义对象就需要另外的方法:
如果返回List<T>,就如以下的案例
public List<Course> getCourseList(String sql){return jdbcTemplate.query(sql,new RowMapper<Course>(){@Overridepublic Course mapRow(ResultSet rs, int rowNum) throws SQLException {Integer id=rs.getInt("id");String coursename=rs.getString("coursename");//把数据封装到对象里Course course=new Course();course.setId(id);course.setCoursename(coursename.trim());return course;}});
}
或者使用List<Map<String,Object>>
@Override
public List<MyScoreDto> listMyScore(Integer studentId) {String sql = "select g.score,c.className from grade g"+ " left join teacher t on t.id=g.teacherId"+ " left join student s on s.id=g.studentId"+ " left join classname c on c.id=t.classNameId"+ " where s.id="+studentId;List<Map<String,Object>> list = jdbcTemplate.queryForList(sql);if(list!=null && list.size()>0){List<MyScoreDto> ls = new ArrayList<MyScoreDto>();for(int i=0;i<list.size();i++){MyScoreDto c = new MyScoreDto();try {BeanUtils.populate(c, list.get(i));} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}ls.add(c);}return ls;}return null;
}
使用了org.apache.commons.beanutils.BeanUtils把Map对象转换为自定义对象。
后来为了方便起见我还自己写了一个RowMapper,来简化操作
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;import org.springframework.jdbc.core.RowMapper;import com.lsb.exam.utils.StringUtils;public class MyRowMapper<T> implements RowMapper<T> {Class<T> cls;public MyRowMapper(Class<T> cls) {this.cls = cls;}@Overridepublic T mapRow(ResultSet rs, int rowNum) {try {Field[] fields = cls.getDeclaredFields();T obj = cls.newInstance();// 获取所有的属性for (Field field : fields) {field.setAccessible(true);if (field.getGenericType().toString().equals("class java.lang.Integer")) {Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.Integer.class);m.invoke(obj, rs.getInt(field.getName()));}if (field.getGenericType().toString().equals("class java.lang.String")) {Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.String.class);m.invoke(obj, rs.getString(field.getName()));}if (field.getGenericType().toString().equals("class java.util.Date")) {Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.util.Date.class);m.invoke(obj, rs.getDate(field.getName()));}}return obj;} catch (Exception e) {e.printStackTrace();return null;}}
}
上面有一个错误,就是如果对象继承了父类,就无法将值注入到父类的的属性中,因为cls.getDeclaredFields()无法获取父类的属性,所以我又改了一种方法
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;import org.springframework.jdbc.core.RowMapper;import com.lsb.exam.utils.StringUtils;public class MyRowMapper<T> implements RowMapper<T> {Class<T> cls;public MyRowMapper(Class<T> cls) {this.cls = cls;}@Overridepublic T mapRow(ResultSet rs, int rowNum) {try {T obj = cls.newInstance();//这个只能获取当前类的共有私有字段//Field[] fields = cls.getDeclaredFields();List<Field> list = new ArrayList<>();Class<?> c = cls;//循环获取while(c != null){list.addAll(Arrays.asList(c.getDeclaredFields()));c = c.getSuperclass();}// 获取所有的属性for (Field field : list) {field.setAccessible(true);if (field.getGenericType().toString().equals("class java.lang.Integer")) {Method m = obj.getClass().getMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.Integer.class);m.invoke(obj, rs.getInt(field.getName()));}if (field.getGenericType().toString().equals("class java.lang.String")) {Method m = obj.getClass().getMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.String.class);m.invoke(obj, rs.getString(field.getName()));}if (field.getGenericType().toString().equals("class java.util.Date")) {Method m = obj.getClass().getMethod("set" + StringUtils.firstChar2UpperCase(field.getName()), java.util.Date.class);m.invoke(obj, rs.getDate(field.getName()));}}return obj;} catch (Exception e) {e.printStackTrace();return null;}}
}
其实过程中,有一个反射的知识很重要,关于方法的介绍:
1、获取class
| 方法 | 描述 |
|---|---|
| object.getClass() | 获取这个实例所属的class对象 |
| T.class | 通过类型获取所属class对象 |
| Class.forName() | 通过类路径获取class对象 |
| Class.getSuperclass() | 获取父类的class对象 |
| Class.getClasses() | 获取类内所有的公开的类,接口,枚举成员,以及它继承的成员(特指类) |
| Class.getDeclaredClasses() | 通过类内显示声明的类,接口 |
| Class.getEnclosingClass() | 获取闭包类 |
2、获取属性
| 方法 | 描述 |
|---|---|
| getDeclaredField(String name) | 获取指定字段(公有,私有),不包括父类字段 |
| getField(String name) | 获取指定字段(公有),包括父类字段 |
| getDelaredFields() | 获取所有类内显示声明的字段(公有,私有),不包括父类字段 |
| getFields() | 获取所有字段(公有),包括父类字段 |
3、获取方法
| 方法 | 描述 |
|---|---|
| getDeclaredMethod(String name, Class<?> … paramType) | 获取指定方法(公有,私有),不包括父类方法 |
| getMethod(String name, Class<?> … paramType) | 获取指定方法(公有),包括父类方法 |
| getDeclaredMethods() | 获取所有声明方法(公有,私有),不包括父类方法 |
| getMethods() | 获取所有方法(公有),包括父类方法 |
上面的信息可以访问Oracle网站的反射信息。
相关文章:
Spring中的JdbcTemplate的使用
在最近的一个工作中,为了简单方便我就是用了Spring自带的JdbcTemplate来访问数据库,我以为之前自己很熟练的掌握,后来才发现我太天真了,踩了很多坑。 基本方法 JdbcTemplate自带很多方法可以执行SQL语句,以下我主要列举…...
机器学习——boosting之GBDT
现在要开始重点关注名字了,名字透漏了很多信息!名字暗藏线索! GBDT,Gradient Boosting Decision Tree: 梯度提升决策树 果然信息很丰富 梯度:意味着计算有迭代递进关系,但还不明确是怎么迭代递进的 提升&…...
如何选择报修管理系统?报修工单管理系统有哪些功能和优势?
报修管理系统是一种能够帮助企业快速反应设备故障和异常情况,并将问题及时通知到相关人员,并对问题进行统计和分析的系统。它能够有效提高企业的工作效率,并减少人员成本的支出。那么,报修工单管理系统有哪些功能和优势呢?下面以“…...
Matlab图像处理-
有些时候,直接利用图像的灰度直方图选择阈值不是非常直观,这时,可以利用图像三个通道的直方图来进行图像分割,操作步骤如上文所示,下图为原始图片。 下图为三通道直方图。 下图将三个通道的直方图会绘制到一个图表上&a…...
数据接口工程对接BI可视化大屏(二)创建BI空间
第2章 创建BI空间 2.1 SugarBI介绍 网站地址:https://cloud.baidu.com/product/sugar.html SugarBI是百度推出的自助BI报表分析和制作可视化数据大屏的强大工具。 基于百度Echarts提供丰富的图表组件,开箱即用、零代码操作、无需SQL,5分钟即可完成数…...
Struts.xml 配置文件说明
<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <!--…...
阿里巴巴API接口解析,实现获得商品详情
要解析阿里巴巴API接口并实现获取商品详情,你需要按照以下步骤进行操作: 了解阿里巴巴开放平台:访问阿里巴巴开放平台,并了解相关的API文档、开发者指南和规定。注册开发者账号:在阿里巴巴开放平台上注册一个开发者账…...
9.(Python数模)(分类模型一)K-means聚类
Python实现K-means聚类 K-means原理 K-means均值聚类算法作为最经典也是最基础的无标签分类学习算法。其实质就是根据两个数据点的距离去判断他们是否属于一类,对于一群点,就是类似用几个圆去框定这些点(簇),然后圆心…...
MinIO集群模式信息泄露漏洞(CVE-2023-28432)
前言:MinIO是一个用Golang开发的基于Apache License v2.0开源协议的对象存储服务。虽然轻量,却拥有着不错的性能。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据。该漏洞会在前台泄露用户的账户和密码。 0x00 环境配置 …...
【从零单排Golang】第十五话:用sync.Once实现懒加载的用法和坑点
在使用Golang做后端开发的工程中,我们通常需要声明一些一些配置类或服务单例等在业务逻辑层面较为底层的实例。为了节省内存或是冷启动开销,我们通常采用lazy-load懒加载的方式去初始化这些实例。初始化单例这个行为是一个非常经典的并发处理的案例&…...
常见注意力机制
注意力机制 (具有自适应性) 18年提出的一种新的 卷积注意力模块 ;对前馈卷积神经网络 是一个 简单而有效的 注意力模块 ; 因为它的 轻量级和通用性 ,可以 无缝集成到任何CNN网络 当中, 对我们来讲&…...
解决报错之org.aspectj.lang不存在
一、IDEA在使用时,可能会遇到maven依赖包明明存在,但是build或者启动时,报找不存在。 解决办法:第一时间检查Setting->Maven-Runner红圈中的√有没有选上。 二、有时候,明明依赖包存在,但是Maven页签中…...
java之SpringBoot基础篇、前后端项目、MyBatisPlus、MySQL、vue、elementUi
文章目录 前言JC-1.快速上手SpringBootJC-1-1.SpringBoot入门程序制作(一)JC-1-2.SpringBoot入门程序制作(二)JC-1-3.SpringBoot入门程序制作(三)JC-1-4.SpringBoot入门程序制作(四)…...
golang中如何判断字符串是否包含另一字符串
golang中如何判断字符串是否包含另一字符串 在Go语言中,可以使用strings.Contains()函数来判断一个字符串是否包含另一个字符串。该函数接受两个参数:要搜索的字符串和要查找的子字符串,如果子字符串存在于要搜索的字符串中,则返…...
ONNX OpenVino TensorRT MediaPipe NCNN Diffusers ComfyUI
框架 和Java生成的中间文件可以在JVM上运行一样,AI技术在具体落地应用方面,和其他软件技术一样,也需要具体的部署和实施的。既然要做部署,那就会有不同平台设备上的各种不同的部署方法和相关的部署架构工具 onnx 在训练模型时可以…...
java中使用 Integer 和 int 的 含义、使用方法 及之间的区别
学习目标: 学习目标如下: 明确 Integer 和 int 的 含义、使用方法 及之间的区别 学习内容: 一、区别: 1.Integer是int的包装类,int则是java的一种基本的数据类型; 2.Integer变量必须实例化之后才能使用&a…...
点云从入门到精通技术详解100篇-点云的特征检测
目录 前言 点云配准的研究背景 多元时间序列的相似性分析研究背景及意义 国内外研究现状...
DOM破坏绕过XSSfilter例题
目录 一、什么是DOM破坏 二、例题1 编辑 三、多层关系 1.Collection集合方式 2.标签关系 四、例题2 一、什么是DOM破坏 DOM破坏(DOM Clobbering)指的是对网页上的DOM结构进行不当的修改,导致页面行为异常、性能问题、安全风险或其他不…...
代码随想录Day_56打卡
①、两个字符串的删除操作 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 事例: 输入: word1 "sea", word2 "eat" 输出: 2 解释: 第一步将 "sea&…...
高忆管理:六连板捷荣技术或难扛“华为概念股”大旗
在本钱商场上名不见经传的捷荣技术(002855.SZ)正扛起“华为概念股”大旗。 9月6日,捷荣技术已拿下第六个连续涨停板,短短七个生意日,股价累积涨幅逾越90%。公司已连发两份股票生意异动公告。 是炒作,还是…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
