2010-09-14 13 views
7

Ich arbeite an einer Anwendung, die einige Datenbankoperationen ausführen muss.Wie erstellt man eine threadsichere EntityManagerFactory?

I erzeugt eine statische Variable für EntityManagerFactory und initialisiert es in der Methode, die von der Anwendung

if (emf == null){ 
        emf = Persistence.createEntityManagerFactory("example"); 
       } 

try { 
      em = emf.createEntityManager(); 
     } catch (Exception ex) { 
      logger.error(ex.getMessage()); 
     } 

ist dieser Faden sicher aufgerufen wird? Wenn ich die EntityManagerFactory in einem synchronisierten Block erstelle, wird die Anzahl der wartenden Threads erhöht und die Anwendung stürzt ab.

Ich schaute in der Dokumentation, um zu sehen, ob die Persistence.createEntityManagerFactory threadsicher ist, ohne Erfolg.

Bitte weisen Sie mich auf die richtigen Ressourcen.

Antwort

11

Eine einfache Möglichkeit zum "Lösen" wäre die Verwendung einer Hilfsklasse (a la HibernateUtil) und die Initialisierung der EntityManagerFactory in einem statischen Initialisierungsblock. Etwas wie dieses:

public class JpaUtil { 
    private static final EntityManagerFactory emf; 

    static { 
     try { 
      factory = Persistence.createEntityManagerFactory("MyPu"); 
     } catch (Throwable ex) { 
      logger.error("Initial SessionFactory creation failed", ex); 
      throw new ExceptionInInitializerError(ex); 
     } 
    } 

    ... 

} 

Und das "Problem" ist weg.

+0

Ich habe das schon mal gemacht. Aber einige Leute finden, dass es nicht eine gute Übung ist, in den statischen Blöcken zu initialisieren. Ist das richtig? –

+1

@Vanchinathan Das ist wirklich ein typischer Ansatz, wenn Sie nicht in einer verwalteten Umgebung sind und ich sehe nichts falsch daran. Nun, wenn Sie einige Argumente dagegen vorbringen, könnten wir sie diskutieren, aber bis dahin halte ich diese Empfehlung aufrecht. –

+2

Das einzige Argument, das ich immer bekomme, ist Code in den statischen Blöcken, der das Testen behindert. Wir folgen Test Driven Entwicklung streng. Also brauche ich etwas, das auch einfacher zu testen ist. –

1

Sie müssen Sperren auf ein Objekt setzen, wenn Sie die EMF erstellen. Sie können die Sperren auf das EMF-Objekt selbst setzen, aber das ist nicht die beste Praxis. Erstellen Sie ein anderes Objekt:

private object factoryLockObject = new object(); 

und setzen Sie Ihre Sperren auf sie, während die Fabrik zu schaffen

lock(factoryLockObject) 
{ 
    if (emf == null) 
    { 
     emf = Persistence.createEntityManagerFactory("example"); 
    } 
} 

das helfen?

+0

Welchen Unterschied macht das? Ich werde es versuchen. –

+0

Dies fügt dem FactoryLockObject eine Objektsperre hinzu und veranlasst alle anderen Threads, die darauf zugreifen wollen, zu warten, bis die Sperre aufgehoben wird (an der letzten geschweiften Klammer). http://csharpindepth.com/Articles/General/Singleton.aspx – Brad

+0

Immer noch wird es nicht die Threads blockieren? Wenn Persistence.createEntityManagerFactory ("Beispiel"); ist threadsicher, dann muss ich es nicht synchronisieren. –

1

Ob createEntityManagerFactory() threadsicher ist oder nicht, Sie benötigen eine Strategie, so dass sie nur einmal aufgerufen wird. Mit anderen Worten, diese Frage ist irrelevant, weil Sie sicherstellen müssen, dass nur ein Thread sie aufruft.

Wenn einfach für einen anderen Thread warten, um die Fabrik stürzt die Anwendung zu erstellen, was passieren wird, wenn jeder Thread seine eigene schafft, in dem Prozess die Arbeit von anderen Threads clobbering?

Der Code, den Sie anzeigen, sollte sich in einem synchronized Block befinden, oder er ist nicht threadsicher.

+0

Es besteht keine Notwendigkeit für eine Strategie, die Fabrik nur einmal anzurufen. Eine Suche muss in jedem Fall durchgeführt werden, und ein Singleton-Muster stellt sicher, dass das Handle für die aktuelle (eine) Instanz immer zurückgegeben wird. Alle aufrufenden Threads erhalten dieselbe Referenz auf dieselbe Factory. Es besteht keine Notwendigkeit für eine Synchronisierung und zusätzlich wird ein Leistungsproblem erzeugt, da Threads kämpfen, um den Monitor zu erhalten, der sowieso nur den einzigen Fabrik-Handle zurückgegeben wird. –

+0

@DarrellTeague Wie ermittelt ein Anrufer, ob eine Fabrik freigegeben ist oder nicht und ob sie daher von diesem Anrufer geschlossen werden soll? Laut OP ist die Initialisierung der Fabrik zeitaufwendig. Das Erstellen mehrerer unnötiger Instanzen während der gesamten Lebensdauer der Anwendung scheint ein weitaus größeres Leistungsproblem zu sein als jeder Konflikt, der in dem Moment auftritt, in dem eine Variable nach der Initialisierung einfach synchronisiert und gelesen wird. – erickson

+0

Ich verwende Persistence.createEntityManagerFactory (ID) als statische, singuläre Instanz und es ist threadsicher. Was zu schützen ist, sind die Instanzen von EntityManager aus EMF.createEntityManager(). Dies muss in einer bestimmten Methode geschehen, gefolgt von der Erstellung einer Transaktion, der Übergabe und dem anschließenden Aufruf von close() im EntityManager (identisch mit J2EE DataSource Connection.close()), um die JDBC-Verbindung wieder an den Pool-Manager zu übergeben). Dies ist das gleiche Muster wie Pre-JPA mit DataSource und JDBC Connection über einen Pool-Manager. –

3

Ich sehe mir keine Probleme mit dem statischen Block Ansatz. Oder Sie können in der unten Weise das gleiche tun, die ein Singleton-Muster mit Doppelverriegelungskontrolle ist

public class JPAHelper { 

private static JPAHelper myHelper = new JPAHelper(); 
private static EntityManagerFactory myFactory = null; 

/** 
    * Private constructor. Implementing synchronization with double-lock check 
    */ 
private JPAHelper() { 

    if(myFactory == null) { 
    synchronized (JPAHelper.class) { 

    // This second check will be true only for the first thread entering the block incase 
    // of thread race 
    if(myFactory == null) { 
    myFactory = Persistence.createEntityManagerFactory("MyUnit"); 
    } 
    } 
    } 
} 

/** 
    * Static Accessor Method 
    * @return 
    */ 
public static JPAHelper getInstance() { 
    if(myHelper == null) { 
    myHelper = new JPAHelper(); 
    } 
    return myHelper; 
} 


public EntityManagerFactory getJPAFactory() { 
    return myFactory; 
} 

Und Sie

EntityManager myManager = JPAhelper.getInstance().getJPAFactory().createEntityManager(); 
+0

Das Singleton-Muster wird empfohlen, aber es besteht keine Notwendigkeit (oder kein Effekt), ein doppelt geprüftes Schloss zu verwenden. createEntityManagerFactory() ist bereits Thread-sicher und gibt in jedem Fall den gleichen Handle an die gleiche Factory zurück. Ein korrekt geschriebener Singleton, der die Fabrik statisch lädt, gibt immer den einen Griff zurück. Double-Check-Sperren funktionieren nicht in realen Anwendungscontainern/Systemen. Siehe hierzu: [http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) –

+0

Double-Check-Schlösser ARBEIT in realen Anwendungen. Der obige Code sollte myFactory als volatil deklarieren. Derselbe Artikel, auf den verwiesen wird, erläutert "Doppel-Checked Locking mit Volatile reparieren" – mcoolive

0

Die Antwort auf die Frage stellen wird, ist: JA, createEntityManagerFactory() ist thread-safe basierend auf der Klassendokumentation, den Quell- und realen Anwendungsergebnissen.

Die Antwort Singleton-Muster ist korrekteste einen zusätzlichen Aufruf zur Vermeidung der eine Fabrik Griff effizient abzurufen, aber beachten Sie, dass keine Notwendigkeit für eine Vier-Augen-Sperre ist wie früher kommentiert.

Verwandte Themen