Java 在应用程序代码(如@Transactional)中设置隔离级别与在db服务器上配置的隔离级别之间有什么关系?
我很难把所有的信息拼凑在一起。让我尽可能详细地解释一下 在SpringJDBC、SpringTransaction和MySQLDB应用程序上工作时,我希望能够在选择更新时锁定表中的数据行。原因是,我有在不同阶段转换数据的业务逻辑。所以,如果应用程序实例(在服务器1上)拾取记录进行处理,那么它不应该被另一个实例拾取。这有点类似于。但我不能接受这个答案,原因与OP对这个问题的回答相同 因此,在仔细检查并了解如何使用jdbcTemplate配置事务管理之后,我的spring应用程序设置如下(仅相关部分): applicationContext.xmlJava 在应用程序代码(如@Transactional)中设置隔离级别与在db服务器上配置的隔离级别之间有什么关系?,java,mysql,spring-jdbc,spring-transactions,transaction-isolation,Java,Mysql,Spring Jdbc,Spring Transactions,Transaction Isolation,我很难把所有的信息拼凑在一起。让我尽可能详细地解释一下 在SpringJDBC、SpringTransaction和MySQLDB应用程序上工作时,我希望能够在选择更新时锁定表中的数据行。原因是,我有在不同阶段转换数据的业务逻辑。所以,如果应用程序实例(在服务器1上)拾取记录进行处理,那么它不应该被另一个实例拾取。这有点类似于。但我不能接受这个答案,原因与OP对这个问题的回答相同 因此,在仔细检查并了解如何使用jdbcTemplate配置事务管理之后,我的spring应用程序设置如下(仅相关部分
...
...
<context:annotation-config />
...
<context:component-scan base-package="org.foo.bar"/>
...
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
...
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://dbserver:3306" />
<property name="username" value="foo" />
<property name="password" value="baz" />
<property name="initialSize" value="10" />
<property name="maxTotal" value="20"/>
</bean>
如果不更改这些设置,我无法获取SELECT。。。使更新
生效
一旦我手动测试了它,我想看看这在我的应用程序代码运行时是否有任何影响。因此,当我保持一个mysql命令行会话处于活动状态时,我启动了我的应用程序,并在dbAccessService.updateRecord()处设置了一个断点代码>行。然后在另一个会话中,我尝试选择这一行(没有selectforupdate
,只是一个简单的select
)语句。现在我可以看到,由于应用程序代码启动的上一个事务,该行被锁定
问题:
要通过spring事务实现行锁定(SELECT…FOR UPDATE
behavior),我必须更改数据库服务器上的设置吗?比如隔离级别和自动提交
假设MySQL服务器在默认情况下运行(隔离级别=可重复读取,自动提交=1)。现在这还能用吗?也就是说,若我像上面所做的那个样在spring的目标方法中配置事务,那个么应用程序的一个实例正在读取的一行是否会保持锁定状态,以便尝试读取该行的任何其他实例都会被阻止
我们在@Transactional
注释上添加的配置如何影响数据库?我知道我们可以为不同的作用域设置事务特征,如的表13.9所述。下面是我对这一切如何运作的想象或猜测。对吗
这一切是如何运作的
- 用@Transactional注释的类/方法将创建一个代理对象,以便添加所有事务性内容以及用户实现的业务逻辑
- 获取数据库连接并创建数据库会话
- 对于此会话,根据
@Transactional
中提供的配置,设置了适当的隔离级别(不知道自动提交会受到怎样的影响)
- 事务完成后,会话关闭。创建的任何新会话都将相应地设置隔离特性
- 有没有更好的方法让我达到想要的
SELECT。。。对于spring jdbc、spring tx的更新
功能?我可能会引入我自己的锁定机制,比如引入一个新的布尔列,比如selected\u for\u processing
,而不是选择,通过设置selected\u for\u processing
列来更新要选择的记录。在下一步中,我将根据此标志和另一个状态标准进行选择。因此,这样一条记录只会被处理一次。但我想知道是否有一种传统的或配置的方式来实现这一点?我不能使用JPA或任何spring-data-x库。spring数据库使用springframework 5.x.x或更高版本,但我一直使用springframework 4.2.3,因此使用SpringJDBC和spring-tx
产生影响的是自动提交。正常自动提交处于启用状态(每个查询本身就是一个事务),要使用事务,您需要在代码中使用BEGIN
或START transaction
,执行事务中需要执行的操作,然后显式调用commit
1) 服务器上的事务隔离设置将更改服务器上的默认设置。但您可以在开始事务之前在本地会话中更改它们,而无需更改服务器范围的默认值
2) 是的,不同会话之间的tx_隔离可能不同
3) 我不确定我是否能完全回答这个问题,因为它似乎是针对Spring的,我在这方面不太流利。但是,一般来说,Spring在没有显式锁定整个表的情况下,无法确保或实现数据库级别的事务性和锁定,因此它最终只能使用底层数据库功能,试图比底层数据库更聪明,必然会带来巨大的性能和并发性损失。必须关闭自动提交才能锁定工作
如果启用,则每个SQL语句都是它自己的事务。因此,SELECT语句将获取锁,但在执行SELECT之后,在运行update语句之前,它将立即释放锁。如果您的dbAccessService bean是hibernate DAO,那么它发出的SQL语句可能是一个all columns更新,类似于放入REST(而不是补丁)的行为。这可能导致现有的更新被完全替换
Tx A
Tx B
选择id=1的位置进行更新
尝试相同操作,但锁定与tx A一起,因此在等待锁定时被阻止
释放锁(因为自动提交)
获取id=1的select的锁
尝试更新但被阻止(更新隐式获得锁)
释放锁
更新foo SET col_a=val_a,col_b=val_b,status='PROCESSING'
等待锁
更新完成,释放锁
更新foo SET col_a=val_a,col_b=val_b,status='PROCESSING'
谢谢你的回答。关闭“自动提交”听起来有点不切实际否?我的意思是想象一下如果ev
public class TransactionSvcImpl implements TransactionSvc {
@Autowired
DatabaseAccessService dbAccessService;
@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
@Override
public List<Foo> getFooForUpdate() {
// Below call executes a SQL like - "SELECT * FROM some_t WHERE id = 1 FOR UPDATE"
List<foo> foos = dbAccessService.getSomeRecord();
dbAccessService.updateTheRecord(foos, Status.PROCESSING);
return foos;
}
}
SET tx_isolation = 'SERIALIZABLE';
SET AUTOCOMMIT=0;