2017-01-22 1 views
1

Ich habe eine Frage in Bezug auf @Transactional Annotation. Nichts besonderes definiert, so wie ich es verstehe, ist PROPAGATION_REQUIRED Nehmen wir an, ich habe eine transaktionale Annotation, die auf Service-und Dao-Layer.Transaktional in beiden Service-und Dao-Schichten

Dienst

@Transactional 
public long createStudentInDB(Student student) { 
    final long id = addStudentToDB (student); 
    addStudentToCourses (id, student.getCourseIds()); 
    return id; 
} 

private long addStudentToDB (Student student) { 
    StudentEntity entity = new StudentEntity(); 
    convertToEntity(student, entity); 
    try { 
     final id = dao.create(entity); 
    } catch (Exception e){ 
     // 
     } 
    return id; 
} 

private void addStudentToCourses (long studentId, List<String> coursesIds){ 
    //add user to group 
    if(coursesIds!= null){ 
     List<StudentCourseEntity> studentCourses = new ArrayList<>(); 
     for(String coursesId: coursesIds){ 
      StudentCourseEntity entity = new StudentCourseEntity(); 
      entity.setCourseId(coursesId); 
      entity.setStudentId(userId); 
      studentCourses.add(studentId); 
     } 
     anotherDao.saveAll(studentCourses); 
    } 
} 

DAO

@Transactional 
public UUID create(StudentEntity entity) { 

    if (entity == null) { throw new Exception(//…); } 

    getCurrentSession().save(entity); 
    return entity.getId(); 
} 

ANOTHERDAO

@Transactional 
public void saveAll(Collection<StudentCourseEntity> studentCourses) { 
    List<StudentCourseEntity> result = new ArrayList<>(); 
    if(studentCourses!= null) { 
     for (StudentCourseEntity studentCourse : studentCourses) { 
      if (studentCourse!= null) { 
       save(studentCourse); 
      } 
     } 
    } 

} 

Trotz der Tatsache, die nicht optimal ist, wie es scheint es Deadlocks verursacht. Angenommen, ich habe maximal 2 Verbindungen zur Datenbank. Und ich verwende 3 verschiedene Threads, um den gleichen Code auszuführen. Thread-1 und Thread-2 erhalten eine Verbindung, Thread-3 erhält keine Verbindung. Mehr als das, scheint Thread-1 stecken, wenn versucht wird, eine Verbindung in Dao-Ebene, das gleiche für Thread-2. Einen Deadlock verursachen.

Ich war mir sicher, dass dies mit propagation_required nicht passieren würde. Fehle ich etwas? Was ist die Empfehlung für so etwas? Gibt es eine Möglichkeit, @ transactional auf beiden Ebenen zu haben? Wenn nicht, was ist bevorzugt? Dank Fabrizio

+0

Wenn Sie keine Verbindungen mehr haben, haben Sie entweder keine Transaktionen richtig eingerichtet oder sind mit Verbindungen selbst beschäftigt, anstatt Spring die Dinge verwalten zu lassen. Nur das Hinzufügen von Methodensignaturen ohne Implementierung hilft nicht bei der Lösung Ihres Problems. Überprüfen Sie zunächst Ihre Einrichtung (stellen Sie sicher, dass die tx-Einrichtung korrekt ist) und überprüfen Sie auch Ihre Implementierungen. –

Antwort

0

Da die dao.doSomeStuff aufgerufen werden soll aus anderen Transaktionen Ich würde Ihnen vorschlagen, diese Methode als konfigurieren: zu

@Transaction(propagation=REQUIRES_NEW) 

Dank, dass die Transaktion, die diese Methode aufruft angehalten, bis der mit REQUIRES_NEW abgeschlossen ist.

Nicht sicher, ob dies die Lösung für Ihren speziellen Deadlock-Fall ist, aber Ihr Beispiel passt zu dieser speziellen Konfiguration.

+0

Danke für Ihre Antwort. Ich brauche genau das Gegenteil. Ich möchte nicht angehalten werden. Ich habe mich gerade gefragt, ob ich die Verbindung auf Service-Ebene erhalten habe, und jetzt verwende ich diese Verbindung wieder.Warum bleibe ich dabei? – fabriziomieli

+0

Versuchen Sie, die Implementierung der Methoden hinzuzufügen. LEts sehen, was dort los ist –

+0

Hallo, ich habe meine Frage bearbeitet. kann das damit zusammenhängen, dass ich 2 verschiedene daos benutze? – fabriziomieli

0

Sie haben Recht, Propagation.REQUIRED ist die Standardeinstellung. Das bedeutet aber auch, dass der zweite (verschachtelte) Aufruf von dao die Transaktion, die auf Service-Ebene erstellt wurde, verbindet/wiederverwendet. Daher muss für den verschachtelten Aufruf keine weitere Transaktion erstellt werden.

Der bevorzugte Ansatz ist die Verwendung Spring höchstes Niveau Vorlage basierte Persistenz Integrations-APIs oder zu verwenden:

Im Allgemeinen Spring (auf hohem Niveau usage) sollte Umgang mit Ressourcen, indem sie es auf die darunter liegende ORM-Schicht Spedition verwalten native ORM-APIs mit transaktionsbewussten Factory-Beans oder Proxies zum Verwalten der systemeigenen Ressourcenfabriken . Diese transaktionsbewussten Lösungen intern handhaben Ressource erstellen und wiederverwenden, Bereinigung, optionale Transaktion Synchronisation der Ressourcen und Ausnahme Mapping. Somit muss der Benutzer Datenzugriffscode diese Aufgaben nicht lösen, sondern kann rein auf Non-Boilerplate-Persistenz-Logik konzentriert sein.

Auch wenn Sie es auf Ihrem eigenen (auf niedrigem Niveau API-Nutzung) verarbeiten die Verbindungen wieder verwendet werden sollten:

Wenn Sie den Anwendungscode wollen Arten der nativen direkt mit der Ressource behandeln Persistenz-APIs verwenden Sie diese Klassen, um sicherzustellen, dass ordnungsgemäße von Spring Framework verwaltete Instanzen abgerufen werden, Transaktionen (optional) synchronisiert sind und die im Prozess auftretenden Ausnahmen ordnungsgemäß einer konsistenten API zugeordnet werden.

...

Wenn bereits eine existierende Transaktion eine Verbindung (gebunden), um es synchronisiert hat, wird diese Instanz zurückgegeben. Andernfalls löst der Methodenaufruf das Erstellen einer neuen Verbindung aus, die (optional) mit einer bestehenden Transaktion synchronisiert (optional) und für die nachfolgende Wiederverwendung in derselben Transaktion verfügbar gemacht wird .

Source

Vielleicht haben Sie herausfinden, was da unten passiert.

Jede Sitzung/Unit of Work wird an einen Thread gebunden und nach dem Ende der Transaktion freigegeben (zusammen mit der zugewiesenen Verbindung). Wenn der Thread hängen bleibt, wird die Verbindung nicht freigegeben.

Sind Sie sicher, dass dieser "Deadlock" durch diese Verschachtelung verursacht wird? Vielleicht hat das einen anderen Grund. Haben Sie einen Testcode für dieses Beispiel? Oder ein Thread Dump oder so?

+0

Danke für Ihre Antwort. Ich verstehe das. Wenn dies jedoch der Fall ist und der verschachtelte Aufruf die auf Service-Ebene erstellte Transaktion wieder verwendet, wie kommt es dann, dass es darauf ankommt, eine Verbindung herzustellen? Ich lese, dass sie dieselbe "physische" Verbindung benutzen, aber eine andere "logische" Verbindung ... aber vielleicht fehlt mir etwas! – fabriziomieli

+0

Redigierte meine Antwort. Und vielleicht könnten Sie hier Ihren Testcode posten. –

+0

Hallo, ich habe meine Frage bearbeitet. kann das damit zusammenhängen, dass ich 2 verschiedene daos benutze? – fabriziomieli

0

@Transactional funktioniert unter Beibehaltung ThreadLocal Zustand, der durch die (Spring verwaltete) Proxy EntityManager zugegriffen werden kann. Wenn Sie Propagation.REQUIRED (Standardeinstellung) verwenden und über eine nicht transaktionale Methode verfügen, die zwei verschiedene DAOs (oder zwei Transactional-Methoden für dasselbe DAO) aufruft, erhalten Sie zwei Transaktionen und zwei Aufrufe zum Abrufen einer gepoolten Verbindung. Sie können die gleiche Verbindung zweimal oder zwei verschiedene Verbindungen erhalten, aber Sie sollten nur eine Verbindung zu der Zeit verwenden.

Wenn Sie zwei DAOs von einer @Transactional-Methode aufrufen, gibt es nur eine Transaktion. Da das DAO die vorhandene Transaktion im ThreadLocal-Status findet und ihr beitritt, benötigen Sie wiederum nur eine Verbindung aus dem Pool.

Wenn Sie einen Deadlock erhalten, dann ist etwas sehr falsch, und Sie möchten möglicherweise debuggen, wenn Ihre Verbindungen und Transaktion erstellt werden. Eine Transaktion wird durch Aufruf von Connection.setAutoCommit(false) gestartet, in Hibernate geschieht dies in org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor#begin(). Verbindungen werden von einer Klasse verwaltet, die org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor erweitert, also sind dies einige gute Orte, um einen Unterbrechungspunkt zu setzen und den Call-Stack zurück zu Ihrem Code zu verfolgen, um zu sehen, welche Verbindungen Verbindungen hergestellt haben.

Verwandte Themen