Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse, funktioniert nicht?

Lesezeit: 6 Minuten

Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Mike

Ich bin neu bei Spring Transaction. Etwas, das ich wirklich seltsam fand, wahrscheinlich habe ich das richtig verstanden.

Ich wollte eine Transaktion auf Methodenebene haben und ich habe eine Aufrufmethode innerhalb derselben Klasse, und es scheint, als würde sie das nicht mögen, sie muss von der separaten Klasse aufgerufen werden. Ich verstehe nicht, wie das möglich ist.

Wenn jemand eine Idee hat, wie man dieses Problem lösen kann, wäre ich sehr dankbar. Ich möchte dieselbe Klasse verwenden, um die annotierte Transaktionsmethode aufzurufen.

Hier ist der Code:

public class UserService {

    @Transactional
    public boolean addUser(String userName, String password) {
        try {
            // call DAO layer and adds to database.
        } catch (Throwable e) {
            TransactionAspectSupport.currentTransactionStatus()
                    .setRollbackOnly();

        }
    }

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            addUser(user.getUserName, user.getPassword);
        }
    } 
}

  • Werfen Sie einen Blick auf die TransactionTemplate Ansatz: stackoverflow.com/a/52989925/355438

    – Lu55

    5. Mai 2019 um 21:03 Uhr

  • Warum die Selbstaufrufung nicht funktioniert, siehe 8.6 Proxying-Mechanismen.

    – Jason Gesetz

    8. November 2019 um 9:33 Uhr

Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Espen

Es ist eine Einschränkung von Frühling AOP (dynamische Objekte und cglib).

Wenn Sie Spring für die Verwendung konfigurieren AspektJ Um die Transaktionen abzuwickeln, funktioniert Ihr Code.

Die einfache und wahrscheinlich beste Alternative besteht darin, Ihren Code umzugestalten. Beispielsweise eine Klasse, die Benutzer behandelt, und eine, die jeden Benutzer verarbeitet. Dann Ursprünglich Transaktionsabwicklung mit Spring AOP funktioniert.


Konfigurationstipps zum Umgang mit Transaktionen mit AspectJ

Damit Spring AspectJ für Transaktionen verwenden kann, müssen Sie den Modus auf AspectJ setzen:

<tx:annotation-driven mode="aspectj"/>

Wenn Sie Spring mit einer älteren Version als 3.0 verwenden, müssen Sie dies auch zu Ihrer Spring-Konfiguration hinzufügen:

<bean class="org.springframework.transaction.aspectj
        .AnnotationTransactionAspect" factory-method="aspectOf">
    <property name="transactionManager" ref="transactionManager" />
</bean>

  • Danke für die Information. Ich habe den Code vorerst umgestaltet, aber könnten Sie mir bitte ein Beispiel mit AspectJ schicken oder mir einige hilfreiche Links geben. Vielen Dank im Voraus. Mike.

    – Mike

    9. August 2010 um 16:54 Uhr


  • Transaktionsspezifische AspectJ-Konfiguration in meiner Antwort hinzugefügt. Ich hoffe, es hilft.

    – Esp

    10. August 2010 um 14:42 Uhr

  • Das ist gut! Übrigens: Es wäre schön, wenn Sie meine Frage als beste Antwort markieren könnten, um mir einige Punkte zu geben. (grünes Häkchen)

    – Esp

    16. August 2010 um 18:31 Uhr

  • Spring Boot-Konfiguration: @EnableTransactionManagement (Modus = AdviceMode.ASPECTJ)

    – VinyJones

    11. Mai 2020 um 10:03 Uhr

  • Warum ist AspectJ nicht standardmäßig für die Abwicklung von Transaktionen?

    – Alex78191

    28. Oktober 2021 um 8:11 Uhr

Das Problem hierbei ist, dass die AOP-Proxys von Spring Ihre Dienstinstanz nicht erweitern, sondern umhüllen, um Anrufe abzufangen. Dies hat den Effekt, dass jeder Aufruf von „this“ aus Ihrer Dienstinstanz direkt auf dieser Instanz aufgerufen wird und nicht vom Wrapping-Proxy abgefangen werden kann (der Proxy ist sich eines solchen Aufrufs nicht einmal bewusst). Eine Lösung ist bereits erwähnt. Eine andere raffinierte Methode wäre, Spring einfach eine Instanz des Dienstes in den Dienst selbst einzufügen und Ihre Methode für die eingefügte Instanz aufzurufen, die der Proxy ist, der Ihre Transaktionen verarbeitet. Beachten Sie jedoch, dass dies auch negative Nebenwirkungen haben kann, wenn Ihre Service-Bean kein Singleton ist:

<bean id="userService" class="your.package.UserService">
  <property name="self" ref="userService" />
    ...
</bean>

public class UserService {
    private UserService self;

    public void setSelf(UserService self) {
        this.self = self;
    }

    @Transactional
    public boolean addUser(String userName, String password) {
        try {
        // call DAO layer and adds to database.
        } catch (Throwable e) {
            TransactionAspectSupport.currentTransactionStatus()
                .setRollbackOnly();

        }
    }

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            self.addUser(user.getUserName, user.getPassword);
        }
    } 
}

  • Wenn Sie sich für diesen Weg entscheiden (ob dies ein gutes Design ist oder nicht, ist eine andere Frage) und keine Konstruktorinjektion verwenden, stellen Sie sicher, dass Sie diese Frage auch sehen

    – Jeschurun

    11. April 2012 um 22:25 Uhr


  • Was, wenn UserService hat Singleton-Bereich? Was ist, wenn es sich um dasselbe Objekt handelt?

    – Jan Chonski

    30. Mai 2019 um 11:11 Uhr

1646423236 273 Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Bunarro

In Java 8+ gibt es eine andere Möglichkeit, die ich aus den unten angegebenen Gründen bevorzuge:

@Service
public class UserService {

    @Autowired
    private TransactionHandler transactionHandler;

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
        }
    }

    private boolean addUser(String username, String password) {
        // TODO call userRepository
    }
}

@Service
public class TransactionHandler {

    @Transactional(propagation = Propagation.REQUIRED)
    public <T> T runInTransaction(Supplier<T> supplier) {
        return supplier.get();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public <T> T runInNewTransaction(Supplier<T> supplier) {
        return supplier.get();
    }
}

Diese Vorgehensweise hat folgende Vorteile:

  1. Es kann angewendet werden Privat Methoden. Sie müssen also die Kapselung nicht aufheben, indem Sie eine Methode öffentlich machen, nur um die Einschränkungen von Spring zu erfüllen.

  2. Dieselbe Methode kann innerhalb verschiedener Transaktionsweitergaben und aufgerufen werden es liegt am Anrufer das passende auszuwählen. Vergleichen Sie diese 2 Zeilen:

    transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

    transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

  3. Es ist explizit, also besser lesbar.

  • Das ist toll! Es vermeidet alle Fallstricke, die Spring sonst mit seiner Anmerkung einführt. Liebe es!

    – Frank Hopkins

    24. Juli 2020 um 14:45 Uhr

  • Wenn ich verlängere TransactionHandler als Unterklasse, und die Unterklasse ruft diese beiden Methoden in auf TransactionHandler Superklasse, werde ich trotzdem in den Genuss bekommen @Transactional wie beabsichtigt?

    – tom_mai78101

    28. Juli 2020 um 14:23 Uhr

  • Klingt wunderbar! Ich frage mich, ob es einige Vorbehalte gibt?

    – ch271828n

    4. September 2020 um 1:07 Uhr

  • Exzellent. Ich habe diese Lösung auch verwendet, mit einem kleinen Unterschied: Ich habe die Methoden in TransactionHandler benannt runInTransactionSupplier und runInNewTransactionSupplier. Dies lässt die Möglichkeit offen, später ähnliche, aber ungültige Rückgabemethoden in TransactionHandler hinzuzufügen.

    – Burebista

    11. März 2021 um 10:51 Uhr

  • @burebista, du machst es falsch, es ist möglich, zwei Methoden mit demselben Namen zu definieren, von denen eine den Lieferanten akzeptiert und T zurückgibt und die andere Runnable akzeptiert und void zurückgibt.

    – roma2341

    12. Juli 2021 um 9:15 Uhr


Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Almas Abdrazak

Mit Spring 4 ist es möglich, sich selbst automatisch zu verdrahten

@Service
@Transactional
public class UserServiceImpl implements UserService{
    @Autowired
    private  UserRepository repository;

    @Autowired
    private UserService userService;

    @Override
    public void update(int id){
       repository.findOne(id).setName("ddd");
    }

    @Override
    public void save(Users user) {
        repository.save(user);
        userService.update(1);
    }
}

1646423237 90 Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Hlex

Das ist meine Lösung für Selbstaufruf:

public class SBMWSBL {
    private SBMWSBL self;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void postContruct(){
        self = applicationContext.getBean(SBMWSBL.class);
    }

    // ...
}

1646423237 852 Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
LöweH

Sie können BeanFactory innerhalb derselben Klasse automatisch verdrahten und Folgendes tun

getBean(YourClazz.class)

Es wird automatisch Ihre Klasse proxifizieren und Ihre @Transactional- oder andere Aop-Anmerkung berücksichtigen.

1646423238 242 Spring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse funktioniert
Mario Eis

Folgendes mache ich für kleine Projekte mit nur marginaler Verwendung von Methodenaufrufen innerhalb derselben Klasse. Eine In-Code-Dokumentation wird dringend empfohlen, da sie für Kollegen seltsam aussehen kann. Aber Es funktioniert mit Singletons, ist einfach zu testen, einfach, schnell zu erreichen und erspart mir die vollständige AspectJ-Instrumentierung. Für eine stärkere Nutzung würde ich jedoch die AspectJ-Lösung empfehlen, wie in der Espens-Antwort beschrieben.

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }

    @Transactional
    public void addUser(String username, String password) {
        // call database layer
    }

    public void addUsers(List<User> users) {
        for (User user : users) {
            _personDao.addUser(user.getUserName, user.getPassword);
        }
    }
}

938170cookie-checkSpring @Transaction Methodenaufruf durch die Methode innerhalb derselben Klasse, funktioniert nicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy