我一直在努力在非常老的Java + Spring MVC代码库中修复SQL注入,该代码库在DAO层上有数百个类,目前正在使用java.sql.PreparedStatement
和java.sql.Connection
。
数据库连接隔离级别,数据库连接提交和连接回滚是使用-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类方法中创建的,在那里设置了隔离级别并传递给该方法(在另一个DAO类中),
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
}
}
此方法的调用者来自不同的类,并且调用层次结构通常是多个级别的,即在两个或三个以上级别创建了Connection对象,并在那里设置了隔离。
例如在DAOClass1.method()中创建连接,并将其传递到DAOClass3的getReportName之上。
DAOClass1.method()-> DAOClass2.method(连接)-> DAOClass3.getReportName(连接)
通过引入@Transactional
和JdbcTemplate
组合可以重新设计这种情况吗?我将仅在创建了@Transactional
的呼叫发起方还是在此方法上应用Connection
?
我想,这更多的是事务传播情况,但有点困惑。
我的问题与下面的问题#1重复,但需要针对我的特定情况的解决方案。
Related Question 1 - Variable transaction isolation levels by request
Related Question 2 - How can I get a spring JdbcTemplate to read_uncommitted?
答案 0 :(得分:2)
我对这里是否需要动态隔离级别的前提感到怀疑。当隔离级别有所不同时,这不是因为特定数据有关,而是语句本身组合的方式成为问题。似乎不太可能出现这样的情况:用一组数据调用的方法应该具有一个隔离级别,而用另一组数据调用的相同方法应该经过另一个隔离级别。
我猜测遗留代码的核心问题是没有事务性服务层的概念。相反,您有一个由控制器直接调用的数据访问对象的杂物箱,并且似乎随意地指定了隔离级别。
指定事务详细信息(如隔离级别)需要在服务级别完成。我将仔细研究控制器,将它们在Web层所做的工作与业务逻辑分开,并将业务逻辑下推到可以用隔离级别等内容进行注释的服务方法中。
一旦这样做,您将拥有可以在不同服务中重用的DAO,其中特定服务中提供了隔离级别,并且可以删除所有获取连接,捕获异常和关闭jdbc资源的代码。