UAP应该知道的那些事儿--2(事务)

第一节 事务是什么?隔离级别?传播属性?

开始本章内容之前,本人希望你已经看过了该系列的第一篇UAP应该知道的那些事儿–1!这对你充分理解本章的内容起到了很大的作用,废话不多说我们开始本章的内容!
在平时的开发中,除了写代码,你一定每天都和各种各样的数据库打过交道,Nosql我这里先不说,起码mysql、oracle、sqlserver这些常见的关系数据库你一定不陌生,那么用数据库有一个名词你是一定要了解的,那就是事务(分布式事务今天这里先不谈),啥是事务?

数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

以上是官方对于数据库事务的一个定义,我这里也不对这些东西做解释了,自行去了解一下这些概念

图p1

图p8

spring七个事物传播属性:

  • PROPAGATION_REQUIRED -支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • PROPAGATION_SUPPORTS -支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY -支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW -新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED -以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER -以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED -如果当前存在事务则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

以上这些不是本章的重点,看一看了解一下先即可。

第二节 NC用的什么隔离级别?支持哪几种传播属性?

ok,经过上一节介绍应该对事务有了一个大致的了解了,那么回来我们说说NC,NC支持Oracle和Sqlserver
(这里多说一句,通过修改源码可以支持db2以及mysql,本人已亲证),其实这两个数据库默认的隔离级别都是读已提交(read commit),也就是说一个事务只能读取到另外一个事务commit了的数据,当然你可以根据你项目的需求进行调整,
上一章我强调过了,从源码层面分析了我们NC其实是只支持两种传播属性:Requires和equiresNew
,见下图:

图p1

图p1

第三节 本章重点:如何让你的代码使用到事务!!!

不用多说这是本章的重点,在说本节内容之前,先提一个名词—>jdk动态代理,不知诸位对这个东东有没有了解过,没了解过的建议去百度一下,我这里直接上代码:
1、先有一个接口TargetItf:

1
2
3
4
5
6
7
8
9
package jdkdtproxy;

public interface TargetItf {

public void method1();

public String method2();

}

2、对该接口有一个实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package jdkdtproxy;

public class Target implements TargetItf {

@Override
public void method1() {
System.out.println("method invoked");
}

@Override
public String method2() {
System.out.println("method invoked");
return "method2 result";
}

}

3、产生动态代理并通过动态代理调用方法

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
package jdkdtproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.junit.Test;
/**
*
* @ClassName: TargetTest
* @Description: TODO(这里用一句话描述这个类的作用)
* @author steven
* @date 2018年11月24日 上午9:53:24
*
*/
public class TargetTest {

@Test
public void test1(){

final Target target = new Target();
TargetItf newProxyInstance = (TargetItf)Proxy.
newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke before");
Object invokeResult = method.invoke(target, args);
System.out.println("invoke after");
return invokeResult;
}
});
newProxyInstance.method1();
String method2 = newProxyInstance.method2();
System.out.println(method2);

}
}

执行结果:

1
2
3
4
5
6
7
invoke before
method invoked
invoke after
invoke before
method invoked
invoke after
method2 result

是不是觉得很神奇?竟然在你的目标方法内容前后都进行了操作!没错,这就是动态代理,它能够对你的目标对象进行拦截切面,而且我们NC的服务端容器就是完全依赖了这个东东,所有通过upm文件中写了cmt属性的接口在服务容器中都是一个动态代理对象而不是实现类本身,现在回应上一章的内容,你如果通过了NcLocator来获取到了一个对象,然后使用这个代理对象去调用对应的服务方法,然后的东西你明白了吗?来看看下面两张我精心挑选的图:
p6

图p2、Requires属性的处理方式

p6

图p3、RequiresNew属性的处理方式
现在是不是清晰了许多,我来总结一下:服务启动,会在内存中产生一个Locator容器keyvalue形式,key是接口类名称,value就是实现类的动态代理对象,通过上图很明显的看到,如果你想让你的代码能够使用到事务以及事务的传播属性,那你一定要通过Nclocator进行对象的获取,因为事务的开启与关闭以及服务方法调用前后的日志记录都是通过代理对象来实现的!

由于篇幅原因,Requires和RequiresNew具体区别我们下一张详细论述!!!,下一章我们通过实例来详细分析如何让你的代码能够使用到事务以及事务的传播属性。

Slogan:技术界一枚不起眼的迷途小书童