猿问

Spring @Transactional-隔离,传播

有人可以通过实际示例解释注释中的隔离和传播参数@Transactional吗?


基本上,何时和为什么我应该选择更改其默认值。


慕尼黑8549860
浏览 228回答 3
3回答

天涯尽头无女友

好的问题,尽管不是一个简单的答案。传播定义事务之间的关系。常用选项:Required:代码将始终在事务中运行。创建一个新事务或重用一个事务(如果有)。Requires_new:代码将始终在新事务中运行。如果存在当前事务,则将其挂起。隔离定义事务之间的数据契约。Read Uncommitted:允许脏读。Read Committed:不允许脏读。Repeatable Read:如果在同一事务中两次读取一行,结果将始终相同。Serializable:按顺序执行所有事务。在多线程应用程序中,不同的级别具有不同的性能特征。我认为,如果您了解dirty reads概念,便可以选择一个不错的选择。何时发生脏读的示例:  thread 1   thread 2            |         |    write(x)    |      |         |      |        read(x)      |         |    rollback    |      v         v            value (x) is now dirty (incorrect)因此,可以设置一个合理的默认值(如果可以要求的话)Read Committed,它只能让您读取传播级别为的其他正在运行的事务已提交的值Required。然后,如果您的应用程序有其他需求,则可以从那里开始。一个实际的示例,该示例在进入provideService例程时始终在其中创建新事务,而在离开时总是在其中完成:public class FooService {    private Repository repo1;    private Repository repo2;    @Transactional(propagation=Propagation.REQUIRES_NEW)    public void provideService() {        repo1.retrieveFoo();        repo2.retrieveFoo();    }}如果我们改为使用Required,则在进入例程时如果事务已经打开,则事务将保持打开状态。还要注意,a的结果rollback可能会有所不同,因为多次执行可能会参与同一事务。我们可以通过测试轻松验证行为,并查看结果随传播级别的不同:@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations="classpath:/fooService.xml")public class FooServiceTests {    private @Autowired TransactionManager transactionManager;    private @Autowired FooService fooService;    @Test    public void testProvideService() {        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());        fooService.provideService();        transactionManager.rollback(status);        // assert repository values are unchanged ... }传播水平为Requires new:我们希望fooService.provideService()它不会回滚,因为它创建了它自己的子事务。Required:我们希望一切都回滚而后备存储保持不变。

阿晨1998

关于其他参数的足够解释由其他答案给出;但是,您要求提供一个真实的示例,以下示例阐明了不同传播选项的目的:假设您负责实施注册服务,在该服务中向用户发送确认电子邮件。您想到了两个服务对象,一个用于注册用户,另一个用于发送电子邮件,后者在第一个中被称为。例如这样的事情:/* Sign Up service */@Service@Transactional(Propagation=REQUIRED)class SignUpService{ ... void SignUp(User user){    ...    emailService.sendMail(User); }}/* E-Mail Service */@Service@Transactional(Propagation=REQUIRES_NEW)class EmailService{ ... void sendMail(User user){  try{     ... // Trying to send the e-mail  }catch( Exception) }}您可能已经注意到第二个服务的传播类型为REQUIRES_NEW,而且有可能引发异常(SMTP服务器关闭,电子邮件无效或其他原因)。您可能不希望整个过程回滚,例如从数据库或其他事物中删除用户信息;因此,您在单独的事务中调用第二个服务。回到我们的示例,这一次您担心数据库的安全性,因此您可以通过以下方式定义DAO类:/* User DAO */@Transactional(Propagation=MANDATORY)class UserDAO{ // some CRUD methods}这就意味着无论何时创建DAO对象,从而可能创建对db的访问,我们都需要确保从内部服务中进行调用,这意味着应该存在一个实时事务。否则将发生异常。因此,传播的类型为MANDATORY。
随时随地看视频慕课网APP

相关分类

Java
我要回答