2

I had a controller method that updates some fields of an order entity. I traced the execution flow of the controller method in debug mode. And I found that the transaction commits too early. The transaction commits just after call repository update method. what's the problem?

source codes are below.

// Controller

@RestController
@RequestMapping(value = "/test", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public class TxTestController extends BaseController {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    @GetMapping(value = "/update")
    public void updateOrder() throws Exception {
        Order order = orderRepository.findAll().get(0);
        order.setFeeRemains(order.getFeeRemains().add(BigDecimal.valueOf(100000000)));

        orderRepository.updateOrder(order.getId(), order.getRemains(), order.getFeeRemains(), order.getStatus());
        // The transaction is commited after execution of the above line.
        // and the external database tools can see the changed data from here.
        // So no way to rollback transaction after this line.

        System.out.println(order);
        // do another persistence jobs
    }
}

// Repository

public interface OrderRepository extends JpaRepository<Order, String>, QueryDslPredicateExecutor<Order> {
@Modifying
@Query("update Order o set o.remains = :remains, o.feeRemains = :feeRemains, o.status = :status where o.id = :orderId")
void updateOrder(@Param("orderId") String orderId,
                 @Param("remains") BigDecimal remains,
                 @Param("feeRemains") BigDecimal feeRemains,
                 @Param("status") Order.Status status);
}

// application.yml

spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect
    generate-ddl: true
    hibernate:
      ddl-auto: update
    show-sql: false
  datasource:
    url: jdbc:mysql://localhost:3306/plutusds
    username: root
    password: root
    testWhileIdle: true
    validationQuery: SELECT 1

// pom dependencies

<?xml version="1.0" encoding="UTF-8"?>
    ...
    <dependencies>
        ...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>1.5.2.RELEASE</version>
        </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.41</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.2.9.Final</version>
    </dependency>
    ...
</dependencies>
...
</project>

if I remove the @Transactional annotation from the controller method, then javax.persistence.TransactionRequiredException occurred.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Shinbop
  • 131
  • 2
  • 14
  • Hi Shinbop. Didn't you try to develop this using MVC. There is some link about spring Rest. Try to get idea about that.Then you can get answer for your question http://www.beingjavaguys.com/2014/08/spring-restful-web-services.html – Priyantha May 04 '17 at 04:52
  • read this link http://stackoverflow.com/questions/1079114/where-does-the-transactional-annotation-belong/29895285#29895285 – Harshal Patil May 04 '17 at 05:02

3 Answers3

2

In spring @Transactional defines single database transaction. Since spring uses Hibernate EntityManager internally to manage the session for database transaction and it is handled automatically.Commit will be done once a database transaction is successful.We can have multiple database transactions in single method.In that case commit will happen after each successful transaction. @Transactional does not mean to the method where we use.It just says that method have a database transaction and that will be taken care of by spring. Another point is we should not write transactional at controller level , we should have a service class for it where we can use transactional. please refer to the below link which describes in detail of @Transactional.

How Spring Jpa Transactional Works

Mihir
  • 75
  • 1
  • 9
1

For a long, long time it hasn't been possible to use @Transactional annotations on controllers using the default Java proxy mechanism. Spring creates a proxy of controllers and the annotation processor that manages transactions looses visibility of the @Transactional annotation as it can only see the proxy.

TL;DR: Spring managed transactions cannot start in a controller. Move that into a service layer.

By the way, controllers shouldn't have business logic as yours have (those 3 lines of 'find - set - update' are business logic).

Augusto
  • 28,839
  • 5
  • 58
  • 88
0

This problem is due to the mysql engine type.

By default, Hibernate creates the tables with the MyISAM engine which is not transactional. Basically just you need to define the dialect for hibernate to switch to a transactional engine type such as InnoDB.

Try this:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLInnoDBDialect

Below link includes useful details about mysql engine type as summary information:

https://www.w3resource.com/mysql/mysql-storage-engines.php

Adrian W
  • 4,563
  • 11
  • 38
  • 52