2016-12-19 85 views
0

我遇到了Spring回滾事務的問題。我有一種方法可以創建插入多個表格的新訂單(usersorder,order_product ...)。如果在方法結束之前有異常,則回滾啓動並刪除order_product中的記錄,但users記錄仍然存在。我想刪除在我的數據庫中生成的所有記錄。 users id字段(主鍵)由MySQL 5.6中的Autoincrement生成。而order_product的主鍵是兩個外鍵。當「執行標識立即插入」時,Spring不會回滾

用戶表:

CREATE TABLE `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `email` varchar(255) NOT NULL, 
    `new_email` varchar(255) DEFAULT NULL, 
    `allowed_newsletter` bit(1) NOT NULL DEFAULT b'1', 
    `lastname` varchar(255) DEFAULT NULL, 
    `phone` varchar(255) DEFAULT NULL, 
    `lang` enum('en','de','fr','es') DEFAULT 'es', 
    `creation_date` datetime DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `IDX_EMAIL` (`email`) USING BTREE 
) ENGINE=InnoDB AUTO_INCREMENT=1092 DEFAULT CHARSET=latin1; 

order_product表:

CREATE TABLE `order_product` (
    `product_id` int(11) NOT NULL, 
    `order_id` int(11) NOT NULL, 
    `detail_json` longtext, 
    `shipping_date` datetime DEFAULT NULL, 
    `order_product_status` enum('PaymentPending','ShippingPending','Sent','Cancelled') NOT NULL DEFAULT 'PaymentPending', 
    `is_downloaded` bit(1) NOT NULL DEFAULT b'0', 
    PRIMARY KEY (`product_id`,`order_id`), 
    KEY `IDX_OP_PRODUCT_ID` (`product_id`) USING BTREE, 
    KEY `IDX_OP_ORDER_ID` (`order_id`) USING BTREE, 
    CONSTRAINT `FK_OP_ORDER_ID` FOREIGN KEY (`order_id`) REFERENCES `order_customer` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `FK_OP_PRODUCT_ID` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

我使用MySQL 5.6我的Hibernate相關的4和Spring 4

日誌文件:

[annotation.AnnotationTransactionAttributeSource(getTransactionAttribute:108)] Adding transactional method 'UsersDAOImpl.insertUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 
[jpa.JpaTransactionManager(doGetTransaction:334)] Found thread-bound EntityManager [[email protected]] for JPA transaction 
[jpa.JpaTransactionManager(handleExistingTransaction:476)] Participating in existing transaction 
[spi.ActionQueue(addResolvedEntityInsertAction:213)] Executing identity-insert immediately 
[hibernate.SQL(logStatement:104)] 
    insert 
    into 
     users 
     (allowed_newsletter, email, lang, lastname, name, new_email, phone) 
    values 
     (?, ?, ?, ?, ?, ?, ?) 
2016-12-19 09:47:46,046 DEBUG (http-bio-8080-exec-3) [id.IdentifierGeneratorHelper(getGeneratedIdentity:93)] Natively generated identity: 1091 
2016-12-19 09:47:46,047 DEBUG (http-bio-8080-exec-3) [spi.ActionQueue(addResolvedEntityInsertAction:213)] Executing identity-insert immediately 
..... 
... MORE INSERTs - SELECTs - etc ... 
..... 
[annotation.AnnotationTransactionAttributeSource(getTransactionAttribute:108)] Adding transactional method 'OrderProductServiceImpl.addNewOrderProduct' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 
[jpa.JpaTransactionManager(doGetTransaction:334)] Found thread-bound EntityManager [[email protected]] for JPA transaction 
[jpa.JpaTransactionManager(handleExistingTransaction:476)] Participating in existing transaction 
[annotation.AnnotationTransactionAttributeSource(getTransactionAttribute:108)] Adding transactional method 'OrderProductDAOImpl.addNewOrderProduct' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 
[jpa.JpaTransactionManager(doGetTransaction:334)] Found thread-bound EntityManager [[email protected]] for JPA transaction 
[jpa.JpaTransactionManager(handleExistingTransaction:476)] Participating in existing transaction 
[internal.AbstractSaveEventListener(saveWithGeneratedId:130)] Generated identifier: component[orderId,productId]{orderId=144, productId=2553}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator 
[writers.HstsHeaderWriter(writeHeaders:130)] Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]1d011fbb 
[context.HttpSessionSecurityContextRepository(saveContext:352)] SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. 
[internal.SessionImpl(disconnect:566)] Disconnecting session 
[internal.LogicalConnectionImpl(releaseConnection:232)] Releasing JDBC connection 
[internal.LogicalConnectionImpl(releaseConnection:250)] Released JDBC connection 
[jpa.JpaTransactionManager(processRollback:851)] Initiating transaction rollback 
[jpa.JpaTransactionManager(doRollback:538)] Rolling back JPA transaction on EntityManager [[email protected]] 
[spi.AbstractTransactionImpl(rollback:203)] rolling back 
[jdbc.JdbcTransaction(doRollback:164)] rolled JDBC Connection 
[jdbc.JdbcTransaction(releaseManagedConnection:126)] re-enabling autocommit 
[internal.JdbcCoordinatorImpl(close:173)] HHH000420: Closing un-released batch 
[jpa.JpaTransactionManager(doCleanupAfterCompletion:600)] Closing JPA EntityManager [[email protected]] after transaction 
[jpa.EntityManagerFactoryUtils(closeEntityManager:435)] Closing JPA EntityManager 
[internal.LogicalConnectionImpl(releaseConnection:232)] Releasing JDBC connection 
[internal.LogicalConnectionImpl(releaseConnection:250)] Released JDBC connection 
編輯:
@Controller 
public class OrderController { 
    @ResponseStatus(value = HttpStatus.ACCEPTED) 
    @Transactional 
    public void finishPayment(...) { 
     //Call to service methods 
    } 
} 

@Service 
@Transactional 
public class UsersServiceImpl implements UsersService { 
    public UsersEntity registerNewGuest(String username, String email, MCountryEntity countryEntity, String nickname, String lang) { 
     // Insert User 
    } 
} 

@Service 
@Transactional 
public class OrderProductServiceImpl implements OrderProductService { 
    @Override 
    public void addNewOrderProduct(OrderProductEntity orderProductEntity) { 
     // Insert OrderProduct 
    } 
} 
+0

請也張貼相關的代碼。看起來你有一個'@ Transactional'用於每個數據庫插入/更新。在這種情況下,你瞄準什麼,不會工作 –

+0

我添加了Java代碼。 – Miguel

回答

0

這裏您正在執行每個@Transactional方法的每個數據庫插入/更新步驟。此批註在該方法執行的開始時創建一個Db Transaction會話,並在方法執行結束時進行提交。

在這裏,你的每個數據庫插入是在不同的數據庫會話。如果您想在發生運行時異常時回滾所有數據庫更新,那麼所有這些事務應該位於同一個會話中。但這不是。這是你的問題的原因。

你可以做的是,把所有的代碼,用@Transactional

我會建議一個模式註解一個方法裏面..

  1. 做一個DAO類層,其方法將直接連接到數據庫(假設)並在其中查詢。使用@Repository來註釋該類。
  2. 使用帶有@Service註解的類用於使用@Transactional註釋的方法。因此,這些方法中的每一個將在相同的跨國會話。現在,您可以使用相同的服務方法調用許多DAO方法。如果這些DAO方法中有任何一個具有運行時異常,則所有內容都將回滾。

內容:將一個單一的方法與@Transactional執行期間所有的數據庫查詢,以自動回滾,如果發生運行時異常。

注意:你可以把@Transactional,方法級別或類級別

+0

謝謝你的回答。我要研究/開發這個解決方案,如果它是正確的,我會接受答案。 – Miguel

0

@Service 
 
@Transactional(rollbackFor = Exception.class)

使用上述註釋回滾任何例外