Cree JPA EntityManager sin el archivo de configuración persistence.xml

82

¿Hay alguna forma de inicializar el EntityManagersin una unidad de persistencia definida? ¿Puede dar todas las propiedades necesarias para crear un administrador de entidad? Necesito crear a EntityManagerpartir de los valores especificados por el usuario en tiempo de ejecución. Actualizar persistence.xmly recompilar no es una opción.

¡Cualquier idea sobre cómo hacer esto es más que bienvenida!

javydreamercsw
fuente

Respuestas:

57

¿Hay alguna forma de inicializar el EntityManagersin una unidad de persistencia definida?

Debe definir al menos una unidad de persistencia en el persistence.xmldescriptor de implementación.

¿Puede dar todas las propiedades necesarias para crear un Entitymanager?

  • El atributo de nombre es obligatorio. Los demás atributos y elementos son opcionales. (Especificación JPA). Entonces este debería ser más o menos su persistence.xmlarchivo mínimo :
<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        SOME_PROPERTIES
    </persistence-unit>
</persistence>

En entornos Java EE, los elementos jta-data-sourcey non-jta-data-sourcese utilizan para especificar el nombre JNDI global de la fuente de datos JTA y / o no JTA que utilizará el proveedor de persistencia.

Entonces, si su servidor de aplicaciones de destino es compatible con JTA (JBoss, Websphere, GlassFish), se persistence.xmlve así:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <jta-data-source>jdbc/myDS</jta-data-source>
    </persistence-unit>
</persistence>

Si su servidor de aplicaciones de destino no es compatible con JTA (Tomcat), se persistence.xmlve así:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <non-jta-data-source>jdbc/myDS</non-jta-data-source>
    </persistence-unit>
</persistence>

Si su fuente de datos no está vinculada a un JNDI global (por ejemplo, fuera de un contenedor Java EE), por lo general, definiría las propiedades de proveedor, controlador, URL, usuario y contraseña de JPA. Pero el nombre de la propiedad depende del proveedor de JPA. Entonces, para Hibernate como proveedor de JPA, su persistence.xmlarchivo se verá así:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>br.com.persistence.SomeClass</class>
        <properties>
            <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.ClientDriver"/>
            <property name="hibernate.connection.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
            <property name="hibernate.connection.username" value="APP"/>
            <property name="hibernate.connection.password" value="APP"/>
        </properties>
    </persistence-unit>
</persistence>

Atributo de tipo de transacción

En general, en entornos Java EE, un tipo de transacción RESOURCE_LOCALasume que se proporcionará una fuente de datos que no es JTA. En un entorno Java EE, si no se especifica este elemento, el valor predeterminado es JTA. En un entorno Java SE, si no se especifica este elemento, RESOURCE_LOCALse puede asumir un valor predeterminado de .

  • Para asegurar la portabilidad de una aplicación Java SE, es necesario enumerar explícitamente las clases de persistencia administradas que se incluyen en la unidad de persistencia (especificación JPA)

Necesito crear a EntityManagerpartir de los valores especificados por el usuario en tiempo de ejecución

Entonces usa esto:

Map addedOrOverridenProperties = new HashMap();

// Let's suppose we are using Hibernate as JPA provider
addedOrOverridenProperties.put("hibernate.show_sql", true);

Persistence.createEntityManagerFactory(<PERSISTENCE_UNIT_NAME_GOES_HERE>, addedOrOverridenProperties);
Arthur Ronald
fuente
Hola, probé su solución, pero tengo problemas, ¿podría revisar mi pregunta: stackoverflow.com/questions/3935394/…
apilador
Pero ... la pregunta era cómo crear un JPA EntityManager sin persistence.xml. Esta respuesta es buena, pero todavía usa persistence.xml, ¿verdad?
Joshua Davis
En el entorno JavaEE, al crear una EntityManagerFactory, ¿las administra EJB / JPA?
Anthony Vinay
29

Sí, puede sin usar ningún archivo xml usando un resorte como este dentro de una clase @Configuration (o su xml de configuración de resorte equivalente):

@Bean
public LocalContainerEntityManagerFactoryBean emf(){
    properties.put("javax.persistence.jdbc.driver", dbDriverClassName);
    properties.put("javax.persistence.jdbc.url", dbConnectionURL);
    properties.put("javax.persistence.jdbc.user", dbUser); //if needed

    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); //If your using eclipse or change it to whatever you're using
    emf.setPackagesToScan("com.yourpkg"); //The packages to search for Entities, line required to avoid looking into the persistence.xml
    emf.setPersistenceUnitName(SysConstants.SysConfigPU);
    emf.setJpaPropertyMap(properties);
    emf.setLoadTimeWeaver(new ReflectiveLoadTimeWeaver()); //required unless you know what your doing
    return emf;
}
Frank Orellana
fuente
¿Qué objeto es properties?
ThreaT
es un objeto java.util.Properties simple
Frank Orellana
20

Aquí hay una solución sin Spring. Las constantes se toman de org.hibernate.cfg.AvailableSettings:

entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
            archiverPersistenceUnitInfo(),
            ImmutableMap.<String, Object>builder()
                    .put(JPA_JDBC_DRIVER, JDBC_DRIVER)
                    .put(JPA_JDBC_URL, JDBC_URL)
                    .put(DIALECT, Oracle12cDialect.class)
                    .put(HBM2DDL_AUTO, CREATE)
                    .put(SHOW_SQL, false)
                    .put(QUERY_STARTUP_CHECKING, false)
                    .put(GENERATE_STATISTICS, false)
                    .put(USE_REFLECTION_OPTIMIZER, false)
                    .put(USE_SECOND_LEVEL_CACHE, false)
                    .put(USE_QUERY_CACHE, false)
                    .put(USE_STRUCTURED_CACHE, false)
                    .put(STATEMENT_BATCH_SIZE, 20)
                    .build());

entityManager = entityManagerFactory.createEntityManager();

Y el infame PersistenceUnitInfo

private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
    return new PersistenceUnitInfo() {
        @Override
        public String getPersistenceUnitName() {
            return "ApplicationPersistenceUnit";
        }

        @Override
        public String getPersistenceProviderClassName() {
            return "org.hibernate.jpa.HibernatePersistenceProvider";
        }

        @Override
        public PersistenceUnitTransactionType getTransactionType() {
            return PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }

        @Override
        public DataSource getJtaDataSource() {
            return null;
        }

        @Override
        public DataSource getNonJtaDataSource() {
            return null;
        }

        @Override
        public List<String> getMappingFileNames() {
            return Collections.emptyList();
        }

        @Override
        public List<URL> getJarFileUrls() {
            try {
                return Collections.list(this.getClass()
                                            .getClassLoader()
                                            .getResources(""));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public URL getPersistenceUnitRootUrl() {
            return null;
        }

        @Override
        public List<String> getManagedClassNames() {
            return Collections.emptyList();
        }

        @Override
        public boolean excludeUnlistedClasses() {
            return false;
        }

        @Override
        public SharedCacheMode getSharedCacheMode() {
            return null;
        }

        @Override
        public ValidationMode getValidationMode() {
            return null;
        }

        @Override
        public Properties getProperties() {
            return new Properties();
        }

        @Override
        public String getPersistenceXMLSchemaVersion() {
            return null;
        }

        @Override
        public ClassLoader getClassLoader() {
            return null;
        }

        @Override
        public void addTransformer(ClassTransformer transformer) {

        }

        @Override
        public ClassLoader getNewTempClassLoader() {
            return null;
        }
    };
}
Brice
fuente
3
¡Esto me ayudó mucho, ya que me ayudó a evitar el uso de arquillian en algunos casos de prueba!
cljk
18

Pude crear un EntityManagercon Hibernate y PostgreSQL usando puramente código Java (con una configuración de Spring) lo siguiente:

@Bean
public DataSource dataSource() {
    final PGSimpleDataSource dataSource = new PGSimpleDataSource();

    dataSource.setDatabaseName( "mytestdb" );
    dataSource.setUser( "myuser" );
    dataSource.setPassword("mypass");

    return dataSource;
}

@Bean
public Properties hibernateProperties(){
    final Properties properties = new Properties();

    properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect" );
    properties.put( "hibernate.connection.driver_class", "org.postgresql.Driver" );
    properties.put( "hibernate.hbm2ddl.auto", "create-drop" );

    return properties;
}

@Bean
public EntityManagerFactory entityManagerFactory( DataSource dataSource, Properties hibernateProperties ){
    final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource( dataSource );
    em.setPackagesToScan( "net.initech.domain" );
    em.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
    em.setJpaProperties( hibernateProperties );
    em.setPersistenceUnitName( "mytestdomain" );
    em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
    em.afterPropertiesSet();

    return em.getObject();
}

La llamada a LocalContainerEntityManagerFactoryBean.afterPropertiesSet()es esencial ya que, de lo contrario, la fábrica nunca se construye, y luego getObject()regresa nully usted está persiguiendoNullPointerException s todo el día. > :-(

Luego funcionó con el siguiente código:

PageEntry pe = new PageEntry();
pe.setLinkName( "Google" );
pe.setLinkDestination( new URL( "http://www.google.com" ) );

EntityTransaction entTrans = entityManager.getTransaction();
entTrans.begin();
entityManager.persist( pe );
entTrans.commit();

Donde mi entidad era esta:

@Entity
@Table(name = "page_entries")
public class PageEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String linkName;
    private URL linkDestination;

    // gets & setters omitted
}
Trineo
fuente
2
Buena alternativa para Hibernate.
javydreamercsw
8

Con JPA simple, asumiendo que tiene una PersistenceProviderimplementación (por ejemplo, Hibernate), puede usar el método PersistenceProvider # createContainerEntityManagerFactory (PersistenceUnitInfo info, Map map) para arrancar EntityManagerFactorysin necesidad de un persistence.xml.

Sin embargo, es molesto que tenga que implementar la PersistenceUnitInfointerfaz, por lo que es mejor que use Spring o Hibernate, que admiten el arranque de JPA sin un persistence.xmlarchivo:

this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
    this.persistenceUnitInfo, 
    getJpaPropertyMap()
);

Donde PersistenceUnitInfo es implementado por el MutablePersistenceUnitInfo específico de Spring clase .

Consulte este artículo para ver una buena demostración de cómo puede lograr este objetivo con Hibernate.

Vlad Mihalcea
fuente
El uso MutablePersistenceUnitInfono funciona ya que algunos métodos lanzan UnsupportedOperationException . Además, el artículo mencionado está un poco desactualizado: getPersistenceUnitRootUrlno se puede devolver un valor nulo, de lo contrario, Hibernate no escanea la ruta de clases (Hibernate 5.2.8).
Brice
1
Estaba un poco equivocado, el artículo no está desactualizado en ese sentido, ya que el código pasa una lista de entidades y no usa el escaneo de paquetes. Sin embargo, para el escaneo automático de entidades es necesario implementar getPersistenceUnitRootUrl o getJarFileUrls. El último se siembra en stackoverflow.com/a/42372648/48136
Brice
0

DataNucleus JPA que utilizo también tiene una forma de hacer esto en sus documentos . No es necesario Spring, ni una implementación desagradable de PersistenceUnitInfo.

Simplemente haz lo siguiente

import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jpa.JPAEntityManagerFactory;

PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
pumd.addClassName("mydomain.test.A");
pumd.setExcludeUnlistedClasses();
pumd.addProperty("javax.persistence.jdbc.url", "jdbc:h2:mem:nucleus");
pumd.addProperty("javax.persistence.jdbc.user", "sa");
pumd.addProperty("javax.persistence.jdbc.password", "");
pumd.addProperty("datanucleus.schema.autoCreateAll", "true");

EntityManagerFactory emf = new JPAEntityManagerFactory(pumd, null);

fuente
0

También puede obtener un EntityManager usando PersistenceContext o la anotación Autowired, pero tenga en cuenta que no será seguro para subprocesos.

@PersistenceContext
private EntityManager entityManager;
Ankit Sharma
fuente