23-Spring-3(JdbcTemplate)
Spring 提供了 JdbcTemplate 以便捷地操作 DB。
1. CRUD 测试#
1.1 导包#
1.2 配置文件#
<!--
DAO 层组件自动装配 JdbcTemplate:
@Repository
public class EmpDao {
@Autowired
JdbcTemplate jdbcTemplate;
}
-->
<context:component-scan base-package="cn.edu.nuist"></context:component-scan>
<!-- 引用外部属性文件;"classpath:" 固定写法,引入类路径下资源 -->
<context:property-placeholder location="classpath:dbconfig.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<!-- Spring 提供了一个类 JdbcTemplate,使用它来操作 DB -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置一个具有 ‘具名参数’ 功能的 JdbcTemplate -->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
1.3 测试#
public class TestJDBC {
ApplicationContext ioc = new
ClassPathXmlApplicationContext("classpath:applicationContext.xml");
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
NamedParameterJdbcTemplate namedParamJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
// 8. 创建 EmpDao,自动装配 JdbcTemplate 对象
@Test
public void testEmpDao() {
EmpDao empDao = ioc.getBean(EmpDao.class);
Employee emp = new Employee();
emp.setEmpName("吴九");
emp.setSalary(5000);
empDao.saveEmp(emp);
empDao.saveEmp(emp);
}
// 7. 以SqlParameterSource形式传入参数值
public void testSqlParameterSource() {
String sql = "INSERT INTO employees(emp_name, salary) VALUES(:empName, :salary)";
Employee emp = new Employee();
emp.setEmpName("崔八");
emp.setSalary(5000);
// SqlParameterSource BeanPropertySqlParameterSource
int update = namedParamJdbcTemplate.update(sql
, new BeanPropertySqlParameterSource(emp));
System.out.println(update);
}
/*
6. 使用带有具名参数的 SQL 语句插入一条员工记录,并以 Map 形式传入参数值
占位符参数:? 的顺序不能乱,传参的时候要注意
具名参数:具有名字的参数,参数不是占位符了,而是一个变量名
语法格式 [:参数名]
Spring 有一个支持具名参数功能的JdbcTemplate: NamedParameterJdbcTemplate
*/
public void insertWithMap() {
String sql = "INSERT INTO employees(emp_name, salary) VALUES(:empName, :salary)";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("empName", "田七");
paramMap.put("salary", 5000);
int update = namedParamJdbcTemplate.update(sql, paramMap);
System.out.println(update);
}
// 5. 更新数据
public void testUpdate() {
String sql = "UPDATE employees SET salary = ? WHERE emp_id = ?";
int update = jdbcTemplate.update(sql, 4000, 4);
System.out.println(update);
}
// 4. 批量插入数据
public void testBatchAdd() {
String sql = "INSERT INTO employees(emp_name, salary) VALUES(?, ?)";
// List<Object[]>
// List.size: SQL 语句要执行的次数
// Object[]: 每次执行要用的参数
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[] {"张三", 3000});
batchArgs.add(new Object[] {"李四", 3000});
batchArgs.add(new Object[] {"王五", 3000});
batchArgs.add(new Object[] {"赵六", 3000});
int[] updates = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(updates));
}
// 3. 查询单个数据
public void queryMaxSalary() {
String sql = "SELECT MAX(salary) FROM employees";
int maxSalary = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(maxSalary);
}
/*
* JavaBean 需要和 DB 中表的字段名一致,否则无法完成封装
* JdbcTemplate 在方法级别进行了区分
* > 查询单个对象:template.queryForObject(),如果查询不到就会抛异常
* > 查询集合:template.query()
*/
// 2. 查询多条记录,封装到 List
public void queryMore() {
String sql = "SELECT emp_id empId, emp_name empName, salary"
+ " FROM employees WHERE salary > ?";
// 封装 List
List<Employee> emps = jdbcTemplate.query(sql
, new BeanPropertyRowMapper<>(Employee.class), 3000);
System.out.println(emps);
}
// 1. 查询 1 条记录,封装到 JavaBean
public void queryOne() {
String sql = "SELECT emp_id empId, emp_name empName, salary"
+ " FROM employees WHERE emp_id = ?";
Employee emp = jdbcTemplate.queryForObject(sql
, new BeanPropertyRowMapper<>(Employee.class), 40);
System.out.println(emp);
}
}
2. 声明式事务#
事务管理代码的固定模式作为一种横切关注点,可以通过 AOP 方法模块化,进而借助 Spring AOP 框架实现声明式事务管理。
这个事务管理器就可以在目标方法运行前后进行事务控制(事务切面)。我们目前使用 DataSourceTransactionManager 即可。
2.1 配置文件#
<!-- 导入外部配置文件 -->
<context:property-placeholder location="classpath:dbconfig.properties"/>
<!-- 配数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
</bean>
<!-- 事务控制-->
<!-- 1. 配置事务管理器让其进行事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 控制住数据源 (控制事务实际就是控制住Connection) -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2. 开启基于注解的事务控制模式(依赖 tx 名称空间), 默认 transactionManager -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 3. 给事务方法加 @Transactional → 环绕通知Around -->
2.2 @Transactional#
2.2.1 propagation#
事务的传播行为(事务的传播 + 事务的行为),如果有多个事务进行嵌套运行,子事务是否和大事务共用一个事务(使用同一条连接)。
- 如果是 REQUIRED,则子事务的属性都是继承自大事务。
- REQUIRED 是将之前事务用的 Connection 传递过来
- REQUIRES_NEW 直接使用新的 Connection
2.2.2 isolation#
事务的隔离级别。
- READ_UNCOMMITTED
- READ_COMMITTED
- REPEATABLE_READ
- SERIALIZABLE
2.2.3 timeout#
- 超时属性
- 事务超出指定时长(秒为单位) 后自动终止并回滚
2.2.4 readOnly#
- 默认 false
- 可以进行事务优化
- 能加快查询速度,不用管事务那堆操作了
- 如果设置为 true 后,做非查询操作,会抛异常
2.2.5 rollback 相关#
- 异常分类
- 运行时异常(非检查异常):可以不用处理,默认都回滚
- 编译时异常(检查异常):默认不回滚
- 回滚相关属性
- rollbackFor:出现哪些异常,事务可以不回滚
- rollbackForClassName
- noRollbackFor:原来不回滚的异常,指定让其回滚
- noRollbackForClassName
3. 基于 XML 配置的事务#
- 依赖 tx、aop 名称空间
<aop:pointcut>
切入点表达式<tx:advice>
事务通知,类比注解式中的切面类 (事务切面按照切入点表达式去切入事务方法)<aop:advisor>
关联 [切入点表达式] 和 [事务通知]
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut id="txPoint" expression="execution(* cn.edu.nuist.service.*.*(..))"/>
<!-- 事务建议:关联 [切入点表达式] 和 [事务通知] -->
<aop:advisor pointcut-ref="txPoint" advice-ref="myAdvice"/>
</aop:config>
<!-- 事务通知 // 关联 [事务管理器] -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!-- 事务属性 -->
<tx:attributes>
<!-- 指明哪些方法是事务方法:
切入点表达式只是说,事务管理器要切入这些方法,至于方法的详细
配置(传播行为、隔离级别、回滚异常 ...),还需要使用 tx:method
-->
<tx:method name="*"/>
<tx:method name="checkout" propagation="REQUIRED" timeout="-1"/>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 都用;重要的用配置,不重要的用注解 -->
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?