3 回答
TA贡献1793条经验 获得超6个赞
好的问题,尽管不是一个简单的答案。
传播
定义事务之间的关系。常用选项:
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:我们希望一切都回滚而后备存储保持不变。
TA贡献1865条经验 获得超7个赞
关于其他参数的足够解释由其他答案给出;但是,您要求提供一个真实的示例,以下示例阐明了不同传播选项的目的:
假设您负责实施注册服务,在该服务中向用户发送确认电子邮件。您想到了两个服务对象,一个用于注册用户,另一个用于发送电子邮件,后者在第一个中被称为。例如这样的事情:
/* 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。
添加回答
举报