Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CrudRepository's "save" operation commits transaction even if Service does rollback [DATAJPA-219] #631

Closed
spring-projects-issues opened this issue Jun 3, 2012 · 7 comments
Assignees
Labels
in: core type: bug

Comments

@spring-projects-issues
Copy link

@spring-projects-issues spring-projects-issues commented Jun 3, 2012

Daniel Fernández opened DATAJPA-219 and commented

I define a JPA entity like this:

@Entity
@Table(name="TEST_DATA")
public class Test {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    
    @Column(name="NAME")
    private String name;

    ...
}

Its corresponding repository looks like:

@Repository
public interface TestRepository extends PagingAndSortingRepository<Test,Integer> {
    
}

And a service method:

@Service
public class TestService {

    @Transactional
    public Test add(final String name) {

        final Test test = new Test();
        test.setName(name);

        final Test result = this.testRepository.save(test);
        this.testRepository.delete(result.getId());

        throw new RuntimeException("Boooom!!");
//        return result;

    }
}

Transactionality is correctly configured at applicationContext.xml with a JpaTransactionManager, <tx:annotation-driven/>, etc. Executing the add() method results in a TEST_DATA tuple living in the database after the exception is raised. If the throw sentence is commented out and the return result; is uncommented, no TEST_DATA tuples live in database after execution. So it looks as if save() is always committing its own transaction, as if it were using REQUIRES_NEW instead of REQUIRED


Affects: 1.1 GA

Attachments:

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 3, 2012

Oliver Drotbohm commented

Would you mind adding a test case showing the described behavior? I doubt the scenario you see is really a bug as as we have tons of integration tests that actually persist data but roll back that in an outer transaction (standard Spring test context framework rollbacks) and they work fine. I guess there's something wrong with your configuration or the way you actually invoke the code. Are you sure you're executing add() from outside TestService? If not, the method invocation will not actually start a transaction which would then cause save(...) to be committed (as it is the most outer transaction)

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 3, 2012

Daniel Fernández commented

Attached is a Spring-MVC based webapp calling the "TestService.add()" method from a controller when a form gets submitted.

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 3, 2012

Daniel Fernández commented

I understand it looks extremely weird... so it seemed to me, too. Been hunting this for two days because I almost didn't believe what I saw, until I finally gave up and filed this ticket. I imagine I can be wrong, but... I cannot see where.

The strangest thing is that the "delete()" call actually gets rolled back when executing the service. And modifying works perfectly too, and correctly rolled back if I throw an exception at the end of a modification method. But "save()" doesn't roll back, that's why I thought of it being using a REQUIRES_NEW.

Maybe it's Micro Cloud Foundry's fault. I have only tested it on this environment (with a mysql service). Or maybe it's just my configuration...

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 4, 2012

Oliver Drotbohm commented

Thanks for the code sample but I am not going to wade through 4 layers of what could possible cause this problem. From a quick glance I see two issues that look weird:

  1. in the service you save the entity and immediately delete it afterwards. Does that even make any kind of sense?
  2. your ApplicationContext setup cannot actually work as you use JDK proxy based transactions but don't have an interface declared for the services. Thus you'll get a CGLib proxy for the service which then cannot be injected into the controller as TestService anymore as the proxy doesn't even implement this type anymore

Long story short, please reduce the scenario to a test case which can be reproduced to fail with a mvn clean test command. Remove as much additional code as possible to make sure it's not something else interfering and reduce the number stuff we actually have to look at to actually understand what's going on

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 4, 2012

Daniel Fernández commented

Hi,

Thanks a lot for your response.

in the service you save the entity and immediately delete it afterwards. Does that even make any kind of sense?

Well, testing. As the "Test" entity implies ;-). I just wanted to perform two different data access operations inside the same transaction.

your ApplicationContext setup cannot actually work as you use JDK proxy based transactions but don't have an interface declared for the services. Thus you'll get a CGLib proxy for the service which then cannot be injected into the controller as TestService anymore as the proxy doesn't even implement this type anymore

Well, in fact CGLIB proxies are created as subclasses of the original class (that's why the original class cannot be "final"), so this proxified bean implements TestService because it is a subclass of it. And I've actually checked it is the proxy that is being injected into the Controller (by checking its System.identityHashCode()).

Anyway, I understand this is hard to test this way, so I'll make a couple more tests myself (for example, switching to JDK proxies) and try to reorganize this code so that you can reproduce it in an easier way. I sent you a webapp because it was a webapp where I came accross this.

Of course, I might find a problem in my code along the way, I'll keep you informed.

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 4, 2012

Daniel Fernández commented

Oliver,

After some more testing, it seems you were right in thinking this was not a Spring Data JPA problem. In fact it seems to be a Cloud Foundry issue related to the "mysql" service. Testing this on a local MySQL it worked perfectly, and also testing it against a Cloud Foundry PostgreSQL service.

I am not sure of the reason for this CF behaviour, but it looks as if CF is always choosing the MyISAM engine for the application tables instead of InnoDB, even if the dialect being used is the InnoDB one.

I've filed this issue here: https://cloudfoundry.atlassian.net/browse/CF-51

Please close this ticket as "INVALID", as it is not Spring Data JPA -specific.

Thanks a lot. Sorry for thinking this was Spring Data's fault.

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jun 4, 2012

Oliver Drotbohm commented

Glad it works out for you, even if it's just getting a bit closer to the actual problem. No problem at all :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core type: bug
Projects
None yet
Development

No branches or pull requests

2 participants