spring 复习整理四

11号楼

spring 复习整理四(仗剑走天涯)

spring 框架的事务抽象

三个关键类

TransactionManager

org.springframework.transaction.PlatformTransactionManager接口用于为不同平台提供统一抽象的事务管理器,重要

org.springframework.transaction.ReactiveTransactionManager接口用于响应式事务管理,这个不重要

1
2
3
4
5
6
7
8
9
public interface PlatformTransactionManager extends TransactionManager {

TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;
}

任何PlatformTransactionManager接口实现类的方法抛出的TransactionException是未检查的 它继承了java.lang.RuntimeException的类

TransactionDefinition

TransactionDefinition 接口指定了当前事务的相关配置

  • 配置

Propagation:通常情况下,事务范围内的所有代码都在该事务中运行。 但是,如果事务方法在 已经存在事务的上下文中运行,则可以指定事务的传播行为

Isolation:该事务与其他事务的工作隔离的程度。 例如,这个事务可以看到其他事务未提交的写入吗 隔离级

Timeout:该事务在超时并被底层事务基础设施自动回滚之前运行多长时间

只读状态:当代码读取但不修改数据时,可以使用只读事务。 在某些情况下,如使用Hibernate时,只读事务可能是一种有用的优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public interface TransactionDefinition {

int PROPAGATION_REQUIRED = 0;

int PROPAGATION_SUPPORTS = 1;

int PROPAGATION_MANDATORY = 2;

int PROPAGATION_REQUIRES_NEW = 3;

int PROPAGATION_NOT_SUPPORTED = 4;

int PROPAGATION_NEVER = 5;

int PROPAGATION_NESTED = 6;

int ISOLATION_DEFAULT = -1;

int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;

int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;

int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;

int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;

int TIMEOUT_DEFAULT = -1;

default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}

default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}

default int getTimeout() {
return TIMEOUT_DEFAULT;
}

default boolean isReadOnly() {
return false;
}

@Nullable
default String getName() {
return null;
}

static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}

TransactionStatus

TransactionStatus接口为事务代码提供了一种简单的方法来控制事务执行和查询事务状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface TransactionStatus extends TransactionExecutionSavepointManagerFlushable {

@Override
//返回当前事务是否是新的; 否则将参与现有事务,或者可能从一开始就不在实际事务中运行。
boolean isNewTransaction();

boolean hasSavepoint();

@Override
// 只设置事务回滚。 这指示事务管理器,事务的唯一可能结果可能是回滚,而不是抛出异常,从而触发回滚。
void setRollbackOnly();

@Override
// 返回事务是否被标记为仅回滚(由应用程序或事务基础设施)。
boolean isRollbackOnly();

void flush();

@Override
// 返回该事务是否已完成,即是否已提交或回滚。
boolean isCompleted();
}

编程式事务管理

使用 TransactionManager

1
2
3
4
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
user=root
password=xiaobo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 引入配置文件-->
<context:property-placeholder location="jdbc.properties" />

<!-- 扫包-->
<context:component-scan base-package="com.dream.xiaobo.service" />


<!-- 注入事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
</bean>

<!-- 数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClass}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</bean>


</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Service
public class AccountServiceImpl implements AccountService {

@Autowired
private PlatformTransactionManager transactionManager;

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public void transfer(String from, String to, Integer money) {

// 默认的事务配置
TransactionDefinition definition = new DefaultTransactionDefinition();

// 进行事务管理
TransactionStatus transaction = transactionManager.getTransaction(definition);

try {
String fromSql = "UPDATE account SET money = money - ? WHERE name = ?";

int i = 1/ 0;

String toSql = "UPDATE account SET money = money + ? WHERE name = ?";

jdbcTemplate.update(fromSql,new Object[]{
money,
from
});

jdbcTemplate.update(toSql,new Object[]{
money,
to
});
}catch (RuntimeException exception){
exception.printStackTrace();
// 回滚
transactionManager.rollback(transaction);
}
// 提交
transactionManager.commit(transaction);
}
}

使用TransactionTemplate

1
2
3
4
5
6
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Service
public class AccountServiceImpl2 implements AccountService {


@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private TransactionTemplate transactionTemplate;


@Override
public void transfer(String from, String to, Integer money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
String fromSql = "UPDATE account SET money = money - ? WHERE name = ?";

int i = 1/ 0;

String toSql = "UPDATE account SET money = money + ? WHERE name = ?";

jdbcTemplate.update(fromSql,new Object[]{
money,
from
});

jdbcTemplate.update(toSql,new Object[]{
money,
to
});
}
});
}
}

new TransactionCallback<Object>() {} 带返回值

new TransactionCallbackWithoutResult(){} 不带返回值

声明式事务管理

Spring Framework的TransactionInterceptor为命令式和响应式编程模型提供了事务管理 拦截器通过检查方法返回类型来检测所需的事务管理风格 事务管理风格会影响需要哪个事务管理器 命令式事务需要PlatformTransactionManager 而响应式事务使用 ReactiveTransactionManager 实现

@Transactional 通常用于PlatformTransactionManager 管理的 线程绑定事务 将事务暴露给当前执行线程中的所有数据访问操作

1
2
3
4
5
6
7
8
9
10
11
12
<tx:advice id="interceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="point" expression="execution(public * com.dream.xiaobo.service..*(..))"/>
<aop:advisor advice-ref="interceptor" pointcut-ref="point" />
</aop:config>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TxAdvice implements MethodInterceptor {

private PlatformTransactionManager transactionManager;

public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

// 默认的事务配置
TransactionDefinition definition = new DefaultTransactionDefinition();

// 进行事务管理
TransactionStatus transaction = transactionManager.getTransaction(definition);

Object invoke = null;
try {

invoke = methodInvocation.getMethod().invoke(
methodInvocation.getThis(),
methodInvocation.getArguments()
);

}catch (RuntimeException exception){
exception.printStackTrace();
transactionManager.rollback(transaction);
throw new RuntimeException("出现异常了");
}

transactionManager.commit(transaction);

return invoke;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void transfer2(String from, String to, Integer money) {

String fromSql = "UPDATE account SET money = money - ? WHERE name = ?";

int i = 1/ 0;

String toSql = "UPDATE account SET money = money + ? WHERE name = ?";

jdbcTemplate.update(fromSql,new Object[]{
money,
from
});

jdbcTemplate.update(toSql,new Object[]{
money,
to
});

}

事务回滚

在其默认配置中,Spring框架的事务基础结构代码只在运行时、未检查的异常情况下标记事务进行回滚。 也就是说,当抛出的异常是 RuntimeException 的实例或子类时 (默认情况下,Error 实例也会导致回滚) 事务方法抛出的已检查异常不会导致默认配置的回滚

指定回滚规则

1
2
3
4
5
6
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

指定无回滚规则

1
2
3
4
5
6
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

tx:advice/ 设置

默认配置

  • 传播行为是REQUIRED

  • 隔离级别为 DEFAULT

  • 事务处于可读写状态

  • 事务超时默认为底层事务系统的默认超时,如果不支持超时,则为none

  • 任何RuntimeException触发回滚,而任何选中的Exception不会

属性介绍

嵌套在tx:advice/tx:attributes/标签中的tx:method/标签的各种属性如下

属性 Required? 默认值 描述
name Yes 要与事务属性相关联的方法名 通配符()字符可用于将相同的事务属性设置与许多方法相关联(例如,’ get ‘、’ handle* ‘、’ on*Event ‘等等)
propagation No REQUIRED 事务传播行为
isolation No DEFAULT 事务隔离级别 仅适用于’ REQUIRED ‘或’ REQUIRES_NEW ‘的传播设置
timeout No -1 事务超时(秒) 仅适用于传播’ REQUIRED ‘或’ REQUIRES_NEW ‘
read-only No false 读写事务与只读事务 只适用于’ REQUIRED ‘或’ REQUIRES_NEW ‘
rollback-for No 触发回滚的“Exception”实例的逗号分隔列表例如,“com.foo.MyBusinessException, ServletException”
no-rollback-for No 不触发回滚的“Exception”实例的逗号分隔列表例如,“com.foo.MyBusinessException, ServletException”

@Transactional

@Transactional 注解是元数据,它指定接口、类或方法必须具有事务性语义

  • 开启Transactional的注解方式
1
@EnableTransactionManagement
  • xml开启Transactional的方式
1
<tx:annotation-driven transaction-manager="txManager"/>

如果要连接的TransactionManager 的bean名称为 TransactionManager 则可以省略 tx:annotation-driven 标记中的 transaction-manager 属性

事务传播

事务的传播通常发生在【一个service层中的方法】调用【其他service层中的方法】,虽然我们并不推荐这么做,但是的确会存在一个【大的业务】包含多个【小业务】,大业务和小业务都可独立运行的场景

传播行为 含义
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。
隔离级别 含义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

spring简单整合mybatis

  • pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<dependencies>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

<!-- spring上下文 Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>

<!-- logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>

<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>


<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>


<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.19</version>
</dependency>


<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>

</dependencies>
  • spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://mybatis.org/schema/mybatis-spring
https://mybatis.org/schema/mybatis-spring.xsd
">

<!-- 引入配置文件-->
<context:property-placeholder location="jdbc.properties" />

<!-- 扫包-->
<context:component-scan base-package="com.dream.xiaobo" />

<!-- 扫描mapper文件-->
<mybatis:scan base-package="com.dream.xiaobo.dao" />

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:mapper/**/*.xml" />
</bean>

<!-- 注入事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>

<!-- 数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClass}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</bean>
</beans>
  • mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<settings>

<!-- 开启懒加载配置 -->
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>

<!-- 设置日志打印格式-->
<setting name="logImpl" value="SLF4J"/>
<setting name="logPrefix" value="mybatis.sql."/>
<!-- 驼峰式命名-->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

<!-- 别名配置-->
<typeAliases>
<!-- <typeAlias type="com.dream.xiaobo.entity.User" alias="user"/>-->
<!-- <typeAlias type="com.dream.xiaobo.datasources.DruidDatasourcesFactory" alias="DRUID"/>-->
<package name="com.dream.xiaobo.entity"/>
</typeAliases>


</configuration>
  • jdbc.properties
1
2
3
4
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
user=root
password=xiaobo
  • userMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--映射地址-->
<mapper namespace="com.dream.xiaobo.dao.UserMapper">

<resultMap id="userMap" type="com.dream.xiaobo.entity.User">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
</resultMap>

<sql id="sql">
id,username,password
</sql>

<select id="selectUser" resultType="com.dream.xiaobo.entity.User">
SELECT <include refid="sql" />
FROM `user`
</select>

</mapper>

你知道的越多 你不知道的越多 嘿 我是小博 带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:spring 复习整理四

文章作者:小博

发布时间:2022年05月11日 - 17:22

最后更新:2022年05月11日 - 18:37

原始链接:https://codexiaobo.github.io/posts/2740706963/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。