2010-09-14 12 views
7

Estoy trabajando en una aplicación que necesita realizar algunas operaciones de base de datos.Cómo crear un hilo seguro EntityManagerFactory?

creé una variable estática para EntityManagerFactory y inicializado en el método que es llamada por la aplicación

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

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

este hilo es seguro? si creo EntityManagerFactory en un bloque sincronizado, el número de subidas en espera aumenta y bloquea la aplicación.

Miré los documentos para ver si Persistence.createEntityManagerFactory es seguro para subprocesos sin éxito.

Indiqueme los recursos correctos.

Respuesta

11

Una manera fácil de "resolver" esto sería usar una clase auxiliar (a la HibernateUtil) e inicializar el EntityManagerFactory en un bloque de inicialización estático. Algo como esto:

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); 
     } 
    } 

    ... 

} 

Y el "problema" se ha ido.

+0

Lo hice antes. Pero, algunas personas sienten que no es una buena práctica inicializarse en los bloques estáticos. ¿Está bien? –

+1

@Vanchinathan Ese es realmente un enfoque típico cuando no estás en un entorno administrado y no veo nada de malo en él. Ahora, si proporciona algunos argumentos en contra, podríamos discutirlos, pero hasta entonces, mantengo esta recomendación. –

+2

El único argumento que siempre obtengo es que tener código en los bloques estáticos dificulta la prueba. Seguimos estrictamente el desarrollo de Test Driven. Entonces necesito algo que también sea más fácil de probar. –

1

Debe colocar bloqueos en un objeto cuando está creando la fem. Usted puede poner los bloqueos en el objeto emf en sí, pero esa no es la mejor práctica. Crear otro objeto:

private object factoryLockObject = new object(); 

y poner cerraduras en él durante la creación de la fábrica

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

Eso ayuda?

+0

¿Qué diferencia hace eso? Lo probaré. –

+0

Esto agregará un bloqueo de objeto en el FactoryLockObject y hará que cualquier otro subproceso que desee acceder a él espere hasta que se levante el bloqueo (en la llave de cierre final). http://csharpindepth.com/Articles/General/Singleton.aspx – Brad

+0

Aún así, ¿no bloqueará los hilos? Si Persistence.createEntityManagerFactory ("ejemplo"); es seguro para subprocesos, entonces no necesito sincronizarlo. –

1

Si createEntityManagerFactory() es seguro para subprocesos o no, necesita alguna estrategia para que se invoque solo una vez. En otras palabras, esa pregunta es irrelevante, porque debes asegurarte de que solo un hilo la llame.

Si simplemente está esperando a que otro hilo cree la fábrica, su aplicación se bloquea, ¿qué pasará cuando cada hilo crea lo suyo, arruinando el trabajo de otros hilos en el proceso?

El código que muestra debe estar dentro de un bloque synchronized, o no es seguro para subprocesos.

+0

No hay necesidad de una estrategia para llamar solo a la fábrica una vez. En cualquier caso, se debe realizar una búsqueda y un patrón Singleton asegurará que siempre se devuelva el identificador a la instancia actual (una). Todos los hilos de llamada obtendrán la misma referencia a la misma fábrica. No hay necesidad de sincronización y, además, creará un problema de rendimiento a medida que los hilos están luchando para lograr que el monitor simplemente sea devuelto como el único manejador de fábrica de todos modos. –

+0

@DarrellTeague ¿Cómo determina una persona que llama si una fábrica se comparte o no y, por lo tanto, si la persona que llama debe cerrarla? Según el OP, la inicialización de la fábrica lleva mucho tiempo. La creación de múltiples instancias innecesarias a lo largo de la vida de la aplicación parece un problema de rendimiento mucho más grande que cualquier contención que pueda ocurrir durante el instante que lleva simplemente sincronizar y leer una variable, una vez inicializada. – erickson

+0

Estoy usando Persistence.createEntityManagerFactory (ID) como una instancia estática, singular y es seguro para subprocesos. Lo que debe protegerse con seguridad son las instancias de EntityManager de EMF.createEntityManager(). Eso debe estar dentro de un método determinado, seguido de crear una transacción, confirmarla y luego llamar a close() en EntityManager (que es lo mismo que J2EE DataSource Connection.close() para volver a liberar la conexión JDBC al administrador de grupo) Este es el mismo patrón que el pre-JPA con DataSource y la conexión JDBC a través de un administrador de grupo. –

3

No veo ningún problema con el enfoque de bloque estático. O bien, puede hacer lo mismo en la forma de abajo que es un patrón Singleton con registro de entrada doble cerradura

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; 
} 

Y se llama

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

Se recomienda el patrón Singleton, pero no hay necesidad (o efecto) de utilizar un bloqueo con doble verificación. createEntityManagerFactory() ya es seguro para subprocesos y devolverá el mismo identificador a la misma fábrica en cualquier caso. Un Singleton correctamente escrito que cargue la fábrica de forma estática siempre devolverá el mango. Los bloqueos de doble verificación no funcionan en contenedores/sistemas de aplicaciones del mundo real. Ver esto: [http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) –

+0

Verifique dos veces WORK en las aplicaciones del mundo real. El código anterior debería declarar myFactory como volátil. El mismo artículo al que se hace referencia explica "Fijar el bloqueo doblemente verificado usando volátiles" – mcoolive

0

La respuesta a la pregunta es: sí, createEntityManagerFactory() es seguro para subprocesos según la documentación de la clase, los resultados de la aplicación fuente y del mundo real.

La respuesta del patrón Singleton es la más correcta para evitar una llamada adicional para recuperar la manija de fábrica eficientemente, pero tenga en cuenta que no hay necesidad de un bloqueo de doble verificación como se comentó anteriormente.

Cuestiones relacionadas