Spring之事务管理TranscationManager(大合集),高级java面试题大全

本文探讨了在未配置事务的情况下,Java服务如何进行CRUD操作,强调了数据安全性和事务控制的重要性。通过示例展示了在错误SQL执行时,数据的不一致性和事务管理的必要性。同时,提到了Spring事务控制的传播行为和Spring与线程的关系.
摘要由CSDN通过智能技术生成

package com.zhu.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.DruidDataSource;

@Service(“service1”)

public class UserJdbcWithoutTransManagerService {

@Autowired

private JdbcTemplate jdbcTemplate;

public void addScore(String userName,int toAdd){

String sql = “UPDATE zhu_test u SET u.age = u.age + ? WHERE name =?”;

jdbcTemplate.update(sql,toAdd,userName);

}

public static void main(String[] args) {

ApplicationContext ctx =

new ClassPathXmlApplicationContext(“file:src/main/resources/applicationContext.xml”);

UserJdbcWithoutTransManagerService service =

(UserJdbcWithoutTransManagerService)ctx.getBean(“service1”);

JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean(“jdbcTemplate”);

DruidDataSource druidDataSource = (DruidDataSource)jdbcTemplate.getDataSource();

//①.检查数据源autoCommit的设置

System.out.println(“autoCommit:”+ druidDataSource.isDefaultAutoCommit());

//②.插入一条记录,初始分数为10

jdbcTemplate.execute(

“INSERT INTO zhu_test VALUES(‘tom’,10)”);

//③.调用工作在无事务环境下的服务类方法,将分数添加20分

service.addScore(“tom”,20);

//④.查看此时用户的分数

@SuppressWarnings(“deprecation”)

int score = jdbcTemplate.queryForInt(“SELECT age FROM zhu_test WHERE name =‘tom’”);

System.out.println(“score:”+score);

jdbcTemplate.execute(“DELETE FROM zhu_test WHERE name=‘tom’”);

}

}

运行结果:

这里写图片描述

applicationContext.xml中并没有配置事务,但是还是持久化到数据库中去了。DataSource默认设置是自动提交的,也就是说,在执行了CRUD之后,会马上持久化到数据库中。如果设置自动提交为false,那么在jdbcTemplate执行完之后并不会马上持久化到数据库中,除非手动提交。

虽说没有事务管理,程序依然可以进行数据的CRUD操作,但是没有事务管理,在数据安全同步方面会面临很大的挑战。栗子太多,不一一举例,看代码

我们故意提交一条错误的sql语句

package com.zhu.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.DruidDataSource;

@Service(“service1”)

public class UserJdbcWithoutTransManagerService {

@Autowired

private JdbcTemplate jdbcTemplate;

public void addScore(String userName,int toAdd){

String sql = “UPDATE zhu_test u SET u.age = u.age + ? WHERE name =?”;

jdbcTemplate.update(sql,toAdd,userName);

}

public static void main(String[] args) {

ApplicationContext ctx =

new ClassPathXmlApplicationContext(“file:src/main/resources/applicationContext.xml”);

UserJdbcWithoutTransManagerService service =

(UserJdbcWithoutTransManagerService)ctx.getBean(“service1”);

JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean(“jdbcTemplate”);

DruidDataSource druidDataSource = (DruidDataSource)jdbcTemplate.getDataSource();

//①.检查数据源autoCommit的设置

System.err.println(“autoCommit:”+ druidDataSource.isDefaultAutoCommit());

//②.插入一条记录,初始分数为10

jdbcTemplate.execute(

“INSERT INTO zhu_test VALUES(‘tom’,10)”);

//③.执行一条错误sql语句

jdbcTemplate.execute(

“INSERT INTO zhu_test VALUES(‘tom1’,10,00)”);

//④.调用工作在无事务环境下的服务类方法,将分数添加20分(不会执行)

service.addScore(“tom”,20);

}

}

清空数据,然后执行结果是:②成功插入数据tom 10 然后③错误的sql抛出异常 然后④更新操作也不会执行。

正常的逻辑是在同一方法或者类中执行一系列的CRUD操作,其中一条出出现问题会抛出异常,然后已经执行完的语句回滚到发生异常之前的状态。

这种情况在正常的开发环境是最基本的常识性错误,开发过程中一定要避免。

然后是配置事务。

applicationContext.xml文件中添加事务和模型视图的配置

package com.zhu.controller;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

@Controller

public class NoTMController {

//②.自动注入JdbcTemplate

@Autowired

private JdbcTemplate jdbcTemplate;

//③.通过Spring MVC注解映URL请求

@RequestMapping("/logon")

@ResponseBody

public String logon(String userName,String password){

String sql = “UPDATE zhu_test u SET u.age = u.age + ? WHERE name =?”;

if(isRightUser(userName,password)){

//执行更新操作(年龄加20)

jdbcTemplate.update(sql,20,“tom”);

//执行错误语句

jdbcTemplate.execute(

"INSERT

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

INTO zhu_test VALUES(‘tom1’,10,00)");

return “success”;

}else{

return “fail”;

}

}

private boolean isRightUser(String userName,String password){

//do sth…

return true;

}

}

启动项目 输入网址

影响了一条数据,操作已经完成

这里写图片描述

检测到错误,回滚

这里写图片描述

所以结果是数据没有变化。

注释掉错误的sql语句则完成执行所有的sql语句。

事务在进行嵌套调用的时候不会分解为多个事务,比如说一个事务中的方法调用其他事务的方法不会产生多余的事务。

spring 对事务控制的支持统一在 TransactionDefinition 类中描述,该类有以下几个重要的接口方法:

int getPropagationBehavior():事务的传播行为

int getIsolationLevel():事务的隔离级别

int getTimeout():事务的过期时间

boolean isReadOnly():事务的读写特性。

所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring 支持 7 种事务传播行为:

  • PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

  • PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行

  • PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常

  • PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。

  • PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  • PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

事务与线程

由于 Spring 的事务管理器是通过线程相关的 ThreadLocal 来保存数据访问基础设施,再结合 IOC 和 AOP 实现高级声明式事务的功能,所以 Spring 的事务天然地和线程有着千丝万缕的联系。

我们知道 Web 容器本身就是多线程的,Web 容器为一个 Http 请求创建一个独立的线程,所以由此请求所牵涉到的 Spring 容器中的 Bean 也是运行于多线程的环境下。在绝大多数情况下,Spring 的 Bean 都是单实例的(singleton),单实例 Bean 的最大的好处是线程无关性,不存在多线程并发访问的问题,也即是线程安全的。

一个类能够以单实例的方式运行的前提是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO 必须执有一个 Connection,而 Connection 即是状态化的对象。所以传统的 DAO 不能做成单实例的,每次要用时都必须 new 一个新的实例。传统的 Service 由于将有状态的 DAO 作为成员变量,所以传统的 Service 本身也是有状态的。

但是在 Spring 中,DAO 和 Service 都以单实例的方式存在。Spring 是通过 ThreadLocal 将有状态的变量(如 Connection 等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。Spring 不遗余力地将状态化的对象无状态化,就是要达到单实例化 Bean 的目的。

由于 Spring 已经通过 ThreadLocal 的设施将 Bean 无状态化,所以 Spring 中单实例 Bean 对线程安全问题拥有了一种天生的免疫能力。不但单实例的 Service 可以成功运行于多线程环境中,Service 本身还可以自由地启动独立线程以执行其它的 Service。

于是我们修改我们的代码

package com.zhu.controller;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

@Controller

public class NoTMController {

//②.自动注入JdbcTemplate

@Autowired

private JdbcTemplate jdbcTemplate;

//③.通过Spring MVC注解映URL请求

@RequestMapping("/logon")

@ResponseBody

public String logon(String userName,String password){

System.err.println(userName);

if(isRightUser(userName,password)){

Thread h1 = new Thread(){

String sql = “UPDATE zhu_test u SET u.age = u.age + ? WHERE name =?”;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值