4

I'm using Spring boot with jpa, mysql in my project. But @Transaction not working. It does not rollback all data if have any error Code given below. Any suggestions? Thanks.

Application:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Entity Product:

@Entity
public class Product extends BaseModel<Long> {

    @Id
    @GeneratedValue
    private Long id;
    private String description;
    @OneToMany(mappedBy = "productId", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<Speed> speeds;

    //geter and seter
}

Entity Speed:

@Entity
public class Speed extends BaseModel<Long> {

    @Id
    @GeneratedValue
    private Long id;
    private Long productId;
    private Long productSpeed;
    //Getter and setter
}

Repository:

public interface SpeedRepository extends JpaRepository<Speed, Long> {

}

public interface ProductRepository extends JpaRepository<Product, Long> {

}

Service:

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private speedRepository speedRepository;

    @Transactional(rollbackFor = TestException.class)
    void saveProduct(Product product) throws TestException {
        try{
            productRepository.save(product);
            for (Speed speed : product.getSpeeds()) {
                speedRepository.save(speed);
            }
        } catch (TestException e) {
            throw new TestException("error", "error message");
        }
    }
}

Controller:

@RestController
@RequestMapping("/product")
public class ProductRestController {

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private MachineSpeedRepository machineSpeedRepository;
    @Autowired
    private ProductService productService;

    @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    Object addListProduct(OAuth2Authentication auth, @RequestBody Product product) throws TestException {
        productService.saveProduct(product);
        return product;
    }

}

application.properties:

spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://192.168.1.111/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

build.gradle:

buildscript {
    ext {
        springBootVersion = '1.5.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile("org.springframework.security.oauth:spring-security-oauth2")
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.hibernate:hibernate-validator")
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'mysql:mysql-connector-java'
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Database:

CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `description` text NOT NULL COMMENT 'product name',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8


CREATE TABLE `speed` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(20) DEFAULT NULL,
  `product_speed` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8

How can i use transaction or configure correctly

daivph
  • 73
  • 1
  • 7

4 Answers4

1

As @Kenny Tai Huynh said i suggest you to move the transaction to service layer (it's up to you though). Anyway it's possible that @Transactional is not being picked up because of the method visibility... try to change it to public and check if it works

Zeromus
  • 4,472
  • 8
  • 32
  • 40
  • To add to this answer, I tried that and it was still not working. I could select new entries in database while debugging the method (and therefore transaction shouldn't have been commited yet and no result should have come). What did solve for me though was to use the `@Transaction` annotation in a class level instead of a method level. I'm not adding this as an answer because I'm still not completely sure if this is a good idea. – Guilherme Taffarel Bergamin Jul 09 '21 at 12:15
1

Thanks all, I set method saveProduct to public. It's working. There is no requirement for whether the @Transactional(rollbackFor = TestException.class) annotation should go on a Controller or on a Service

Service:

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private speedRepository speedRepository;

    @Transactional(rollbackFor = TestException.class)
    public void saveProduct(Product product) throws TestException {
        try{
            productRepository.save(product);
            for (Speed speed : product.getSpeeds()) {
                speedRepository.save(speed);
            }
        } catch (TestException e) {
            throw new TestException("error", "error message");
        }
    }
}

Controller:

@RestController
@RequestMapping("/product")
public class ProductRestController {

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private MachineSpeedRepository machineSpeedRepository;
    @Autowired
    private ProductService productService;

    @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    Object addListProduct(OAuth2Authentication auth, @RequestBody Product product) throws TestException {
        productService.saveProduct(product);
        return product;
    }

}
daivph
  • 73
  • 1
  • 7
0

While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.

Nicely explained in following page

https://www.catalysts.cc/en/wissenswertes/spring-transactional-rollback-on-checked-exceptions/

bhshank
  • 1
  • 1
  • I also used @Transactional (rollbackFor = TestException.class). But it does not rollback previous data – daivph Oct 11 '17 at 04:31
  • https://stackoverflow.com/questions/15620355/for-web-mvc-spring-app-should-transactional-go-on-controller-or-service – bhshank Oct 11 '17 at 04:42
0

The @Transactional should not be in Controller. Only the service layer in the application identifies the scope of a database/business transaction. Can you move the code to a @Service class and then try again?!

Kenny Tai Huynh
  • 1,464
  • 2
  • 11
  • 23
  • 1
    I moved the code to a `@Service` class and i also use @Transactional (rollbackFor = TestException.class). It's not effective – daivph Oct 11 '17 at 09:38