Wann werden Verbindungen mit Spring JPA (Hibernate) Entity Manager an den Verbindungspool zurückgegeben?

Lesezeit: 6 Minuten

Benutzer-Avatar
fürhas

In meinem Java-Prozess verbinde ich mich mit MySql mit der folgenden Frühlingskonfiguration:

@Configuration
@EnableTransactionManagement
@PropertySources({ @PropertySource("classpath:/myProperties1.properties"), @PropertySource("classpath:/myProperties2.properties") })
public class MyConfiguration {

    @Autowired
    protected Environment env;

    /**
     * @return EntityManagerFactory for use with Hibernate JPA provider
     */
    @Bean(destroyMethod = "destroy")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setJpaVendorAdapter(jpaVendorAdapter());
    em.setPersistenceUnitManager(persistenceUnitManager());

    return em;
    }

    /**
     * 
     * @return jpaVendorAdapter that works in conjunction with the
     *         persistence.xml
     */
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setDatabase(Database.valueOf(env.getProperty("jpa.database")));
    vendorAdapter.setDatabasePlatform(env.getProperty("jpa.dialect"));
    vendorAdapter.setGenerateDdl(env.getProperty("jpa.generateDdl", Boolean.class, false));
    vendorAdapter.setShowSql(env.getProperty("jpa.showSql", Boolean.class, false));

    return vendorAdapter;
    }

    @Bean
    public PersistenceUnitManager persistenceUnitManager() {
    DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
    pum.setPackagesToScan("com.app.dal");
    pum.setDefaultPersistenceUnitName("my-pu");
    pum.setPersistenceXmlLocations("classpath:/META-INF/persistence.xml");
    pum.setDefaultDataSource(dataSource());

    return pum;
    }

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
    Properties dsProps = new Properties();
    dsProps.put("driverClassName", env.getProperty("hikari.driverClassName"));
    dsProps.put("username", env.getProperty("hikari.username"));
    dsProps.put("password", env.getProperty("hikari.password"));
    dsProps.put("jdbcUrl", env.getProperty("hikari.source.data.jdbcUrl"));
    dsProps.put("connectionTimeout", env.getProperty("hikari.connectionTimeout", Integer.class));
    dsProps.put("idleTimeout", env.getProperty("hikari.idleTimeout", Integer.class));
    dsProps.put("maxLifetime", env.getProperty("hikari.maxLifetime", Integer.class));
    dsProps.put("maximumPoolSize", env.getProperty("hikari.maximumPoolSize.rtb.source", Integer.class));
    dsProps.put("leakDetectionThreshold", env.getProperty("hikari.leakDetectionThreshold", Integer.class));
    dsProps.put("jdbc4ConnectionTest", env.getProperty("hikari.jdbc4ConnectionTest", Boolean.class));

    HikariConfig config = new HikariConfig(dsProps);
    HikariDataSource ds = new HikariDataSource(config);

    return ds;
    }

    @Bean(name = "sourceTxMgr")
    public PlatformTransactionManager sourceDatatransactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setPersistenceUnitName("my-pu");
    transactionManager.setDataSource(dataSource());

    return transactionManager;
    }

    @Bean
    public PersistencyManager persistencyManager() {
    return new JpaPersistencyManager();
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
    }

}

Der Entity-Manager wird vom Container in die Datenzugriffsschicht injiziert:

@PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;

Und meine öffentlichen Methoden der Geschäftslogik sind mit kommentiert @Transactional Anmerkung.

Soweit ich weiß, ist der Container dafür verantwortlich, dass der Entity-Manager Verbindungen zum Pool zurückgibt (in meinem Fall HikariCP), sobald eine Transaktion abgeschlossen ist, aber ich habe keine offizielle Dokumentation gefunden, die beschreibt, wie die Verbindungen verwaltet werden. Kann mir das jemand erklären oder eine gute Referenz liefern, die erklären kann, wann genau Verbindungen an den Pool zurückgegeben werden, wenn eine solche Konfiguration verwendet wird?

AKTUALISIEREN:

Die besten verwandten Informationen, die mir bisher eingefallen sind (von hier genommen):

Der Persistenzkontext-Proxy, der EntityManager implementiert, ist nicht die einzige Komponente, die benötigt wird, damit die deklarative Transaktionsverwaltung funktioniert. Eigentlich werden drei separate Komponenten benötigt:

Der EntityManager-Proxy selbst Der transaktionale Aspekt Der Transaktionsmanager Lassen Sie uns jeden einzeln durchgehen und sehen, wie sie interagieren.

Der transaktionale Aspekt

Der transaktionale Aspekt ist ein „Around“-Aspekt, der sowohl vor als auch nach der annotierten Geschäftsmethode aufgerufen wird. Die konkrete Klasse zur Implementierung des Aspekts ist TransactionInterceptor.

Der transaktionale Aspekt hat zwei Hauptaufgaben:

Im ‘Before’-Moment stellt der Aspekt einen Einstiegspunkt bereit, um zu bestimmen, ob die aufzurufende Geschäftsmethode im Rahmen einer laufenden Datenbanktransaktion ausgeführt werden soll oder ob eine neue separate Transaktion gestartet werden soll.

Im „Nachher“-Moment muss der Aspekt entscheiden, ob die Transaktion festgeschrieben, rückgängig gemacht oder ausgeführt werden soll.

Im „Vorher“-Moment enthält der Transaktionsaspekt selbst keine Entscheidungslogik, die Entscheidung, bei Bedarf eine neue Transaktion zu starten, wird an den Transaktionsmanager delegiert.

Der Transaktionsmanager

Der Transaktionsmanager muss eine Antwort auf zwei Fragen geben:

soll ein neuer Entity Manager erstellt werden? soll eine neue Datenbanktransaktion gestartet werden? Dies muss in dem Moment entschieden werden, in dem der transaktionale Aspekt „vor“ der Logik aufgerufen wird. Der Transaktionsmanager entscheidet basierend auf:

die Tatsache, dass eine Transaktion bereits läuft oder nicht das Weitergabeattribut der Transaktionsmethode (z. B. REQUIRES_NEW startet immer eine neue Transaktion) Wenn der Transaktionsmanager beschließt, eine neue Transaktion zu erstellen, dann wird er:

Erstellen Sie einen neuen Entitätsmanager. Binden Sie den Entitätsmanager an den aktuellen Thread. Greifen Sie eine Verbindung aus dem DB-Verbindungspool. Binden Sie die Verbindung an den aktuellen Thread. Der Entitätsmanager und die Verbindung werden beide mithilfe von ThreadLocal-Variablen an den aktuellen Thread gebunden.

Sie werden im Thread gespeichert, während die Transaktion ausgeführt wird, und es liegt am Transaktionsmanager, sie zu bereinigen, wenn sie nicht mehr benötigt werden.

Alle Teile des Programms, die den aktuellen Entitätsmanager oder die aktuelle Verbindung benötigen, können sie aus dem Thread abrufen. Eine Programmkomponente, die genau das tut, ist der EntityManager-Proxy.

  • Ich bezweifle, dass der Container für die Rückgabe von Verbindungen verantwortlich ist. Spring ist dafür verantwortlich, da es die Transaktionen über den JPATransactionManager verwaltet. Eine gute Möglichkeit zur Bestätigung wäre, Spring und die HikariCP-Protokolle zu aktivieren und zu überprüfen.

    – Andy Dufresne

    16. Dezember 2014 um 6:49 Uhr

  • Wenn ich “Der Container” sage, springe ich Container, oder genauer gesagt den Entity-Manager, der von Spring Container verwaltet wird. docs.spring.io/spring/docs/current/spring-framework-reference/…

    – fürhas

    16. Dezember 2014 um 9:24 Uhr


  • Die von Ihnen freigegebene Website ist nicht verfügbar. Kannst du den Link aktualisieren.

    – Hose

    22. Oktober 2018 um 18:38 Uhr

Benutzer-Avatar
Vlad Mihalcea

Es ist überhaupt nicht kompliziert.

  1. Zunächst müssen Sie verstehen, dass der Spring-Transaktionsmanager nur eine Transaktionsverwaltungsabstraktion ist. In Ihrem Fall finden die eigentlichen Transaktionen auf der JDBC-Verbindungsebene statt.

  2. Alle @Transactional Dienstmethodenaufrufe werden von der abgefangen TransactionInterceptor Aspekt.

  3. Das TransactionIntreceptor delegiert die Transaktionsverwaltung an die aktuelle Konfiguration
    AbstractPlatformTransactionManager Implementierung (JpaTransactionManager in Ihrem Fall).

  4. JpaTransactionManager bindet die aktuell ausgeführte Spring-Transaktion an einen EntityManager, sodass alle an der aktuellen Transaktion beteiligten DAOs denselben Persistenzkontext teilen.

  5. JpaTransactionManager verwendet einfach die EntityManager Transaktions-API zur Steuerung von Transaktionen:

     EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
     tx.commit();
    

Die JPA-Transaktions-API delegiert den Aufruf einfach an die zugrunde liegenden Commit-/Rollback-Methoden der JDBC-Verbindung.

  1. Wenn die Transaktion abgeschlossen ist (Commit/Rollback), wird die org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction Anrufe:

     transactionCoordinator().getTransactionContext().managedClose();
    

was das Schließen einer Hibernate Session (Entity Manager) auslöst.

  1. Die zugrunde liegende JDBC-Verbindung wird daher ebenfalls geschlossen:

     jdbcCoordinator.close();
    
  2. Hibernate hat ein logisches JDBC-Verbindungshandle:

     @Override
     public Connection close() {
         LOG.tracev( "Closing JDBC container [{0}]", this );
         if ( currentBatch != null ) {
         LOG.closingUnreleasedBatch();
             currentBatch.release();
         }
         cleanup();
         return logicalConnection.close();
     }
    
  3. Die logische Verbindung delegiert den Close-Aufruf an den aktuell konfigurierten Verbindungsanbieter (DataSourceConnectionProvider in Ihrem Fall), die einfach die Methode close für die JDBC-Verbindung aufruft:

     @Override
     public void closeConnection(Connection connection) throws SQLException {
          connection.close();
     }
    
  4. Wie jede andere Datenquelle für das Verbindungspooling gibt das Schließen der JDBC-Verbindung einfach die Verbindung zum Pool zurück und schließt nicht die physische Datenbankverbindung. Das liegt daran, dass die Datenquelle für das Verbindungspooling einen JDBC-Verbindungsproxy zurückgibt, der alle Aufrufe abfängt und das Schließen an die Verarbeitungslogik für den Verbindungspool delegiert.

Beachten Sie, dass Sie für RESOURCE_LOCAL-Transaktionen auch die festlegen sollten hibernate.connection.provider_disables_autocommit Eigentum, wenn die autocommit check wurde vom Verbindungspool deaktiviert. Auf diese Weise werden die Datenbankverbindungen träge erfasst, bevor eine SQL-Abfrage ausgeführt oder der Persistenzkontext geleert wird.

  • Schön, aber woher hast du diese Info? Wie erhalte ich eine offizielle Dokumentation, die diesen Prozess erklärt oder zumindest die Verantwortlichkeiten jeder Komponente in der Kette erklärt?

    – fürhas

    23. Dezember 2014 um 15:55 Uhr

  • Ich habe gerade den Quellcode durchsucht. Ich denke, es ist die aktuellste Dokumentation.

    – Vlad Mihalcea

    23. Dezember 2014 um 16:14 Uhr

  • Ich werde einen Blogbeitrag schreiben und ihn dann mit dieser Frage verlinken. Es ist eine sehr interessante Frage.

    – Vlad Mihalcea

    24. Dezember 2014 um 17:18 Uhr

  • Sicher, ich habe die Antwort mit einem Link zu einem Blogbeitrag aktualisiert, der weitere Details enthält.

    – Vlad Mihalcea

    16. August 2019 um 9:45 Uhr

  • @morgwai Das sind 10 Artikellisten. Hier gibt es nur drei Abstraktionsebenen: Spring Logical Transaction, Hibernate Logical Transaction und der Hibernate Connection Provider-Mechanismus.

    – Vlad Mihalcea

    11. November 2021 um 18:54 Uhr

1018260cookie-checkWann werden Verbindungen mit Spring JPA (Hibernate) Entity Manager an den Verbindungspool zurückgegeben?

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

Privacy policy