Cree JPA EntityManager sin el archivo de configuración persistence.xml
-
22-09-2019 - |
Pregunta
¿Hay alguna manera de inicializar el EntityManager
sin una unidad de persistencia definida?¿Puedes proporcionar todas las propiedades requeridas para crear un administrador de entidad?necesito crear el EntityManager
a partir de los valores especificados por el usuario en tiempo de ejecución.Actualizando el persistence.xml
y recompilar no es una opción.
¡Cualquier idea sobre cómo hacer esto es más que bienvenida!
Solución
¿Hay alguna manera de inicializar el
EntityManager
sin una unidad de persistencia definida?
Deberías definir al menos una unidad de persistencia en el persistence.xml
descriptor de implementación.
¿Puedes proporcionar todas las propiedades necesarias para crear un
Entitymanager
?
- El atributo de nombre es obligatorio.Los demás atributos y elementos son opcionales.(especificación APP).Entonces este debería ser más o menos tu mínimo.
persistence.xml
archivo:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
SOME_PROPERTIES
</persistence-unit>
</persistence>
En entornos Java EE, el
jta-data-source
ynon-jta-data-source
Los elementos se utilizan para especificar. el nombre JNDI global de la fuente de datos JTA y/o no JTA para ser utilizado por el proveedor de persistencia.
Entonces, si su servidor de aplicaciones de destino admite JTA (JBoss, Websphere, GlassFish), su persistence.xml
parece:
<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), su persistence.xml
parece:
<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), normalmente definiría las propiedades de proveedor, controlador, URL, usuario y contraseña de JPA. Pero El nombre de la propiedad depende del proveedor JPA.Entonces, para Hibernate como proveedor JPA, su persistence.xml
el archivo 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 de
RESOURCE_LOCAL
asume que se proporcionará una fuente de datos que no sea de 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, se aplicará un valor predeterminado deRESOURCE_LOCAL
se puede suponer.
- 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 el
EntityManager
a partir 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);
Otros consejos
Sí se puede sin utilizar ningún archivo XML usando la primavera como esta dentro de una clase @Configuration (o su equivalente XML de configuración de la primavera):
@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;
}
I fue capaz de crear un EntityManager
con Hibernate y PostgreSQL puramente utilizando código Java (con una configuración de primavera) 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 construyó, y luego vuelve getObject()
null
y que están persiguiendo después NullPointerException
s durante todo el día. >: - (
A continuación, trabajó 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();
Cuando mi entidad fue la siguiente:
@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
}
Aquí hay una solución sin primavera.
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;
}
};
}
Con JPA puro, asumiendo que tiene una aplicación PersistenceProvider
(por ejemplo, Hibernate), se puede utilizar el PersistenceProvider # createContainerEntityManagerFactory (información PersistenceUnitInfo, mapa mapa) método para arrancar un EntityManagerFactory
sin necesidad de una persistence.xml
.
Sin embargo, es molesto que hay que implementar la interfaz PersistenceUnitInfo
, por lo que es mejor usar la primavera o de hibernación, que tanto apoyo de programa previo APP sin un archivo persistence.xml
:
this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
this.persistenceUnitInfo,
getJpaPropertyMap()
);
Cuando el PersistenceUnitInfo se implementa por el resorte-específico clase MutablePersistenceUnitInfo .
este artículo para una buena demostración de cómo se puede lograr este objetivo con Hibernate.
DataNucleus APP que usar también tiene una forma de hacer esto en sus documentos . No hay necesidad de Primavera, o feo implementación de PersistenceUnitInfo
.
Simplemente haga 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);