当前位置: 首页 > news >正文

Spring JDBC高级操作全解析

这段内容是关于 Spring Framework 中 JDBC 操作的高级用法,主要分为两大部分:

  1. SimpleJdbc:简化 JDBC 增删改查操作。
  2. 将 JDBC 操作建模为 Java 对象:通过面向对象的方式封装数据库操作。

第一部分:使用 SimpleJdbc 简化 JDBC 操作

核心思想
  • 使用 SimpleJdbcInsertSimpleJdbcCall 这两个类来减少配置代码。
  • 利用 JDBC 驱动提供的元数据(metadata)自动推断表结构、列名、参数类型等信息,从而避免手动声明。
  • 支持“流式 API”风格(fluid style),允许链式调用配置方法。

3.6.1 插入数据 (SimpleJdbcInsert)

核心步骤:

  1. 在 DAO 初始化时创建 SimpleJdbcInsert 实例,并指定数据源和表名。
  2. 将要插入的数据放入一个 Map<String, Object>,其中 key 是数据库字段名,value 是对应的值。
  3. 调用 .execute(parameters) 执行插入。

优点

  • 不需要写 SQL 语句。
  • 自动利用元数据生成 INSERT 语句。
this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");Map<String, Object> parameters = new HashMap<>();
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());insertActor.execute(parameters);

3.6.2 获取自动生成的主键

如果主键是数据库自增的(如 MySQL 的 AUTO_INCREMENT),可以使用 .usingGeneratedKeyColumns("id") 来告诉 Spring 返回这个生成的 ID。

然后调用 .executeAndReturnKey() 方法获取主键值。

this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingGeneratedKeyColumns("id");// 注意这里不传 id
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue()); // 设置回实体

📌 返回的是 java.lang.Number 类型,因为不同数据库返回的具体类型可能不同(Long、Integer 等)。


3.6.3 显式指定插入列

如果不希望插入所有字段(比如有些字段有默认值或不允许为空但不想设值),可以用 .usingColumns(...) 明确指定要插入哪些列。

this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor").usingColumns("first_name", "last_name") // 只插这两个字段.usingGeneratedKeyColumns("id");

这样即使 Map 中有多余字段也不会报错,只会处理指定的列。


3.6.4 使用 SqlParameterSource 提供参数值

除了使用 Map,还可以使用更方便的 SqlParameterSource 接口实现类:

BeanPropertySqlParameterSource

如果你有一个符合 JavaBean 规范的对象(getter/setter),可以直接传入该对象。它会自动通过 getter 提取属性值作为参数。

SqlParameterSource params = new BeanPropertySqlParameterSource(actor);
Number newId = insertActor.executeAndReturnKey(params);

前提是对象的属性名与数据库字段名一致(或通过命名策略映射)。

MapSqlParameterSource

比普通 HashMap 更友好的 map 实现,支持链式添加参数。

SqlParameterSource params = new MapSqlParameterSource().addValue("first_name", actor.getFirstName()).addValue("last_name", actor.getLastName());

3.6.5 调用存储过程 (SimpleJdbcCall)

用于调用数据库中的 存储过程(Stored Procedure)

假设有个 MySQL 存储过程:

CREATE PROCEDURE read_actor (IN in_id INTEGER,OUT out_first_name VARCHAR(100),OUT out_last_name VARCHAR(100),OUT out_birth_date DATE)
BEGINSELECT first_name, last_name, birth_dateINTO out_first_name, out_last_name, out_birth_dateFROM t_actor WHERE id = in_id;
END;

在 Java 中调用:

this.procReadActor = new SimpleJdbcCall(dataSource).withProcedureName("read_actor"); // 指定过程名// 输入参数
SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);// 执行并获得输出结果(Map)
Map<String, Object> out = procReadActor.execute(in);// 构造 Actor 对象
Actor actor = new Actor();
actor.setId(id);
actor.setFirstName((String)out.get("out_first_name"));
actor.setLastName((String)out.get("out_last_name"));
actor.setBirthDate((Date)out.get("out_birth_date"));

🔍 注意

  • 参数名大小写不敏感(Spring 使用元数据匹配)。
  • 输出参数也以 name-value 形式存在返回的 Map 中。

💡 建议开启忽略大小写的结果映射

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true); // 忽略大小写
this.procReadActor = new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_actor");

这能防止因数据库返回字段名为大写/小写导致取不到值的问题。


3.6.6 显式声明参数(适用于不支持元数据读取的数据库)

某些数据库(如 PostgreSQL)可能不被 Spring 完全支持元数据读取,此时需要手动声明参数。

你可以使用 .declareParameters(...) 明确指定每个参数的名称和类型。

this.procReadActor = new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_actor").withoutProcedureColumnMetaDataAccess() // 关闭元数据访问.useInParameterNames("in_id")           // 指定输入参数名.declareParameters(new SqlParameter("in_id", Types.NUMERIC),new SqlOutParameter("out_first_name", Types.VARCHAR),new SqlOutParameter("out_last_name", Types.VARCHAR),new SqlOutParameter("out_birth_date", Types.DATE));

📌 Types 来自 java.sql.Types,表示 JDBC 数据类型常量。


3.6.7 如何定义 SqlParameters
类型说明
SqlParameter输入参数(IN)
SqlOutParameter输出参数(OUT)
SqlInOutParameter既输入又输出(INOUT)

这些参数用于 SimpleJdbcCall 或后续提到的 StoredProcedure

例如:

new SqlParameter("in_id", Types.INTEGER)
new SqlOutParameter("result", Types.VARCHAR)

还可以附加额外信息,如精度、scale、自定义类型名、RowMapper(用于 REF CURSOR)等。


3.6.8 调用存储函数 (SimpleJdbcCall with Function)

类似调用存储过程,但用于调用 函数(Function),通常有返回值。

MySQL 函数示例:

CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200)
READS SQL DATA
BEGINDECLARE out_name VARCHAR(200);SELECT concat(first_name, ' ', last_name) INTO out_name FROM t_actor WHERE id = in_id;RETURN out_name;
END;

Java 调用方式:

this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate).withFunctionName("get_actor_name"); // 注意这里是 functionpublic String getActorName(Long id) {SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);return funcGetActorName.executeFunction(String.class, in); // 直接返回 String
}

.executeFunction(returnType, inputParams) 直接返回函数结果,无需从 Map 取。


3.6.9 从存储过程返回 ResultSet 或 REF Cursor

当存储过程返回结果集时(比如查询多条记录),需要用 .returningResultSet() 注册一个 RowMapper 来处理每行数据。

MySQL 示例:

CREATE PROCEDURE read_all_actors()
BEGINSELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a;
END;

Java 处理:

this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate).withProcedureName("read_all_actors").returningResultSet("actors", BeanPropertyRowMapper.newInstance(Actor.class)); // 映射到 Actor 列表public List<Actor> getActorsList() {Map<String, Object> result = procReadAllActors.execute(Collections.emptyMap());return (List<Actor>) result.get("actors"); // 获取映射后的列表
}

BeanPropertyRowMapper 可自动将列映射到同名属性上(驼峰转下划线等规则也可配置)。


第二部分:将 JDBC 操作建模为 Java 对象(RDBMS Operation Classes)

Spring 提供了一套面向对象的方式来封装数据库操作。虽然现在很多人更倾向于直接使用 JdbcTemplate,但在复杂场景下仍有价值。


3.7.1 & 3.7.2 SqlQuery / MappingSqlQuery

封装可重用的查询逻辑,特别是将结果映射为 Java 对象。

示例:根据 ID 查询演员

public class ActorMappingQuery extends MappingSqlQuery<Actor> {public ActorMappingQuery(DataSource ds) {super(ds, "SELECT id, first_name, last_name FROM t_actor WHERE id = ?");declareParameter(new SqlParameter("id", Types.INTEGER));compile(); // 编译准备语句}@Overrideprotected Actor mapRow(ResultSet rs, int rowNum) throws SQLException {Actor actor = new Actor();actor.setId(rs.getLong("id"));actor.setFirstName(rs.getString("first_name"));actor.setLastName(rs.getString("last_name"));return actor;}
}

使用:

Actor actor = actorMappingQuery.findObject(id); // findObject 返回单个对象
List<Actor> actors = actorSearchMappingQuery.execute(age, pattern); // execute 返回列表

✅ 特点:

  • 线程安全(编译后不可变)
  • 可复用
  • 强类型查询封装

3.7.3 SqlUpdate

封装更新操作(INSERT/UPDATE/DELETE)。

示例:更新客户信用评级

public class UpdateCreditRating extends SqlUpdate {public UpdateCreditRating(DataSource ds) {setDataSource(ds);setSql("UPDATE customer SET credit_rating = ? WHERE id = ?");declareParameter(new SqlParameter("creditRating", Types.NUMERIC));declareParameter(new SqlParameter("id", Types.NUMERIC));compile();}public int execute(int id, int rating) {return update(rating, id); // 调用父类 update 方法}
}

返回受影响行数。


3.7.4 StoredProcedure

抽象类,用于封装对数据库存储过程的调用。

特点:

  • 必须继承它
  • 支持输入、输出、输入输出参数
  • 更灵活,适合复杂过程调用

示例:调用 Oracle 的 sysdate 函数

private class GetSysdateProcedure extends StoredProcedure {public GetSysdateProcedure(DataSource dataSource) {setDataSource(dataSource);setFunction(true); // 表示这是一个函数setSql("sysdate");declareParameter(new SqlOutParameter("date", Types.DATE));compile();}public Date execute() {Map<String, Object> results = execute(new HashMap<>());return (Date) results.get("date");}
}

另一个例子:返回多个 REF CURSOR(Oracle 特有)

declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));

配合 RowMapper 使用,可以把游标结果映射成对象列表。


总结对比

功能推荐方式说明
简单插入SimpleJdbcInsert最简洁,自动利用元数据
调用存储过程SimpleJdbcCall推荐首选,配置简单
调用函数SimpleJdbcCall.withFunctionName()直接返回函数值
复杂映射查询MappingSqlQuery封装固定查询逻辑
更新操作SqlUpdate 或直接 JdbcTemplate.update()后者更常用
复杂存储过程(多结果集、REF CURSOR)StoredProcedure更强大,适合 Oracle 等企业级数据库

💡 实际开发建议

  1. 优先使用 JdbcTemplate + Lambda(现代 Spring Boot 风格)
  2. 如果想进一步简化 CRUD,考虑使用 Spring Data JPA / MyBatis / QueryDSL
  3. SimpleJdbcInsertSimpleJdbcCall 适合快速集成已有数据库过程或简单批量操作。
  4. MappingSqlQuerySqlUpdate 属于较老的设计模式,现在较少单独使用。
  5. StoredProcedure 主要在需要对接遗留系统或 Oracle PL/SQL 时使用。

如有具体问题(比如某个类怎么用、报错分析、性能优化),欢迎继续提问!

http://www.dtcms.com/a/541138.html

相关文章:

  • Matlab混合编程技术学习教程——目录
  • 基于MATLAB的LBFGS优化算法实现
  • 【matlab】字符串数组 转 double
  • 技术速递|Playwright MCP 调试 Web 应用时,GitHub Copilot 生成断言脚本的实用方法
  • RTSP低延迟播放重构:SmartMediaKit如何让系统“看见即行动”
  • 技术文档搭建实战:基于PandaWiki的五步自动化方案
  • wordpress能做手机站么电商网站设计系列
  • 深入剖析SLAB分配器原理与优化实战
  • 建设安全备案登入那个网站wordpress文章微信公众号推送
  • 6.1.3.1 大数据方法论与实践指南-开源大数据离线调度平台
  • 技术支持 东莞网站建设石材seo智能优化系统
  • 南沙区建设局网站如何进行网站域名解析
  • GNSS+LiDAR+Camera(双目)+IMU(战术级)的多传感器融合定位-WayFinder
  • HTML基础(一)
  • 诺亚财富汪静波:在波动中捕捉结构性机会,创造穿越周期的长期价值
  • SAP SD销售开票及回款信息查询接口分享
  • 更新维护:定期更新、功能修复、性能优化的全面指南
  • vue3 全局定义动态样式
  • 常州网站建设效果黑马程序员学费多少钱
  • 昆山设计网站的公司深圳市涂能装饰设计公司网站
  • Rocky Linux 9.4 搭建k8s-1.28.0 + docker一主多从集群测试环境
  • 做网站的一般要多钱wordpress国内不使用方法
  • docker自定义网络
  • K8S 安装 部署 文档
  • stm32_关于乐鑫ESP8266-07S WIFI模组烧录安信可科技的MQTT固件流程
  • GitLab 私服(基于 Docker)搭建方案
  • 外贸网站wordpresswordpress模版安装
  • React 09
  • 2 VTK的基本概念
  • 慈溪市建设局网站表格下载工装公司名字怎么起