Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data updates entity without transaction

I have simple code example with Spring Data:

Entity

@Entity
@Table(name = "car")
public class Car {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
//...
}

Repository:

@Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}

Service with example methods:

@Service
public class CarService {

    @Autowired
    private CarRepository carRepository;

    public void change() {
        Car one = carRepository.findOne(1L);
        one.setName("changed"); //important
    }
}

And controller:

@RestController("/")
public class CarController {

    @Autowired
    private CarRepository carRepository;

    @Autowired
    private CarService carService;

    @GetMapping
    public List<Car> home() {
        carService.change();
        List<Car> all = carRepository.findAll();

        return all;
    }

}

I also have data.sql with 3 records:

insert into car values (1, 'aaa');
insert into car values (2, 'bbb');
insert into car values (3, 'ccc');

When I send request to the localhost:8080 I get list with three cars, where the first one is updated:

insert into car values (1, 'changed');
insert into car values (2, 'bbb');
insert into car values (3, 'ccc');

My expectation is that after calling change() from controller actually nothing happens because this method is not transactional. So why my home() method in controller returns list with updated entity?

This question is completly different from this Spring data jpa @transactional I don't understand why you mark this as duplicate.

like image 858
swch Avatar asked Nov 27 '17 17:11

swch


2 Answers

I assume, that you are using Spring Boot. If you use Spring Boot with Spring MVC and Spring Data, by default a Hibernate Session will be opened every time your controller methods receive a request. A Session provides a physical connectivity between your application and database. This functionality can be controlled with the spring.jpa.open-in-view application property.

Car one = carRepository.findOne(1L); //1
one.setName("changed"); //2
List<Car> all = carRepository.findAll(); //3
return all;
  1. A session will be open, when the program reaches the code above. On the first line, the Car object is retrieved from the DB, and the object is stored in the First Level Cache.
  2. As the "one" reference points to the same object, that is in the cache, at line 2 the cached objects name changes.
  3. On the third line we get the cached value for the object, that we retrieved earlier, and that value is returned at the end.

Since the modified value is never flushed to the DB, after your controller call you can check your DB, and you will see, that the old value remained for your object, despite you received a different value in the controller response.

If you set the spring.jpa.open-in-view property to false, your controllers will return the "correct" value.

like image 138
Bence Dergez Avatar answered Oct 18 '22 22:10

Bence Dergez


According to the Spring Data documentation methods are transactional!

4.7. Transactionality

CRUD methods on repository instances are transactional by default. For reading operations the transaction configuration readOnly flag is set to true, all others are configured with a plain @Transactional so that default transaction configuration applies. For details see JavaDoc of SimpleJpaRepository.

Read the full documentation here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

like image 1
Simon Martinelli Avatar answered Oct 18 '22 20:10

Simon Martinelli