@Transactional 的动态事务隔离级别
Dynamic transaction isolation levels for @Transactional
我一直在努力修复一个非常古老的 Java + Spring MVC 代码库中的 SQL 注入,在 DAO 层有几百个 classes目前正在使用 java.sql.PreparedStatement
& java.sql.Connection
.
DB 连接隔离级别,DB 连接提交和连接回滚直接在 Connection
对象上使用 - Connection.setIsolationLevel(int isolationLevel)
、Connection.commit()
和 Connection.rollback()
处理。
假设,我有如下方法,
public List<String> getReportName() {
try {
Connection connection = getConnection();
connection.setIsolationLevel(Isolation.READ_UNCOMMITTED); // Just an example
String sql = //ActualSQLString;
PreparedStatement pstmt = connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
......
......
}
} catch (Exception e) {
...
} finally {
//Close connection, statement & result set
}
}
如果我想引入 org.springframework.jdbc.core.JdbcTemplate
代替 java.sql.PreparedStatement
,自动要求我摆脱调用 connection.setTransactionIsolation(isolationLevel)
、提交和回滚,因为 JdbcTemplate 在 DatSource 上工作单个连接对象。所以我改变上面的方法如下。 getJdbcTemplate()
仅用于说明目的,我也可以通过 @Autowired
获得它。此外,此要求与修复 SQL 注入的核心要求无关。
@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
public List<String> getReportName() {
try {
String sql = //ActualSQLString;
return getJdbcTemplate().queryForList(sql);
} catch (Exception e) {
...
}
}
提交和回滚的场景由 rollbackFor
属性处理,如果在上述方法中发生的话。
现在,我对方法签名何时如下所示感到困惑,即在另一个 DAO Class 方法中创建连接,在那里设置隔离级别并传递给此方法(在另一个 DAO class ) ,
public List<String> getReportName(Connection connection) {
try {
String sql = //ActualSQLString;
PreparedStatement pstmt = connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
......
......
}
} catch (Exception e) {
...
} finally {
//Close connection, statement & result set
}
}
上述方法有来自不同 classes 的各种调用者,调用层次结构通常是多个级别,即连接对象是在上面两层或三层创建的,并在那里设置了隔离。
例如连接在 DAOClass1.method() 中创建并传递给 DAOClass3 的上面的 getReportName。
DAOClass1.method() -> DAOClass2.method(连接连接) -> DAOClass3.getReportName(连接连接)
是否可以通过引入 @Transactional
& JdbcTemplate
组合来重新设计此场景?我会仅在创建 Connection
的调用发起者处应用 @Transactional
还是在该方法中应用?
我想,这更像是一个事务传播案例,但有点混乱。
我的问题与以下问题 # 1 重复,但需要针对我的特定情况的解决方案。
Related Question 2 - How can I get a spring JdbcTemplate to read_uncommitted?
我对这里要求动态隔离级别这一前提表示怀疑。当隔离级别产生影响时,这不是因为特定数据的原因,而是语句本身的组合方式才是问题所在。您似乎不太可能会遇到这样一种情况:使用一组数据调用的方法应该具有一个隔离级别,而使用另一组数据调用的相同方法应该在另一个隔离级别下进行。
我猜遗留代码的核心问题是没有事务服务层的概念。相反,您有一个由控制器直接调用的数据访问对象的大杂烩,而且隔离级别似乎是随意指定的。
需要在服务级别指定隔离级别等事务详细信息。我会遍历控制器,将它们所做的 Web 层工作与业务逻辑分开,并将业务逻辑下推到服务方法中,您可以用诸如隔离级别之类的东西进行注释。
一旦你这样做,你将拥有可以在不同服务中重用的 DAO,其中隔离级别在特定服务中给出,所有代码获取连接、捕获异常和关闭 jdbc 资源可以删除。
我一直在努力修复一个非常古老的 Java + Spring MVC 代码库中的 SQL 注入,在 DAO 层有几百个 classes目前正在使用 java.sql.PreparedStatement
& java.sql.Connection
.
DB 连接隔离级别,DB 连接提交和连接回滚直接在 Connection
对象上使用 - Connection.setIsolationLevel(int isolationLevel)
、Connection.commit()
和 Connection.rollback()
处理。
假设,我有如下方法,
public List<String> getReportName() {
try {
Connection connection = getConnection();
connection.setIsolationLevel(Isolation.READ_UNCOMMITTED); // Just an example
String sql = //ActualSQLString;
PreparedStatement pstmt = connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
......
......
}
} catch (Exception e) {
...
} finally {
//Close connection, statement & result set
}
}
如果我想引入 org.springframework.jdbc.core.JdbcTemplate
代替 java.sql.PreparedStatement
,自动要求我摆脱调用 connection.setTransactionIsolation(isolationLevel)
、提交和回滚,因为 JdbcTemplate 在 DatSource 上工作单个连接对象。所以我改变上面的方法如下。 getJdbcTemplate()
仅用于说明目的,我也可以通过 @Autowired
获得它。此外,此要求与修复 SQL 注入的核心要求无关。
@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
public List<String> getReportName() {
try {
String sql = //ActualSQLString;
return getJdbcTemplate().queryForList(sql);
} catch (Exception e) {
...
}
}
提交和回滚的场景由 rollbackFor
属性处理,如果在上述方法中发生的话。
现在,我对方法签名何时如下所示感到困惑,即在另一个 DAO Class 方法中创建连接,在那里设置隔离级别并传递给此方法(在另一个 DAO class ) ,
public List<String> getReportName(Connection connection) {
try {
String sql = //ActualSQLString;
PreparedStatement pstmt = connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
......
......
}
} catch (Exception e) {
...
} finally {
//Close connection, statement & result set
}
}
上述方法有来自不同 classes 的各种调用者,调用层次结构通常是多个级别,即连接对象是在上面两层或三层创建的,并在那里设置了隔离。
例如连接在 DAOClass1.method() 中创建并传递给 DAOClass3 的上面的 getReportName。
DAOClass1.method() -> DAOClass2.method(连接连接) -> DAOClass3.getReportName(连接连接)
是否可以通过引入 @Transactional
& JdbcTemplate
组合来重新设计此场景?我会仅在创建 Connection
的调用发起者处应用 @Transactional
还是在该方法中应用?
我想,这更像是一个事务传播案例,但有点混乱。
我的问题与以下问题 # 1 重复,但需要针对我的特定情况的解决方案。
Related Question 2 - How can I get a spring JdbcTemplate to read_uncommitted?
我对这里要求动态隔离级别这一前提表示怀疑。当隔离级别产生影响时,这不是因为特定数据的原因,而是语句本身的组合方式才是问题所在。您似乎不太可能会遇到这样一种情况:使用一组数据调用的方法应该具有一个隔离级别,而使用另一组数据调用的相同方法应该在另一个隔离级别下进行。
我猜遗留代码的核心问题是没有事务服务层的概念。相反,您有一个由控制器直接调用的数据访问对象的大杂烩,而且隔离级别似乎是随意指定的。
需要在服务级别指定隔离级别等事务详细信息。我会遍历控制器,将它们所做的 Web 层工作与业务逻辑分开,并将业务逻辑下推到服务方法中,您可以用诸如隔离级别之类的东西进行注释。
一旦你这样做,你将拥有可以在不同服务中重用的 DAO,其中隔离级别在特定服务中给出,所有代码获取连接、捕获异常和关闭 jdbc 资源可以删除。