@Transactional na metodzie dodającej jeden rekord

0

Cześć,

Staram się zgłębić temat transakcji na podstawie przykładkowego projektu stworzonego przez programistów springa -> link do projektu na githubie

Mamy tam napisany serwis jak poniżej.
Nie mogę zrozumieć dlaczego użyto tutaj @Transactional skoro jest tylko jeden insert do bazy danych.
Czytałem, że transakcji powinno się używać tam gdzie np. mamy kilka insertów do bazy i chcemy aby wykonały się wszystkie albo żaden.

package org.springframework.samples.petclinic.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.model.User;
import org.springframework.samples.petclinic.model.Role;
import org.springframework.samples.petclinic.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public void saveUser(User user) {

        if(user.getRoles() == null || user.getRoles().isEmpty()) {
            throw new IllegalArgumentException("User must have at least a role set!");
        }

        for (Role role : user.getRoles()) {
            if(!role.getName().startsWith("ROLE_")) {
                role.setName("ROLE_" + role.getName());
            }

            if(role.getUser() == null) {
                role.setUser(user);
            }
        }

        userRepository.save(user);
    }
}
0

W podanym linku jest akurat REST data, wiec nie znalazłem tego kodu.
No ale do rzeczy.

Mając tylko tyle kodu mogę założyć, że masz tam architekturę warstwową. Powyżej (czyli to co używa tego serwisu) serwisu jest pewnie kontroler restowy.
"Poniżej" (czyli to czego używa ten service) masz warstwę danych.
Więc naturalnym miejsce dla Transacational ta metoda. (Oczywiście ta "logika" mogłaby być przed wywołaniem metody, ale w praktyce tak się to robi)
Co robi @transactional to używa Aspektu, aby otoczyć wywołanie metody w proxy, dzięki czemu nie musisz robić:

public void save(User user) throws SQLException{
	entityManager.getTransaction().begin();
	try {
		return entityManager.persist(user);
	} finally {
		entityManager.getTransaction().commit();
		entityManager.close();
	}
}

W dobry sposób opisane jest to tutaj: https://pater.dev/co-tak-na-prawde-robi-transactional/

6

Po pierwsze to ten kod nie robi "tylko jednego inserta".
Prawdopodobnie * robi max jeden insert i n updatów (na Role). Dlatego : https://github.com/spring-petclinic/spring-petclinic-rest/blob/ee236caf798dde6ead7ab0726fb1cea96ca398ae/src/main/java/org/springframework/samples/petclinic/model/User.java#L30

Transactional w Springu/JPA ogranicza też sesję JPA, a więc np. cache pierwszego poziomu i inne cuda. Więc nawet jak nie ma żadnych zapisów (ale jest wiele odczytów) to nadal ma swój sens (ograniczamy nonsensowne powtarzające się odczyty z bazy, które w gównianym ORM jakim jest JPA są normą).

--
* prawdopodobnie - bo to Spring, więc równie dobrze ten kod może wysyłać maile i trzepać dywany, a bazy danych nawet nie ma, wystarczy tylko, że ktoś podpiął odpowiednie aspekty.

0

Nie zapominaj że transakcje do jednego inserta też mogą być useful, jak np chcesz dodać coś do bazy, a potem móc to zrollbackować. Nie jest to częsty case, ale niewykluczone.

1 użytkowników online, w tym zalogowanych: 0, gości: 1