2011-12-09 14 views
29

Tengo este requisito para agregar clases de entidad a la unidad de persistencia en tiempo de ejecución en lugar de especificarlas todas en persistence.xml. ¿Alguien puede ayudarme con lo mismo?Agregar clases de entidad dinámicamente en tiempo de ejecución

Soy consciente de que Hibernate tiene su propio mecanismo de hacer lo mismo usando:

AnnotationConfiguration.addAnnotatedClass(Class), etc - También puede agregar configuración de hibernación (*.hbm.xml) archivos mediante programación.

El requisito es que sin reiniciar el servidor de la aplicación, debería ser capaz de seguir agregando dinámicamente clases de entidad/sus archivos de configuración (mapeo) a la unidad de persistencia.

Pero la solución para agregar programáticamente clases de entidad/archivos de configuración en el tiempo de ejecución a la unidad de persistencia no debe ser específica para una implementación de JPA.

Respuesta

30

JPA no ofrece esta característica todavía. Aquí hay tres opciones que puede echa un vistazo a:

EDIT:

Si el proveedor JPA es Hibernate, desde Hibernate 4.0, es posible pasar entidades directamente a este proveedor JPA sin declararlos en el archivo persistence.xml. Hibernate se encargará de las entidades sobre la marcha.

EDIT:

Aquí está un ejemplo de configuración de JPA 2.1 + Hibernate 4.3.7.Final sin declarar cualesquiera entidades:

META-INF/persistencia.xml

<?xml version="1.0" encoding="UTF-8" ?> 
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" 
    version="2.1"> 

    <persistence-unit name="my-persistence-unit" 
     transaction-type="RESOURCE_LOCAL"> 
     <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> 
     <properties> 
      <!-- Database Properties --> 
      <property name="javax.persistence.jdbc.url" 
       value="jdbc:postgresql://localhost:5432/my-database" /> 
      <property name="javax.persistence.jdbc.user" value="login" /> 
      <property name="javax.persistence.jdbc.password" value="password" /> 

      <!-- Hibernate Properties --> 
      <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" /> 
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> 
      <property name="hibernate.default_schema" value="public" /> 
      <property name="hibernate.hbm2ddl.auto" value="update" /> 
      <property name="hibernate.show_sql" value="false" /> 
      <property name="hibernate.format_sql" value="true" /> 

      <!-- Connection Pool --> 
      <property name="hibernate.c3p0.min_size" value="5" /> 
      <property name="hibernate.c3p0.max_size" value="20" /> 
      <property name="hibernate.c3p0.timeout" value="500" /> 
      <property name="hibernate.c3p0.max_statements" value="50" /> 
      <property name="hibernate.c3p0.idle_test_period" value="2000" /> 
     </properties> 
    </persistence-unit> 

</persistence> 

Referencias

+2

¿Podría darme información sobre cómo configurar Hibernate para aceptar entidades sin declararlas? Parece que esto no es un comportamiento predeterminado. – Rafal

+0

Lo siento por la votación negativa, después de probar con otro Tomcat funcionó como un encanto.Es un comportamiento predeterminado y creo que fue causado por el plugin gradle-tomcat>. < – lucasvc

4

llego tarde a la fiesta, pero creo que esto va a salvar algunas personas un poco de dolor de cabeza. Implementé el escaneo classpath para JPA puro (no se necesita primavera, etc.) que se integra con, p. guice-persist si es necesario también.

Esto es lo que necesita hacer.

En primer lugar, cambiar la persistence.xml y añadir su propia aplicación, como:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
     http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" 
     version="2.1"> 

<persistence-unit name="my.persistence.unit" transaction-type="RESOURCE_LOCAL"> 

    <provider>my.custom.package.HibernateDynamicPersistenceProvider</provider> 

    <exclude-unlisted-classes>true</exclude-unlisted-classes> 

    <properties> 
     <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> 
     <property name="hibernate.max_fetch_depth" value="30" /> 
     <property name="hibernate.hbm2ddl.auto" value="update" /> 
     <property name="hibernate.show_sql" value="true" /> 
    </properties> 
</persistence-unit> 

A fin de que los proveedores de ser reconocido, tendrá que hacerla visible. JPA descubre el uso del mecanismo de carga de servicio, por lo que añadir:

/src/main/resources/META-INF/services/javax.persistence.spi.PersistenceProvider 

Este archivo tiene exactamente una línea:

my.custom.package.HibernateDynamicPersistenceProvider 

Por último añadir su propio proveedor y basarlo en la HibernateProvider (Me baso en ese ya que quiero utilizar hibernación):

public class HibernateDynamicPersistenceProvider extends HibernatePersistenceProvider implements PersistenceProvider { 

    private static final Logger log = Logger.getLogger(HibernateDynamicPersistenceProvider.class); 

    public static final String CUSTOM_CLASSES = "CUSTOM_CLASSES"; 

    @Override 
    protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
      PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader) { 

     if(persistenceUnitDescriptor instanceof ParsedPersistenceXmlDescriptor) { 
      ParsedPersistenceXmlDescriptor tmp = (ParsedPersistenceXmlDescriptor) persistenceUnitDescriptor; 
      Object object = integration.get("CUSTOM_CLASSES"); 
     } 

     return super.getEntityManagerFactoryBuilder(persistenceUnitDescriptor, integration, providedClassLoader); 
    } 

    protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader) { 
     log.debug(String.format("Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", persistenceUnitName)); 

     final Map integration = wrap(properties); 
     final List<ParsedPersistenceXmlDescriptor> units; 
     try { 
      units = PersistenceXmlParser.locatePersistenceUnits(integration); 
     } 
     catch (Exception e) { 
      log.debug("Unable to locate persistence units", e); 
      throw new PersistenceException("Unable to locate persistence units", e); 
     } 

     log.debug(String.format("Located and parsed %s persistence units; checking each", units.size())); 

     if (persistenceUnitName == null && units.size() > 1) { 
      // no persistence-unit name to look for was given and we found multiple persistence-units 
      throw new PersistenceException("No name provided and multiple persistence units found"); 
     } 

     for (ParsedPersistenceXmlDescriptor persistenceUnit : units) { 
      log.debug(String.format(
        "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]", 
        persistenceUnit.getName(), 
        persistenceUnit.getProviderClassName(), 
        persistenceUnitName 
      )); 

      final boolean matches = persistenceUnitName == null || persistenceUnit.getName().equals(persistenceUnitName); 
      if (!matches) { 
       log.debug("Excluding from consideration due to name mis-match"); 
       continue; 
      } 

      // See if we (Hibernate) are the persistence provider 

      String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration); 

      if (! ProviderChecker.isProvider(persistenceUnit, properties) && !(this.getClass().getName().equals(extractRequestedProviderName))) { 
       log.debug("Excluding from consideration due to provider mis-match"); 
       continue; 
      } 

      return getEntityManagerFactoryBuilder(persistenceUnit, integration, providedClassLoader); 
     } 

     log.debug("Found no matching persistence units"); 
     return null; 
    } 
} 

tuve que sobrescribir los métodos 2, en primer lugar:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
      PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader) 

Este es el método de interceptación. Agregué una propiedad personalizada "CUSTOM_CLASSES" que realmente debería llamarse "CUSTOM_PACKAGES" que enumerará todos los paquetes que necesitan ser escaneados. En este punto, soy un poco vago y omitiré el escaneo del classpath actual, pero puede hacerlo usted mismo, es bastante directo. A continuación, puede llamar al

tmp.addClasses("class1", "class2"); 

Donde las clases son las que descubrió.

El segundo método que estamos primordial es:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader) 

Esto es porque el proveedor estamos extendiendo es codificado a sólo permiten clases de hibernación para crear un EMF. Como tenemos una clase personalizada que intercepta la construcción, nuestros nombres no se suman. Así que agregué:

String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration); 

if (! ProviderChecker.isProvider(persistenceUnit, properties) && !(this.getClass().getName().equals(extractRequestedProviderName))) { 
     log.debug("Excluding from consideration due to provider mis-match"); 
     continue; 
} 

Esto amplía la verificación de hibernación normal para incluir también mi proveedor personalizado para que sea válido.

Wola, hemos terminado, ahora tiene hibernate habilitado escaneado classpath con JPA.

+0

Algunas preguntas: ¿Por qué declaraste CUSTOM_CLASSES constante y nunca lo usaste? ¿Qué versión de Hibernate estás usando? – Stephan

+0

CUSTOM_CLASSES nunca se usa porque es solo una configuración de ejemplo. Yo no lo uso en realidad. Utilizo guice para inyectar a mi proveedor las entidades reales que quiero en su lugar. Uso Hibernate 5.1.0.Final Puede usar CUSTOM_CLASSES para, p. Ej. pase los nombres de los paquetes y luego implemente su propia versión del paquete discovery – pandaadb

+0

. Desde Hibernate 4.0, es posible pasarle entidades sin declararlas. ¿Desapareció esta función en 5.1.0.Final? – Stephan

Cuestiones relacionadas