Créer JPA EntityManager sans fichier de configuration persistence.xml
-
22-09-2019 - |
Question
Est-il possible d'initialiser le EntityManager
sans unité de persistance définie? Pouvez-vous donner toutes les propriétés requises pour créer un gestionnaire d'entités? Je dois créer le EntityManager
à partir des valeurs spécifiées par l'utilisateur lors de l'exécution. Mise à jour du persistence.xml
et recompilation n'est pas une option.
Toute idée sur la façon de le faire est plus que bienvenue!
La solution
Est-il possible d'initialiser le
EntityManager
sans unité de persistance définie?
Vous devez définir au moins une unité de persistance dans le descripteur de déploiement persistence.xml
.
Pouvez-vous donner toutes les propriétés requises pour créer un
Entitymanager
?
- L'attribut name est nécessaire. Les autres attributs et éléments sont facultatifs. (Spécification APP). Donc, cela devrait être plus ou moins votre fichier
persistence.xml
minimal:
<persistence>
<persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
SOME_PROPERTIES
</persistence-unit>
</persistence>
Dans les environnements Java EE, les
jta-data-source
et les élémentsnon-jta-data-source
sont utilisés pour spécifier le nom JNDI global de la JTA et / ou non-JTA source de données pour être utilisé par le fournisseur de persistance.
Donc, si votre cible Application Server prend en charge JTA (JBoss, Websphere, GlassFish), votre persistence.xml
ressemble à:
<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 votre cible Application Server ne, votre persistence.xml
ressemble supporte pas JTA (Tomcat):
<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 votre source de données ne sont pas liés à un JNDI global (par exemple, en dehors d'un conteneur Java EE), de sorte que vous définirait généralement fournisseur JPA, pilote, url, utilisateur et mot de passe propriétés. Mais nom de la propriété dépend du fournisseur JPA. Ainsi, pour Hibernate en tant que fournisseur JPA, votre fichier persistence.xml
volonté ressemble à:
<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>
Type de transaction Attribut
En général, dans les environnements Java EE, une transaction de type de
RESOURCE_LOCAL
suppose qu'une source de données non-JTA sera fourni. Dans un environnement Java EE, si cet élément n'est pas spécifié, la valeur par défaut est JTA. Dans un environnement Java SE, si cet élément n'est pas spécifié, un défaut deRESOURCE_LOCAL
peut supposer.
- Pour assurer la portabilité d'une application Java SE, il est nécessaire d'énumérer explicitement les classes de persistance gérées qui sont inclus dans l'unité de persistance (JPA)
Je dois créer le
EntityManager
à partir des valeurs spécifiées par l'utilisateur lors de l'exécution
Il faut donc utiliser ceci:
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);
Autres conseils
Oui, vous pouvez sans utiliser un fichier XML à l'aide du printemps comme celui-ci dans une classe @Configuration (ou son xml de configuration ressort équivalent):
@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;
}
Je suis en mesure de créer un EntityManager
avec Hibernate et PostgreSQL uniquement à l'aide du code Java (avec une configuration de ressort) qui suit:
@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();
}
L'appel à LocalContainerEntityManagerFactoryBean.afterPropertiesSet()
est essentiel car sinon l'usine se construit jamais, puis getObject()
retours null
et vous pourchassent après NullPointerException
s toute la journée. >: - (
Il a ensuite travaillé avec le code suivant:
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();
Si mon entité était la suivante:
@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
}
Voici une solution sans ressort.
Constantes sont prises à partir 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();
Et le PersistenceUnitInfo
infâme
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;
}
};
}
JPA simple, en supposant que vous avez une implémentation PersistenceProvider
(par exemple Hibernate), vous pouvez utiliser le PersistenceProvider # createContainerEntityManagerFactory (PersistenceUnitInfo informations, carte carte) méthode pour bootstrap une EntityManagerFactory
sans avoir besoin d'un persistence.xml
.
Cependant, il est ennuyeux que vous devez implémenter l'interface PersistenceUnitInfo
, vous feriez mieux d'utiliser Spring ou Hibernate qui à la fois le soutien bootstrapping JPA sans fichier persistence.xml
:
this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
this.persistenceUnitInfo,
getJpaPropertyMap()
);
Si le PersistenceUnitInfo est mis en œuvre par le spécifique au printemps classe de MutablePersistenceUnitInfo.
Consultez cet article pour une belle démonstration de la façon dont vous pouvez atteindre cet objectif avec Hibernate.
DataNucleus JPA que j'utiliser a aussi une façon de le faire dans sa docs . Pas besoin pour le printemps ou la mise en œuvre de laide PersistenceUnitInfo
.
Il suffit de faire comme suit
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);