动手写web框架(9): 事务实现

定义事务注解

@Transaction注解只能用于方法级别。

Transaction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.smart4j.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义需要事务控制的方法
* Created by Roger on 2016/11/30.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transaction {
}

事务操作工具

每个线程维护本地变量Connection,提供开启事务、提交事务、回滚事务等操作。

DBUtil
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package org.smart4j.framework.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.helper.ConfigHelper;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* Created by Roger on 2016/11/30.
*/
public final class DBUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(DBUtil.class);
// 用于放置数据库连接的局部线程变量
private static ThreadLocal<Connection> connContainer = new ThreadLocal<>();
/**
* 获取数据库连接
*
* @return
*/
public static Connection getConnection() {
Connection conn = connContainer.get();
if (conn == null) {
try {
Class.forName(ConfigHelper.getJdbcDriver());
conn = DriverManager.getConnection(ConfigHelper.getJdbcUrl(), ConfigHelper.getJdbcUsername(), ConfigHelper.getJdbcPassword());
} catch (Exception e) {
LOGGER.error("database get connection error", e);
throw new RuntimeException(e);
} finally {
connContainer.set(conn);
}
}
return conn;
}
/**
* 关闭数据库连接
*/
public static void closeConnection() {
Connection conn = connContainer.get();
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
LOGGER.error("database close connection error", e);
throw new RuntimeException(e);
} finally {
connContainer.remove();
}
}
}
/**
* 开启事务
*/
public static void beginTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.setAutoCommit(false);
} catch (Exception e) {
LOGGER.error("database begin transaction error", e);
throw new RuntimeException(e);
}
}
}
/**
* 提交事务
*/
public static void commitTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.commit();
} catch (Exception e) {
LOGGER.error("database commit transaction error", e);
throw new RuntimeException(e);
} finally {
closeConnection();
}
}
}
/**
* 回滚事务
*/
public static void rollbackTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.rollback();
} catch (Exception e) {
LOGGER.error("database rolleback transaction error", e);
throw new RuntimeException(e);
} finally {
closeConnection();
}
}
}
}

事务切面类

TransactionProxy类实现Proxy接口,在doProxy()方法中完成事务控制的相关逻辑。

内部定义一个FLAG_HOLDER的本地线程变量,用于保证同一线程中事务控制相关逻辑只会执行一次。

使用@Transaction注解的方法才会执行事务相关逻辑。

TransactionProxy
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
package org.smart4j.framework.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Transaction;
import org.smart4j.framework.util.DBUtil;
import java.lang.reflect.Method;
/**
* Created by Roger on 2016/11/30.
*/
public class TransactionProxy implements Proxy {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class);
// 保证同一线程中事务控制相关逻辑只会执行一次
private static final ThreadLocal<Boolean> FLAG_HOLDER = new ThreadLocal<Boolean>(){
@Override
protected Boolean initialValue() {
return false;
}
};
@Override
public Object doProxy(ProxyChain proxyChain) throws Throwable {
Object result;
boolean flag = FLAG_HOLDER.get();
Method method = proxyChain.getTargetMethod();
if (!flag && method.isAnnotationPresent(Transaction.class)){
FLAG_HOLDER.set(true);
try {
DBUtil.beginTransaction();
LOGGER.debug("begin transaction");
result = proxyChain.doProxyChain();
DBUtil.commitTransaction();
LOGGER.debug("commit transaction");
} catch (Exception e) {
DBUtil.rollbackTransaction();
LOGGER.debug("rollback transaction");
throw e;
} finally {
FLAG_HOLDER.remove();
}
}else {
result = proxyChain.doProxyChain();
}
return result;
}
}

框架中添加事务机制

AopHelper类中createProxyMap()方法中添加事务代理机制addTransactionProxy(),之前AOP部分只是添加了普通的切面代理。