简体   繁体   中英

Jpa @Transactional doesn't rollback

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'

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 (ie 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.

@Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.

@Transactional(rollbackFor = Exception.class)

If you throw an Exception or a subclass of it, always use the above with the @Transactional annotation to tell Spring to roll back transactions if a checked exception occurs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM