Can handle transaction là gì
When you buy something from Google, you’ll find a charge on your account that starts with "GOOGLE*" and ends with the Google product or another descriptor. Show Here’s how some common Google purchases might appear on your bank statement. Tip: Some of these descriptors may appear shortened. Statement itemGoogle product GOOGLE *Ads Chile only Google AdsGOOGLE *ADWS*DL Colombia only Google AdsGOOGLE *{Company}Google Play Store for AppsGOOGLE *CLOUD_{BAID}Google CloudGOOGLE *Commerce LtdGoogle Play MusicGOOGLE *{Developer}Google Play Store for AppsGOOGLE PLAY JAPAN Japan only Google Play Store for AppsGOOGLE *DevicesGoogle StoreGOOGLE *DomainsGoogle DomainsGOOGLE *GOOGLEYouTube PremiumGOOGLE *Google, Inc.Google Play MusicGOOGLE *Google MusicGoogle Play MusicGOOGLE *Google PlayGoogle Play Movies & TVGOOGLE *Google StorageGoogle DriveGOOGLE *Google StoreGoogle StoreGOOGLE *Google SurveysGoogle AnalyticsGOOGLE *GoogleShoppingGoogle ShoppingGOOGLE *MusicGoogle Play MusicGOOGLE* Google StorageGoogle OneGOOGLE *Play Store Chile only Google Play Store for AppsGOOGLE *Youtube Chile only YouTube Premium YouTube Music YouTube Member YouTube Super GOOGLE *PLAY-YOUTUBE*DL Colombia only Google Play Store for Apps YouTube Premium YouTube Music YouTube Member YouTube SuperGOOGLE *Play CreditGoogle Play gift cards and other transfers to a Google Play balanceGOOGLE *Play NewsstandGoogle Play NewsstandGOOGLE *PROJECT FIGoogle Project FiGOOGLE *SERVICESGoogle FiberYouTube TVGOOGLE WORKSPACE {first_7_letters_of_domain_name}Google WorkspaceGOOGLE STORE {store_location}Google StoreGOOGLE *VoiceGoogle VoiceGOOGLE *WALLETGoogle WalletGOOGLE *YouTube VideosYouTube Movies Pending transactionsYou may find a charge on your account with the descriptor GOOGLE *TEMPORARY HOLD. It may be cut short on your bank statement. This is a pending charge for a transaction that hasn’t been processed yet. When the transaction goes through, it’ll go away and you won’t be charged. Find Google Pay charges for non-Google productsWhen you buy from a retailer other than Google through Google Pay, the charge will appear with the retailer’s brand. If you have questions about the charge, contact the retailer. The Buy/Sell orders, Buy/Selldocumentsby any other means(if any), transaction documents and other relateddocumentsexecuted by Client or Client's authorized party are an integral part of the Client Service Agreement and therefore binding on Client's responsibility. Sapo API cho phép bạn thực hiện các thao tác sau với tài nguyên Transaction. Các phiên bản chi tiết hơn của những thao tác này có thể có:
Các thuộc tính của Transactionamount
Số lượng tiền có trong Transaction. authorization
Mã xác thực ứng với Transaction. created_on
Thời gian Transaction được tạo. API trả về kết quả theo định dạnh chuẩn ISO 8601. device_id
Số duy nhất định danh cho thiết bị. gateway
Tên của cổng thanh toán giao dịch được thực hiện thông qua nó. 0 1Nguồn thực hiện giao dịch. Giá trị này được thiết lập bởi Sapo và không thể ghi đè. Các giá trị có thể là: 'web', 'pos', 'iphone', 'android' Spring và Spring Data hỗ trợ việc quản lý transaction khiến cho việc này cực kỳ đơn giản, tất cả những gì chúng ta cần làm là chú thích một class hay một method với @Transactional annotation. Nhưng thật sự bên dưới nó đang làm gì? những method nào nên được chú thích bằng @Transactional? Và tại sao bạn có thể đặt propagation ở các mức độ khác nhau? Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu các câu hỏi được đặt ra ở trên. Nhưng trước khi tìm hiểu về transaction trong Spring, chúng ta cần nhớ lại kiến thức một chút về transaction, quản lý transaction trong JDBC. Bởi vì transaction trong Spring cũng chỉ dựa trên database và JDBC mà thôi. Transaction là gì?Transaction quản lý những thay đổi mà bạn thực hiện trong một hoặc nhiều hệ thống, nó có thể database, message brokers, hoặc bất kỳ loại hệ thống phần mềm nào khác. Mục tiêu chính của giao dịch là cung cấp các đặc điểm ACID để đảm bảo tính nhất quán và hợp lệ của dữ liệu của bạn. ACID transactionsVốn dĩ một transaction được đặc trưng bởi 4 yếu tố (thường được gọi là ACID):
Quản lý transaction trong JDBCCó 3 thao tác chính bạn có thể thực hiện thông qua java.sql.Connection để kiểm soát transaction trên database. try (Connection con = dataSource.getConnection()) { con.setAutoCommit(false); // do something ... con.commit(); } catch (SQLException e) { con.rollback(); } Bạn phải:
Như bạn có thể thấy, quản lý một transaction không phải quá khó, nhưng để triển khai chúng trong một ứng dụng lớn thì không dễ như vậy, điều đầu tiên chúng ta có thể thấy đó là việc lặp lại lặp lại code quá nhiều khi start transaction, commit và rollback. Chưa kể trong ứng dụng lớn chứa nhiều logic thì có lẽ sẽ rối tung mù khi mà chi chích những dòng code commit, rollback như thế kia. Đó là lý do tại sao Spring cung cấp cơ chế hỗ trợ quản lý transaction, giúp chúng ta chú tâm hơn vào business thay vì phải xử lý transaction cách thủ công như trên. Quản lý transaction trong SpringNhư đã thảo luận ở trên, chúng ta có thể thấy rằng việc quản lý transaction trong JDBC khiến chúng ta phải lặp đi lặp đi các công việc như start, commit, rollback transaction. Spring cung cấp cơ chế hỗ trợ quản lý transaction tự động start, commit, hay rollback transaction tự động. Ngoài ra, nó cũng có thể tích hợp với Hibermate, JPA transaction. Nếu các bạn đang sử dụng Spring Boot, chỉ cần chú thích một class, method hay interface với @Transactional annotation thì chúng sẽ được thực thi bên trong một transaction. NOTE: Nếu bạn đang sử dụng Spring Boot và có spring-data hay spring-tx dependency thì cơ chế quản lý transaction của Spring sẽ được kích hoạt mặc định. Nếu bạn đang sử dụng Spring mà không phải Spring Boot thì các bạn cần phải cấu hình với @EnableTransactionManagement để kích hoạt tính năng quản lý transaction trong Spring. @Transactional annotationDưới đây là một đoạn mã của một service có chứa một method được chú thích với @Transactional annotation. public interface AuthorService { void updateAuthorName(Long id, String name); } package com.deft.transactionexample; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class AuthorServiceImpl implements AuthorService { @Autowired private AuthorRepository authorRepository; @Override @Transactional public void updateAuthorName(Long id, String name) { Author author = authorRepository.findById(id).get(); author.setName(name); } } @Transactional annotation sẽ nói với Spring rằng updateAuthorName() method cần được thực thi bên trong một transaction. Khi bạn sử dụng AuthorService ở một nơi nào nó, chẳng hạn như ở các controller class, Spring sẽ tạo ra một proxy object bao bọc AuthorService object và cung cấp các đoạn mã cần thiết để bắt đầu một transaction. Mặc định, proxy sẽ start một transaction trước khi có một yêu cầu đến method được chú thích với @Transactional annotation. Sau khi method thực thi xong, proxy sẽ commit hoặc rollback transaction nếu có một RuntimeException hoặc Error xảy ra trong quá trình thực thi. Mọi thứ xảy ra ở giữa, chỉ là các đoạn mã code thực thi logic business do chính chúng ta viết. @Transactional annottion còn hỗ trợ cho chúng ta tuỳ biến một các hành vi của một transaction thông qua một số thuộc tính quan trọng như propagation, readOnly, rollbackFor, noRollbackFor. Transaction PropagationSpring cung cấp 7 tuỳ biến cho Propagation trong @Transactional annotation như sau: REQUIREDREQUIRED – Nói với Spring rằng nếu có một transaction đang hoạt động thì nó sẽ sử dụng chung, nếu không có transaction nào đang hoạt động, method được gọi sẽ tạo một transaction mới. Đây là giá trị mặc định của Propagation. @RestController @RequestMapping(value = "/author") public class AuthorController { @Autowired private AuthorService authorService; @PutMapping("/{id}") public void updateAuthorName(@PathVariable Long id, @RequestParam String name) { authorService.updateAuthorName(id, name); } } Như trong ví dụ trên, AuthorController gọi đến authorService.updateAuthorName() không bao gồm 1 transaction, vì thế trong AuthorService#updateAuthorName() sẽ tạo ra một transaction mới để thực thi. Output Kết quả chúng ta có thể thấy tạo controller, mình đã sử dụng TransactionAspectSupport để lấy thông tin của transaction hiện tại, nhưng kết quả trả về NULL, nghĩa là tại controller không có transaction nào đang hoạt động. Sau khi đi vào AuthorServiceImpl thì chúng ta có thể thấy nó đã khởi tạo một transaction mới dùng để thao tác với database. SUPPORTSSUPPORTS – Chỉ đơn giản là sử dụng lại transaction hiện đang hoạt động. Nếu không thì method được gọi sẽ thực thi mà không được đặt trong một transactional context nào. @Override @Transactional(propagation = Propagation.SUPPORTS) public void updateAuthorNameSupport(Long id, String name) { Author author = authorRepository.findById(id).orElse(null); author.setName(name); } Ví dụ trên chúng ta có thể lấy được một author trong database, tuy nhiên giá trị name mới được thay thế sẽ không được commit xuống database vì những hoạt động này không được đặt trong transactional context nào, vì vậy JPA sẽ không trigger những thay đổi và đồng bộ xuống database. MANDATORYMANDATORY yêu cầu phải có một transaction đang hoạt động trước khi gọi, nếu không method được gọi sẽ ném ra một exception. @Override @Transactional(propagation = Propagation.MANDATORY) public void updateAuthorNameMandatory(Long id, String name) { Author author = authorRepository.findById(id).orElse(null); author.setName(name); } Như vậy khi bạn gọi updateAuthorNameMandatory() từ controller sẽ nhận lại một exception vì ở controller không có transaction nào được khởi tạo và hoạt động. @PutMapping("/{id}") public void updateAuthorName(@PathVariable Long id, @RequestParam String name) { authorService.updateAuthorNameMandatory(id, name); } Output org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' NEVERNEVER sẽ ném một exception nếu method được gọi trong một transaction hoạt động. NOT_SUPPORTEDDừng transaction hiện tại và thực thi method mà không thuộc một transaction nào. REQUIRES_NEWĐể luôn bắt đầu một transaction mới cho method được gọi. Nếu method được gọi với một transaction đang hoạt động, transaction đó sẽ bị tạm ngưng, một transaction mới sẽ được tạo và sử dụng cho method này. Transaction mới vừa được tạo sẽ thực thi độc lập với transaction bên ngoài, khi transaction này kết thúc dữ liệu sẽ được đồng bộ xuống database. Sau đó transaction bên ngoài sẽ được kích hoạt và hoạt động trở lại. AuthorServiceAImpl { @Override @Transactional public void updateAuthorNameRequireNew(Long id, String name) { Author author = authorRepository.findById(id).orElse(null); updateAnotherAuthor(2L, "new name"); author.setName(name); throw new RuntimeException("no way"); } } AuthorServiceBImpl { @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateAnotherAuthor(Long id, String name) { Author author = authorRepository.findById(id).orElse(null); author.setName(name); } } Như ví dụ trên, updateAnotherAuthor() được thực thi trong một transaction mới độc lập với transaction bên ngoài nên các thay đổi bên trong nó sẽ được đồng bộ xuống database bất kể updateAuthorNameRequireNew() xảy ra exception. Vì updateAuthorNameRequireNew() xảy ra exception nên các thay đổi dữ liệu sẽ không được đồng bộ xuống database. NESTEDMethod được gọi sẽ tạo một transaction mới nếu không có transaction nào đang hoạt động. Nếu method được gọi với một transaction đang hoạt động Spring sẽ tạo một savepoint và rollback tại đây nếu có Exception xảy ra. Read-Only transactionThuộc tính readOnly trong @Transactional annotation gây sự nhầm lẫn cho nhiều người đặc biệt là khi làm việc với JPA, trong Java docs của nó mô tả như sau:
Hiểu đơn giản nghĩa là chúng ta không thể chắc chắn sẽ có các hoạt động INSERT hay UPDATE dữ liệu xảy ra trong một transacion được chú thích với readOnly. Hành vi này phụ thuộc vào nhà cung cấp các JPA implemetation (Như Hibernate, EclipseLink, etc) trong khi JPA không thể quản lý việc này từ nhà cung cấp. Cũng cần hiểu rằng thuộc tính readOnly chỉ có liên quan trong một transaction. Nếu một hoạt động xảy ra bên ngoài ngữ cảnh của transaction, readOnly sẽ bị bỏ qua. Từ non-transactional context – một transaction sẽ không được tạo và thuộc tính readOnly sẽ bị bỏ qua. Tuy nhiên, kể từ Spring 5.1 thuộc tính readOnly trong Hibernate sẽ giúp chúng ta tránh khởi các kiểm tra trên các thực thể cần truy xuất, từ đó có thể tối ưu hiệu xuất. @Override @Transactional(readOnly = true) public Author getAuthorById(Long id) { Author author = authorRepository.findById(id).orElse(null); return author; } Handling ExceptionsNhư đã thảo luận ở phần trước, Spring proxy sẽ tự động rollback transaction nếu có một RuntimeException xảy ra. Bạn có thể tùy biến bằng cách sử dụng thuộc tính rollbackFor và noRollbackFor của @Transactional annotation. Chúng ta có thể phỏng đoán ý nghĩa của chúng từ tên như thuộc tính rollbackFor cho phép bạn cung cấp một mảng các Exception class mà transaction sẽ bị rollback nếu chúng xảy ra. Và noRollbackFor được dùng để chỉ định một mảng các Exception class mà transaction sẽ không rollback khi chúng xảy ra. Trong ví dụ sau, mình muốn rollback transaction cho tất cả các sub-class của Exception ngoại trừ EntityNotFoundException public interface AuthorService { void updateAuthorName(Long id, String name); }0 Kết bàiSpring và Spring Data JPA cung cấp cơ chế quản lý transaction giúp chúng ta dễ dàng hơn trong việc xử lý transaction. Bạn chỉ cần chú thích @Transactional annotation trên các interface, class, method và Spring sẽ bao bao ngoài chúng một proxy giúp chúng ta thực thi các thao tác tự động như start, commt hay rollback transaction. Ngoài ra bạn có thể sử dụng readOnly flag để tối ưu hóa hiệu xuất cho các method chỉ thực thi các hoạt động đọc dữ liệu từ database. Mặc định thì transaction chỉ được rollback khi có RuntimeException xảy ra. Bạn có thể tùy biến điều này với thuộc tính rollbackFor trong @Transactional annotation. Nếu muốn transaction rollback cho tất cả các lỗi xảy ra thì có thể sử dụng @Transactional (rollbackFor = Throwable.class) Sau cùng để có thể hiểu và thực hành lý thuyết thông qua bài viết trên, các bạn có thể tham khảo mã nguồn được mình công khai trên gitlab: transaction-example |