Home > Software engineering >  How to understand @Transactional with setters in Java?
How to understand @Transactional with setters in Java?

Time:04-18

Following tutorial on Java Spring, I'm trying to understand how @Transactional work with setters, and from other question/sources, I can't find a beginner-friendly explanation for it.

Let's say I have a user entity with getters and setters:

@Entity
@Table
public class User {
    // Sequence set up
    
    private Long id;
    private String name;
    private String email;
    private String password;

    // Other constructors, setters and getters

    public void setName(String name) {
        this.name = name;
    }
}

And in the UserService I have a getUserName method:

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public StudentService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional
    public Boolean getName(Long id) {
        User user = userRepository
            .findById(id)
            .orElseThrow(
                () ->  new IllegalStateException("Invalid id"));

        user.setName("new user name"); // Why will this update db?
 
        return true;        
    }
}

Comming from javascript ORM, we usually update data like this: userRepository.update(). With @Transactional annotated, the setter function does update db, is this the spring way of updating data? Can someone help explain in layman term, how the Transactional work with setters under the hood?

Edit: Without @Transactional, setter function won't update db, but in order to mutate db, will have to call userRepository.save(user). And from the video, the instructor simply says the Transactional will handle jpql for us, and use setters along with it to update db.

CodePudding user response:

Firstly, it is the underlying JPA provider (assume it is Hibernate) to be responsible for updating the entity but not Spring. Spring just provides the integration support with Hibernate.

To update an entity loaded from the DB , generally you need to make sure the following happens in order.

  1. Begin a DB transaction

  2. Use EntityManager to load the entity that you want to update.The loaded entity is said to be managed by this EntityManager such that it will keep track all the changes made on its state and will generate the necessary update SQL to update this entity in (4) automatically.

  3. Make some changes to the entity 's state. You can do it through any means such as calling any methods on it , not just restricting to calling it by setter

  4. Flush the EntityManager. It will then generate update SQL and send to DB.

  5. Commit the DB transaction

Also note the followings:

  • Spring provides @Transactional which is a declarative way to execute (1) and (5) by annotating it to a method.
  • By default , Hibernate will call (4) automatically before executing (5) such that you do not need to call (4) explicitly.
  • Spring Data JPA repository internally use EntityManager to load the user. So the user return from the repository will be managed by this EntityManager.

So in short , @Transactional is necessary to update the entity. And updating the entity is nothing to do with setter as it just care if there are state changes on the entity in the end , and you can do it without using setter.

CodePudding user response:

Spring uses Hibernate as ORM under the hood. When you call userRepository.findById, Hibernate entity manager is called under the hood, it retrieves entity from database and at the same time makes this entity manageable (you can read separately about Hibernate managed entities). What it means, in a simple words, the Hibernate 'remembers' the reference to this entity in its internal structures, in the so-called session. It, actually, 'remembers' all entities which it retrieves from database (even the list of entities obtained by queries) during single transaction (in the very basic case).

When you make some method @Transactional, by default Hibernate session is flushed when such method is finished. session.flush() is called under the hood.

Once session gets flushed, Hibernate pushes all changes made to these managed entities back into the database.

That is why your changes got to the database, once method was finished, without any additional calls.

To dig deeper into the topic, you can read more about Hibernate managed entities , session flush mode, repository.save(), repository.saveAndFlush() in Spring Data.

  • Related