1

UPDATE: The table was set to MyISAM instead of InnoDB which fixes the problem.

So I have this method:

@Transactional()
@Override
public List<Order> findAll() {
    Order order = new Order();
    Order order1 = new Order();
    Order order2 = new Order();
    orderRepository.save(order);
    orderRepository.save(order1);
    orderRepository.save(order2);

    return orders;
}

When one of them throws exception the others don't rollback. I tried to set some wrong data type in order to get DataException, but then I realized that it is maybe a checked exception? So I did this:

    order2.setDishesReady(5);
    if(order2.getDishesReady() == 5){
        throw new RuntimeException();
    }

Still no rollback. I also tried

@Transactional(propagation=Propagation.REQUIRED)
@Transactional(rollbackFor = Exception.class)

And still nothing is it possible to fake a rollback like this? By throwing exception in the method and if not how can I test the rollback? This is my configuration:

@Configuration
@EnableWebMvc
@EnableTransactionManagement
public class AppConfig {


    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan("com.project.models");

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());

        return em;
    }
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/project");
        dataSource.setUsername( "root" );
        dataSource.setPassword( "1234" );
        return dataSource;
    }
    @Bean
    public PlatformTransactionManager transactionManager(
            EntityManagerFactory emf){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);

        return transactionManager;
    }
    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {
        Properties properties = new Properties();
        properties.setProperty(
                "hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        return properties;
    }

Update with some something from log:

Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:05 - Creating new EntityManager for shared EntityManager invocation
""2019-02-10 09:30:05 - Closing JPA EntityManager
""2019-02-10 09:30:06 - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
""2019-02-10 09:30:06 - Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1d1cfe4, org.springframework.security.web.context.SecurityContextPersistenceFilter@10745a02, org.springframework.security.web.header.HeaderWriterFilter@3d798e76, org.springframework.web.filter.CorsFilter@ba562e0, org.springframework.security.web.authentication.logout.LogoutFilter@13c90c06, com.vision.project.security.JwtAuthenticationTokenFilter@7c447c76, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@72715e61, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5611bba, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5a51336a, org.springframework.security.web.session.SessionManagementFilter@386e9fd8, org.springframework.security.web.access.ExceptionTranslationFilter@5403431a, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@58b8f9e3]
""2019-02-10 09:30:07 - Initializing ExecutorService 'taskScheduler'
""2019-02-10 09:30:07 - Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
""2019-02-10 09:30:07 - Tomcat started on port(s): 8080 (http) with context path ''
""2019-02-10 09:30:07 - Started ProjectApplication in 13.85 seconds (JVM running for 15.214)
""2019-02-10 09:31:09 - Initializing Spring DispatcherServlet 'dispatcherServlet'
""2019-02-10 09:31:09 - Initializing Servlet 'dispatcherServlet'
""2019-02-10 09:31:09 - Completed initialization in 16 ms
""2019-02-10 09:31:09 - Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
""2019-02-10 09:31:09 - Found thread-bound EntityManager [SessionImpl(1272634933<open>)] for JPA transaction
""2019-02-10 09:31:09 - Creating new transaction with name [com.vision.project.services.OrderServiceImpl.findAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.RuntimeException,-java.lang.Exception
""2019-02-10 09:31:09 - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@7234e97]
""2019-02-10 09:31:09 - Found thread-bound EntityManager [SessionImpl(1272634933<open>)] for JPA transaction
""2019-02-10 09:31:09 - Participating in existing transaction
""2019-02-10 09:31:09 - Found thread-bound EntityManager [SessionImpl(1272634933<open>)] for JPA transaction
""2019-02-10 09:31:09 - Participating in existing transaction
""2019-02-10 09:31:09 - Initiating transaction rollback
""2019-02-10 09:31:09 - Rolling back JPA transaction on EntityManager [SessionImpl(1272634933<open>)]
""2019-02-10 09:31:09 - Not closing pre-bound JPA EntityManager after transaction
""2019-02-10 09:31:09 - Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
""2019-02-10 09:31:09 - Closing JPA EntityManager

Gradle 4.4, Java 8, Dependencies:

    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.boot:spring-boot-starter-security")
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile "org.springframework.boot:spring-boot-configuration-processor"
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.1.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-jdbc', version: '5.0.8.RELEASE'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.2.5.RELEASE'
    compile group: 'org.springframework.security', name: 'spring-security-jwt', version: '1.0.2.RELEASE'

    compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.1.Final'
    compile group: 'org.hibernate', name: 'hibernate-gradle-plugin', version: '5.3.5.Final'

    compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.6.0'
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.6'
    compile group: 'com.mchange', name: 'c3p0', version: '0.9.5.2'
  • Let's increaae log level to trace and let see what we got. set `org.springframework.transaction` package logging level as `trace` or `debug` – dgregory Feb 10 '19 at 06:54
  • @dgregory when settin logging.level.org.springframework = trace some beans missing, but "match" or "found" others. I tried setting explicity logging.level.org.springframework.transaction = trace and all the information about the transactions beans is gone, but this: Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$c2c8b097] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) –  Feb 10 '19 at 07:20
  • @dgregory I updated something from the log in the question: it is initiating transaction roolback and then it also says: Not closing pre-bound JPA EntityManager after transaction. Also it is creating and closing EntityManager many time is this normal? –  Feb 10 '19 at 07:36
  • Only checked exceptions need to be declared in "rollbackOn" attribute in Transactional annotation, unchecked will be rolled back by default. I have tried your code and it works well. Maybe you have something wrong in your dependecies. Agree with @heyza22, creating and closing EntityManager many times doesn't look like normal. – Jose Luis Feb 10 '19 at 08:44
  • @JoseLuis updated question with my dependencies using gradle 4.4 with java 1.8 –  Feb 10 '19 at 09:09
  • If you are using Spring Data you don't need spring-jdbc nor hibernate dependencies, they already come with spring-data and could make some extrange behaviour like you have now. These are the dependencies I have used to build your example: compile("org.springframework.boot:spring-boot-starter-data-jpa") compile("org.springframework.boot:spring-boot-starter-web") runtime('mysql:mysql-connector-java') testCompile("org.springframework.boot:spring-boot-starter-test") – Jose Luis Feb 10 '19 at 10:28
  • Also I would suggest to remove C3P0 if you are not going to use it (you didn't configure it) – Jose Luis Feb 10 '19 at 10:32
  • @JoseLuis compile('org.springframework.boot:spring-boot-starter-web') compile("org.springframework.boot:spring-boot-starter-security") testCompile('org.springframework.boot:spring-boot-starter-test') compile("org.springframework.boot:spring-boot-starter-data-jpa") runtime('mysql:mysql-connector-java') compile group: 'org.springframework.security', name: 'spring-security-jwt', version: '1.0.2.RELEASE' compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.6.0' compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5' –  Feb 10 '19 at 11:00
  • @JoseLuis I deleted all and left with your dependencies and some jwt token and apache ones and a jwt token implementation in the code and still the same thing. Last thing did you the same app config as mine when I changed to runtime('mysql:mysql-connector-java') it said something about setDriverClassName being deprecated and setting time zoned but still nothing. –  Feb 10 '19 at 11:03
  • @JoseLuis and last is there somethin wrong with the rest of my gradle build buildscript { ext { springBootVersion = '2.1.1.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } –  Feb 10 '19 at 11:07
  • apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' apply plugin: 'jacoco' apply plugin: 'war' group = 'com.vision' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { mavenCentral() } –  Feb 10 '19 at 11:09
  • according to log, transaction marked rollback as you expect. What if we change the method name and remove override annotation? It looks extended or implemented class of JpaRepository or something, so it may decorated or intercepted by framewok's code – dgregory Feb 10 '19 at 11:32
  • @heyza22 for the problem of time zone add this to the url connection string: ?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC (just right after the name of the schema). It's a matter of configuration of the server. – Jose Luis Feb 10 '19 at 11:40
  • @heza22 why do you need war pluggin if you are using spring boot? – Jose Luis Feb 10 '19 at 11:42
  • @dgregory so after all it was the database my table was not set to InnoDB. –  Feb 10 '19 at 13:32
  • @JoseLuis it was left with one of my last projects my college was deploying the application. I am using fat jar for my other application, but I think you can also use war with spring. –  Feb 10 '19 at 13:34
  • lol it was mysql isam engine issue. :-( thank you for updateing issue. – dgregory Feb 10 '19 at 17:38

1 Answers1

1

I am answering my question for people who will skip the update. So after some time, I realized that something like this:

    orderRepository.save(order1);
    orderRepository.save(order2);
    Order order = orderRepository.save(order3);
    order.setDishesReady = 5;
    orderRepository.delete(order3);
    if(order3.getDishesReady() == 5){
        throw new RuntimeException();
    }

Is doing a rollback only for the delete, but not for the save and with some more research, I found out that my table was set to MyISAM and not InnoDB. From What's the difference between MyISAM and InnoDB? some more on the topic:

If you need the database to enforce foreign key constraints, or you need the database to support transactions (i.e. changes made by two or more DML operations handled as single unit of work, with all of the changes either applied, or all the changes reverted) then you would choose the InnoDB engine, since these features are absent from the MyISAM engine.