Spring declarative transaction management in the context of real service and unit test rollback, issues needing attention, JPA as an example

Source: Internet
Author: User

How to test transactions and test transaction rollback conditions:

I have done a lot of different tests:


Scenario 1:

The service layer uses annotations @transactional, annotations @persistencecontext private Entitymanager emt;

wrote two ways.

Publicvoid insertfail ()                //insert failed to rollback {for (int i=0;i<20;i++) {User users=new User (); users.setemail ("[Email Protected] "+i); if (i==10) {int a=1/0;} This.emt.persist (users);}} Publicvoid insertsuccess ()             //Normal insert {for (int i=0;i<20;i++) {User users=new User (); users.setemail ("[Email Protected] "+i); this.emt.persist (users);}}


Unit Testing with SPRING-TEST,JUNIT4

Unit tests without @Transactional and @transactionconfiguration (defaultrollback=false), then activate the service with annotations, write two test methods, Is the method of invoking the service directly

@Autowiredprivate uservice usv; @Testpublic void Testshiwu_fail () {this.usv.insertfail ();} @Testpublic void Testshiwu_success () {this.usv.insertsuccess ();}

Final Test Result:

Successful: Hibernate:insert into User (changetime, email, password, registtime, username) values (?, ?, ?, ?, ?) 11:48:43.960 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction commit11:48:43.960 [main] Debug O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]11:48:44.038 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after transaction11     : 48:44.038 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA Entitymanager to rollback: Because of an error divided by 0, rollback: Hibernate: Insert into User (changetime, email, password, registtime, username) VALUES (?,?,?,?,?) 11:50:55.662 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction rollback11:50:55.662 [main] Debug O . s.orm.jpa.jpatransactionmanager-rolling back JPA transaction on Entitymanager [[email protected]]11:50:56.416 [ Main] DEBUG O.S.ORM.JPA.JPATransactionmanager-closing JPA Entitymanager [org.hibernate.jpa.internal.entit[email protected]] After transaction11:50:56.416 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA Entitymanager


Is this the end of it? Of course not: scenario One is a common service layer of transactional testing. If we insert correctly, we really plug in the database, and if we run out of error, we roll back. This is obvious. Now, what I'm going to say is,

Scenario Two: When the data is inserted correctly, the data is also rolled back, but we get the information that has been inserted, which can protect the data site.

@TransactionConfiguration (Defaultrollback=true)

First: Do not protect the data site

The service layer code is not changed, the test code in test is inconvenient, but added @Transactional @TransactionConfiguration in Test (defaultrollback=false)//Open Start the transaction and do not roll back

The test results are:

Correct insertion method: Hibernate:insert into User (changetime, email, password, registtime, username) values (?, ?, ?, ?, ?) 11:59:52.169 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction commit11:59:52.169 [main] Debug O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]11:59:52.356 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after transaction11 : 59:52.356 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager11:59:52.372 [main] INFO o.s.t.c. t.transactioncontext-committed transaction for test context rollback: Hibernate:insert into User (Chang etime, email, password, registtime, username) VALUES (?,?,?,?,?) 12:04:26.861 [main] DEBUG o.s.orm.jpa.jpatransactionmanager-participating transaction failed-marking existing Transac tion as rollback-only12:04:26.861 [main] DEBUG o.s.orm.jpa.jpAtransactionmanager-setting JPA transaction on Entitymanager [[email protected]] rollback-only12:04:26.861 [main ] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction commit12:04:26.861 [main] Debug O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]12:04:26.939 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after Transaction12 : 04:26.939 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager12:04:26.970 [main] WARN o.s.test . Context. Testcontextmanager-caught exception while allowing testexecutionlistenercould not commit JPA transaction; Transaction marked as Rollbackonly
Although the final result is the same as the scenario one, the log is a bit different (when correctly inserted: The service's transaction commits the insert correctly, to the test layer, because the transaction is declared, and is set to not rollback, so the test layer commits the transaction, so the database is inserted correctly ; When the error is inserted: The service has rolled back the request, so it doesn't matter if the test layer does not commit.

The second type: Protect data field: Change test layer to @transactional @TransactionConfiguration (defaultrollback=true)

Insert correctly: Hibernate:insert into User (changetime, email, password, registtime, username) values (?, ?, ?, ?, ?) 12:15:22.067 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction rollback12:15:22.067 [main] Debug O . s.orm.jpa.jpatransactionmanager-rolling back JPA transaction on Entitymanager [[email protected]]12:15:22.157 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after Transaction12 : 15:22.157 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager12:15:22.157 [main] INFO o.s.t.c. T.transactioncontext-rolled back transaction for test context error insert: Hibernate:insert into User (cha Ngetime, email, password, registtime, username) VALUES (?,?,?,?,?) 12:16:18.037 [main] DEBUG o.s.orm.jpa.jpatransactionmanager-participating transaction failed-marking existing Transac tion as rollback-only12:16:18.037 [main] DEBUG O.S.ORM.JPA. Jpatransactionmanager-setting JPA transaction on Entitymanager [[email protected]] rollback-only12:16:18.053 [ Main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction rollback12:16:18.053 [main] Debug O.s.orm.jpa.jpatransactionmanager-rolling back JPA transaction on Entitymanager [[email protected]]12:16:18.205 [Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after Transaction12 : 16:18.205 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager12:16:18.205 [main] INFO o.s.t.c.  T.transactioncontext-rolled back transaction for test context

Analysis: is not very magical, correctly inserted, but actually rolled back, so also protect the data site. Instead of being inserted incorrectly, the rollback of test is useless because the transaction failed to roll back in the service. Of course, even if successful, it will be rolled back.


Note: The relationship between the @Transactional and the @TransactionConfiguration (defaultrollback=true) is that only the @transactional,@ is set Transactionconfiguration (defaultrollback=true) can work, otherwise no eggs. Of course, if @transactional is set, @transactionconfiguration (Defaultrollback=true) is not set, the default is rollback.


Scenario Three: Bypassing the service layer to measure transactions (will be very pit dad)

First: The transaction is introduced and set to not rollback, and the transaction management object is introduced @persistencecontext private Entitymanager EMT;

@Transactional @transactionconfiguration (defaultrollback=false) @PersistenceContextprivate Entitymanager  EMT;

Two test methods to bypass service

@Testpublic void Testwusuccess () {                //normal insert for (int i=0;i<20;i++) {User user=new User (); user.setemail ("[Email Protected] "+i); emt.persist (user);}} @Testpublic void Testwufail ()                   //non-normal insert {for (int i=0;i<20;i++) {User user=new User (); user.setemail ("[Email Protected] "+i); if (i==10) {int a=5/0;} Emt.persist (user);}}


Observation results:

Results of normal insertion hibernate:insert into User (changetime, email, password, registtime, username) values (?, ?, ?, ?, ?) 12:38:44.699 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction commit12:38:44.699 [main] Debug O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]12:38:44.855 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after Transaction12 : 38:44.855 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager12:38:44.855 [main] INFO o.s.t.c. t.transactioncontext-committed transaction for test context non-normal insert condition Result: 12:41:24.759 [main] DEBUG o.s.orm.jpa.jpatransactionmanager-initiating transaction commit12:41:24.759 [main] DEBUG O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]12:41:24.822 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager[[email protected]] after transaction12:41:24.822 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA  entitymanager12:41:24.822 [main] INFO o.s.t.c.t.transactioncontext-committed transaction for test context

I rub, incredibly same, I did not save, directly on the test, absolutely not, normal insert does not throw an exception, error insert thrown exception. and the normal insert inserted 20, non-normal insertion only inserted in the first 10. A good rollback.

I've measured it over and over again: including @transation in the method, adding the various modes of the transaction, including actively throwing various exceptions, including setting various default exception detections for @transation (we know that declarative transactions are default detection Runtionexction, But I am here in addition to 0 formal runtionexction in the AH), so I also directly change the default detection, the final result or no eggs, still do not roll back. In a variety of tests, I found one that could be rolled back. As shown below:

@Testpublic void Testwufail () {for (int i=0;i<20;i++) {User user=new User (); user.setemail ("[Email protected]" +i); if ( i==10) {User.setid (26);} Emt.persist (user);}}
Hibernate:     Insert     into        user         (changetime, email, password, registtime, username)     values         (?,?,?,?,?) 12:53:44.306 [main] Debug o.s.orm.jpa.jpatransactionmanager-initiating transaction commit12:53:44.306 [main] Debug O.s.orm.jpa.jpatransactionmanager-committing JPA transaction on Entitymanager [[email protected]]12:53:44.370 [ Main] DEBUG o.s.orm.jpa.jpatransactionmanager-closing JPA Entitymanager [[[email protected]] after Transaction12 : 53:44.370 [main] DEBUG o.s.o.jpa.entitymanagerfactoryutils-closing JPA entitymanager12:53:44.378 [main] WARN  O.s.test.context.testcontextmanager-caught exception while allowing Testexecutionlistener [org.springframew[email &NBSP;PROTECTED]1E412161] to process "after" execution for Test:method [public void Com.wenyan.test.jpatest.testwufail ( )], InstaNCE [[email protected]], exception [javax.persistence.PersistenceException: Org.hibernate.PersistentObjectException:detached entity passed to Persist:com.wenyan.model.User] Org.springframework.transaction.TransactionSystemException:Could not commit JPA transaction; Transaction marked as Rollbackonly
But what I want is to roll back all the runtimeexction, so there's no egg.
There is another way to roll back: that is to set a
@Transactional @transactionconfiguration (defaultrollback=true)
But: this is the transaction itself not rolling back, but test has helped us roll back, these two are different categories. Still no eggs to use
Interim Summary:
1. Do unit testing, or according to the norms to test, test transactions do not bypass the service @transactional, otherwise it will be very sad urge. The reason is that the @transactional in the service effectively manages the cycle of the transaction, the @transational in test is the management data field, and after the data operation transaction itself passes through the test layer to decide to not rollback. So it can be said that these two levels of @transactional do things in different categories.      Even so, I believe that it should be possible to bypass the service directly to do the unit test of the transaction, but I have not found, so still measured according to the specification
2. It is clearly somewhat contradictory to say that the @transational of the Test level does not have the functionality to manage the transaction itself. When I manually transaction management, I want to open the transaction, then do the operation, last commit, and then close:
<pre class= "html" name= "code" > @Test                              //This is an example of normal insertion of public void Testshiwu () {Entitymanager em=this.getemf (). Createentitymanager (); Em.gettransaction (). Begin (); for (int i=0;i<20;i++) {User user=new User (); User.setemail ("[ Email protected] "+i); if (i==10) {int a=5/0;} Em.persist (user);} Em.gettransaction (). commit (); Em.close ();}

When an exception is encountered, it is not committed, so there is nothing to roll back this kind of thing. If it is normal, it will be submitted.


In the case of declarative management, we can insert the database directly without having to open the transaction and commit the transaction, but it is strange that the test does not actually help us to roll back in the event of an exception. I think the biggest possibility is conflict. So I personally think that the @transation in the test has the ability to open transactions and commit transactions. However, the rollback function is not correct.


Scene Four:

Look at the real situation and what's the difference in the test case:

1. When @transation is not declared in the service: using functions such as the canonical interface function persist (), there is no transaction exception, and cannot be inserted into the database, using the implemented interface functions such as Save (), you can insert the function, Because it has transaction management by default, it does not have a back-palace feature.

2. When I declare @transation in the service: using functions such as the canonical interface function persist (), there is still an exception with no transaction and cannot be inserted into the database.

Analysis: What is the reason for this: I used the spring MVC framework, when the MVC configuration file scanned the entire package, (including the package in the service), will naturally use @trancetion as a normal bean, so there is no transaction management.

For example, the MVC configuration file uses the

<context:component-scan base-package= "Com.wenyan" ></context:component-scan> this is equivalent to an all-inclusive scan. The Wenyan package contains both a controller and a service, which results in the transaction declared in the service being used as a simple bean, so there is no management feature of the transaction naturally.


Workaround:

The settings for the package scan do not include service: There are two ways:

First: Direct Scan controller package: <context:component-scan base-package= "Com.wenyan.controller" ></context: Component-scan>

Second: All-inclusive scanning: ignore the service:

<context:component-scan base-package= "Com.wenyan" >
<context:exclude-filter type= "Annotation" expression= "Org.springframework.stereotype.Service"/>
</context:component-scan>

Both of these methods can resolve this issue.


3. Therefore, when we join the transaction declaration without scanning the service, the function persist () using the canonical interface and the implemented interface function save () all have transaction management characteristics. That is, do not pay attention to, open, submit, close, rollback and other operations, it helps us to automatically fix.



Final Summary:

[email protected] the declaration in service and test is a different category, as stated in the service, the transaction management feature exists. Test declares that it has an open, committed, closed feature, but does not have a rollback feature, and the transation in test is the most important to decide whether or not to commit to the database, which plays a role in protecting the data site.


2. When integrating spring MVC, classes with @service are not scanned in the MVC configuration file, which causes transactions to become normal beans rather than beans with transaction management attributes.


3. The implemented interface function save () and so on, no need to @transation can also realize the features such as Open, commit, close, but many operations, there is no rollback characteristics.


4. Both the canonical interface persist and the implemented interface save require @transation support before they all have the full characteristics of the transaction.


5: Why the implemented interface has the ability to open transactions, Auto Commit, turn off transactions, but no rollback features;

The reason: When we configured spring MVC, we had to let him, with the factory method, and the transaction management, where the bottom-layer should do is we manually generate the transaction, turn on, close, commit the function.

<jpa:repositories base-package= "Com.wenyan.dao" entity-manager-factory-ref= "Entitymanagerfactory" transaction-manager-ref= "TransactionManager" >
</jpa:repositories>


6. Test and write code according to specifications, then do not consider so much, if you want to not standardize, you must understand the principle of the United States can do it.
























Spring declarative transaction management in the context of real service and unit test rollback, issues needing attention, JPA as an example

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.