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。
添加回答
舉報