I have an application running on Websphere Liberty which should compare tables from 2 databases/schemas.
The user should be able to input the connection data, like the host and the credentials.
I'm using Hibernate to access the application DB.
I've tried to use multiple Persistence Units, one for the application DB and one for all other DBs.
But i got 2 problems:
i get an "Illegal attempt to enlist multiple 1PC XAResources"
error sometimes
can query the 2 DBs with
user-submitted credentials, but i get no results except if i connect
to the same db listed in server.xml file as DataSource
This are the DataSources on the server.xml on the server (dbs are oracle dbs)
<dataSource id="MyAppDS" jndiName="jdbc/MyDS" type="javax.sql.ConnectionPoolDataSource">
<jdbcDriver javax.sql.ConnectionPoolDataSource="oracle.jdbc.pool.OracleConnectionPoolDataSource" libraryRef="OracleSQLLib"/>
<connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
<properties password="..." url="jdbc:oracle:thin:#...:1521:..." user="..."/>
</dataSource>
<dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.ConnectionPoolDataSource">
<jdbcDriver javax.sql.ConnectionPoolDataSource="oracle.jdbc.pool.OracleConnectionPoolDataSource" libraryRef="OracleSQLLib"/>
<connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
<properties password="..." url="jdbc:oracle:thin:#127.0.0.1:1521:XE" user="..."/>
</dataSource>
This is persistence.xml on the EJB module
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
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">
<persistence-unit name="main-persistence">
<jta-data-source>jdbc/MyDS</jta-data-source>
<class>classes...</class>
<properties>
<property name="hibernate.transaction.jta.platform"
value="org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.Oracle9iDialect" />
<property name="hibernate.temp.use_jdbc_metadata_defaults"
value="false" />
</properties>
</persistence-unit>
<persistence-unit name="other-persistence" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>jdbc/OtherOracle</non-jta-data-source>
<class>classes...</class>
<properties>
<property name="hibernate.transaction.jta.platform"
value="org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.Oracle9iDialect" />
<property name="hibernate.temp.use_jdbc_metadata_defaults"
value="false" />
</properties>
</persistence-unit>
On the Java Bean i use the EntityManagerFactory
#PersistenceUnit(unitName = "other-persistence")
private EntityManagerFactory emf;
And i create the entity manager with custom credentials like this
Map<String, String> properties = new HashMap<String, String>();
properties.put("hibernate.connection.driver_class", "oracle.jdbc.OracleDriver");
properties.put("hibernate.connection.url", myCustomCreatedConnectionUrl);
properties.put("hibernate.connection.username", customUser);
properties.put("hibernate.connection.password", customPassword);
properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle9iDialect");
properties.put("hibernate.show-sql", "true");
EntityManager entityManager = emf.createEntityManager(properties);
If i check EntityManager properties with getProperties everything seems to be right. But the queries works only if the credentials/host are = to the DataSource. Otherwise i get no results (but no errors)
What could the problem be?
Is there a way to use just one Persistence Unit but with custom host/credentials for different queries?
Regarding the first error, ""Illegal attempt to enlist multiple 1PC XAResources", this occurs because you are using both resources within the same transaction. I can see <non-jta-data-source>jdbc/OtherOracle</non-jta-data-source> in your configuration, which indicates that you might have intended for jdbc/OtherOracle to be a non-enlisting resource. To make that work, the data source itself needs to be configured as non-enlisting. You can do this with the transactional="false" attribute as follows:
<dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.ConnectionPoolDataSource" transactional="false">
...
On the other hand, if you actually want both resources to enlist in the transaction, then you need to use XADataSource rather than ConnectionPoolDataSource. Here is an example of how to do that (notice that both type type under dataSource and the attribute & class under jdbcDriver must be updated for this:
<dataSource id="MyAppDS" jndiName="jdbc/MyDS" type="javax.sql.XADataSource">
<jdbcDriver javax.sql.XADataSource="oracle.jdbc.xa.client.OracleXADataSource" libraryRef="OracleSQLLib"/>
<connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
<properties password="..." url="jdbc:oracle:thin:#...:1521:..." user="..."/>
</dataSource>
<dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.XADataSource">
<jdbcDriver javax.sql.XADataSource="oracle.jdbc.xa.client.OracleXADataSource" libraryRef="OracleSQLLib"/>
<connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
<properties password="..." url="jdbc:oracle:thin:#127.0.0.1:1521:XE" user="..."/>
</dataSource>
In the second question, I think you are saying the different users cannot see the data. Could this be because the different database users are using different schema and do not have access each others' data? If you can get all of the users using a common schema, then adding #Table(schema="YOUR_SCHEMA_NAME") to the JPA #Entity might help. JavaDoc for the Table annotation can be found here.
I have recently had the same request where a user can connect to application and enter their database details. From this point on every JPA Query is executed on this new connection.
The additional problem was that there were more than 100 bases to choose from and creating all datasources at start was not a good idea.
We have thus created a single RoutingDatasource that manages the JDBC connections. We were heavily inspired by Spring's implementation org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.
The main idea is:
You extend AbstractDataSource and getConnection() method which is called on every JPA query. You are basically tinkering the JDBC layer under the JPA layer.
You then create a new datasource according to what your user entered
Set Your routing datasource as main datasource for your entity manager
Optionally add some caching to avoid recreating datasources every time (or implement some pool)
Here is a demo class:
public class RoutingDataSource extends AbstractDataSource {
...
...
#Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
DataSource determineTargetDataSource() {
EmployeeDatabase lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.datasources.get(lookupKey);
if (dataSource == null) {
logger.debug("Datasource not found. Creating new one");
SQLServerDataSource newDatasource = new SQLServerDataSource();
newDatasource.setURL("jdbc:sqlserver://" + lookupKey.getDatabaseHost());
newDatasource.setPassword(dbPass);
datasources.put(lookupKey, newDatasource);
dataSource = newDatasource;
} else {
logger.debug("Found existing database for key " + lookupKey);
}
logger.debug("Connecting to " + dataSource);
return dataSource;
}
}
Related
I have a Spring Batch with Spring Boot application with an Oracle Wallet to connect and open JPA to handle persistence. I have some entities the primary keys of which are managed with sequences in the database, like this one:
CREATED 11/11/11
LAST_DDL_TIME 11/11/11
SEQUENCE_OWNER EXAMPLE
SEQUENCE_NAME EXAMPLE_SEQ
MIN_VALUE 1
MAX_VALUE 9999999999999999999999999999
INCREMENT_BY 1
CYCLE_FLAG N
ORDER_FLAG N
CACHE_SIZE 20
LAST_NUMBER 1111
and the corresponding entity annotation:
#Id
#SequenceGenerator(name="seq_examples", sequenceName="EXAMPLE_SEQ", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_examples")
#Column (name="ID_EXAMPLE", nullable=false)
private Integer id_example;
With this code JPA is trying constantly to execute an ALTER SEQUENCE, reading a lot about this, I have got two ways to solve the problem, with Spring Boot config file or with the persistence.xml config file.
I have tried both of them: config ->
#Configuration
public class ContextConfiguration {
#Bean(name="springtest_entitymanager")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean(
#Qualifier("vendorAdapter") JpaVendorAdapter jpaVendorAdapter,
#Value("${${enviroment}.databaseSchema}") String databaseSchema,
#Qualifier("datasourceWalletExampleDB") DataSource dataSource,
//#Value("${ConnectionFactoryProperties}") String ConnectionFactoryProperties,
#Value("${${enviroment}.openjpa.ConnectionFactoryProperties}") String
ConnectionFactoryProperties,
#Value("${${enviroment}.openjpa.log}") String logLevel
){
Map<String, String> jpaProperties = new HashMap<String, String>();
jpaProperties.put("openjpa.jdbc.Schema", databaseSchema);
jpaProperties.put("openjpa.Log", logLevel);
jpaProperties.put("openjpa.ConnectionFactoryProperties", ConnectionFactoryProperties);
jpaProperties.put("openjpa.jdbc.DBDictionary.disableAlterSequenceIncrementBy", "true");
//debug only
//jpaProperties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new
LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setPersistenceUnitName("pu_Example");
localContainerEntityManagerFactoryBean.setDataSource(dataSource);
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
localContainerEntityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return localContainerEntityManagerFactoryBean;
}
...
and persistence ->
<persistence-unit name="pu_Example"
transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>exampleapp.dao.model.Example</class>
<properties>
<property name="openjpa.Log" value="DefaultLevel=ERROR" />
<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true,
PrettyPrintLineLength=72" />
<property name="openjpa.jdbc.Schema" value="EXAMPLE" />
<property name="openjpa.jdbc.DBDictionary" value="DisableAlterSeqenceIncrementBy=true" />
</properties>
</persistence-unit>
But it still keeps doing the alter sequence statement, what more can I do?
Some info that I have already read:
https://issues.apache.org/jira/browse/OPENJPA-2450
https://www-01.ibm.com/support/docview.wss?uid=swg1PI05956
How does the JPA #SequenceGenerator annotation work
Thanks to everybody who read this
Both properties(persistence.xml / configuration class) works fine, but I had to run the application at least one time with alter sequence privilege, probably some pk was not in sync between JPA and the DB in the first run of it, i think because of the DB cache. In this case the DB was migrated from another enviroment.
After the run with alter sequence privileges, the privileges were removed and after few more runs, i have not seen any more issue.
Usefull info that i found about caching in sequences and my help others:
https://developer.ibm.com/answers/questions/503873/why-does-the-jpa-sequence-generator-increase-more/
http://www.dba-oracle.com/t_sequence_caching.htm
https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/seq-generator.html
We have a Service which is #Stateful. Most of the Data-Operations are atomic, but within a certain set of functions We want to run multiple native queries within one transaction.
We injected the EntityManager with a transaction scoped persistence context. When creating a "bunch" of normal Entities, using em.persist() everything is working fine.
But when using native queries (some tables are not represented by any #Entity) Hibernate does not run them within the same transaction but basically uses ONE transaction per query.
So, I already tried to use manual START TRANSACTION; and COMMIT; entries - but that seems to interfere with the transactions, hibernate is using to persist Entities, when mixing native queries and persistence calls.
#Stateful
class Service{
#PersistenceContext(unitName = "service")
private EntityManager em;
public void doSth(){
this.em.createNativeQuery("blabla").executeUpdate();
this.em.persist(SomeEntity);
this.em.createNativeQuery("blablubb").executeUpdate();
}
}
Everything inside this method should happen within one transaction. Is this possible with Hibernate?
When debugging it, it is clearly visible that every statement happens "independent" of any transaction. (I.e. Changes are flushed to the database right after every statement.)
I've tested the bellow given example with a minimum setup in order to eliminate any other factors on the problem (Strings are just for breakpoints to review the database after each query):
#Stateful
#TransactionManagement(value=TransactionManagementType.CONTAINER)
#TransactionAttribute(value=TransactionAttributeType.REQUIRED)
public class TestService {
#PersistenceContext(name = "test")
private EntityManager em;
public void transactionalCreation(){
em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate();
String x = "test";
em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate();
String y = "test2";
em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate();
}
}
Hibernate is configured like this:
<persistence-unit name="test">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/test</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.transaction.jta.platform"
value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.archive.autodetection" value="true" />
<property name="hibernate.jdbc.batch_size" value="20" />
<property name="connection.autocommit" value="false"/>
</properties>
</persistence-unit>
And the outcome is the same as with autocommit mode: After every native query, the database (reviewing content from a second connection) is updated immediately.
The idea of using the transaction in a manual way leads to the same result:
public void transactionalCreation(){
Session s = em.unwrap(Session.class);
Session s2 = s.getSessionFactory().openSession();
s2.setFlushMode(FlushMode.MANUAL);
s2.getTransaction().begin();
s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate();
String x = "test";
s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate();
String y = "test2";
s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate();
s2.getTransaction().commit();
s2.close();
}
In case you don't use container managed transactions then you need to add the transaction policy too:
#Stateful
#TransactionManagement(value=TransactionManagementType.CONTAINER)
#TransactionAttribute(value=REQUIRED)
I have only seen this phenomenon in two situations:
the DataSource is running in auto-commit mode, hence each statement is executed in a separate transaction
the EntityManager was not configured with #Transactional, but then only queries can be run since any DML operation would end-up throwing a transaction required exception.
Let's recap you have set the following Hibernate properties:
hibernate.current_session_context_class=JTA
transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
jta.UserTransaction=java:comp/UserTransaction
Where the final property must be set with your Application Server UserTransaction JNDI naming key.
You could also use the:
hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup
or some other strategy according to your current Java EE Application Server.
After reading about the topic for another bunch of hours while playing around with every configuration property and/or annotation I could find a working solution for my usecase. It might not be the best or only solution, but since the question has received some bookmarks and upvotes, i'd like to share what i have so far:
At first, there was no way to get it working as expected when running the persistence-unit in managed mode. (<persistence-unit name="test" transaction-type="JTA"> - JTA is default if no value given.)
I decided to add another persistence-unit to the persistence xml, which is configured to run in unmanaged mode: <persistence-unit name="test2" transaction-type="RESOURCE_LOCAL">.
(Note: The waring about Multiple Persistence Units is just cause eclipse can't handle. It has no functional impact at all)
The unmanaged persitence-context requires local configuration of the database, since it is no longer container-provided:
<persistence-unit name="test2" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>test.AEntity</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.password" value="1234"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="true" />
<property name="hibernate.jdbc.batch_size" value="20" />
<property name="hibernate.connection.autocommit" value="false" />
</properties>
</persistence-unit>
A change required to the project would now be, that you add an unitName, whenever you use the #PersistenceContext annotation to retrieve a managed instance of the EntityManager.
But be aware, that you can only use #PersistenceContext for the managed persistence-unit. For the unmanaged one, you could implement a simple Producer and Inject the EntityManager using CDI whenever required:
#ApplicationScoped
public class Resources {
private static EntityManagerFactory emf;
static {
emf = Persistence.createEntityManagerFactory("test2");
}
#Produces
public static EntityManager createEm(){
return emf.createEntityManager();
}
}
Now, in the example given in the original Post, you need to Inject the EntityManager and manually take care about transactions.
#Stateful
public class TestService {
#Inject
private EntityManager em;
public void transactionalCreation() throws Exception {
em.getTransaction().begin();
try {
em.createNativeQuery(
"INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','a')")
.executeUpdate();
em.createNativeQuery(
"INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','b')")
.executeUpdate();
em.createNativeQuery(
"INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')")
.executeUpdate();
em.createNativeQuery(
"INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')")
.executeUpdate();
AEntity a = new AEntity();
a.setName("TestEntity1");
em.persist(a);
// force unique key violation, rollback should appear.
// em.createNativeQuery(
// "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')")
// .executeUpdate();
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
}
}
}
My tests so far showed that mixing of native queries and persistence calls lead to the desired result: Either everything is commited or the transaction is rolledback as a whole.
For now, the solution seems to work. I will continue to validate it's functionality in the main project and check if there are any other sideeffects.
Another thing I need to verify is if it would be save to:
Inject both Versions of the EM into one Bean and mix usage. (First checks seem to work, even when using both ems at the same time on the same table(s))
Having both Versions of the EM operating on the same datasource. (Same data source would most likely be no problem, same tables I assume could lead to unexpected problems.)
ps.: This is Draft 1. I will continue to improve the answer and point out problems and/or drawbacks I'm going to find.
You have to add <hibernate.connection.release_mode key="hibernate.connection.release_mode" value="after_transaction" /> to your properties. After a restart should the Transaction handling work.
My goal is to have a standalone solution for building JUnit tests for local and distributed transactions using JPA over Hibernate over MySQL.
So far, I have been able to use an XADataSource to access a XAResource and manage the distributed transaction following the 2 phase commit protocol. However, I have to issue SQL statements directly.
I have been trying to do the same but using JPA 2.0 persistence.
I'm using simple-jndi to have an in-memory JNDI implementation.
However I keep getting NullPointerException whenever Hibernate tries to access the TransactionManager.
Any ideas?
What is missing from my configuration?
Here's what I would like to do:
// Create the XA datasource instance directly
MysqlXADataSource mysqlDS = new MysqlXADataSource();
mysqlDS.setServerName("localhost");
mysqlDS.setDatabaseName("test");
mysqlDS.setUser("root");
mysqlDS.setPassword("rootroot");
// setup local JNDI
final XADataSource xaDataSource = (XADataSource) mysqlDS;
InitialContext ctx = new InitialContext( );
ctx.bind("java:/ExampleDS", xaDataSource);
{
System.out.println("Lookup...");
Object o = ctx.lookup("java:/ExampleDS");
System.out.println("Test lookup: " + o);
}
// XID - transaction ID
// global transaction identifier
// - -- --
byte[] gtrid = new byte[] { 0x44, 0x11, 0x55, 0x66 };
// branch qualifier
// - ----
byte[] bqual = new byte[] { 0x00, 0x22, 0x00 };
// combination of gtrid and bqual must be unique
Xid xid1 = new com.mysql.jdbc.jdbc2.optional.MysqlXid(gtrid, bqual, 0);
// byte[] gtrid, byte[] bqual, int formatId
// before transaction
{
XADataSource xaDS = (XADataSource) ctx.lookup("java:/ExampleDS");
XAConnection xaconn = xaDS.getXAConnection();
Connection conn = xaconn.getConnection();
XAResource xares = xaconn.getXAResource();
/* the transaction begins */
System.out.println("Start transaction");
xares.start(xid1, TMNOFLAGS);
}
// JPA code
EntityManagerFactory emf;
emf = Persistence.createEntityManagerFactory("MyPersistenceUnit"); // defined in persistence.xml
EntityManager em = emf.createEntityManager();
// System.out.println("begin");
// em.getTransaction().begin();
System.out.println("new ContactBook");
ContactBook contactBook = new ContactBook("Alice");
System.out.println("addContacts");
contactBook.addContact("Alice", 100100100);
contactBook.addContact("Bob", 200200200);
contactBook.addContact("Charlie", 300300300);
System.out.println("persist");
em.persist(contactBook);
//em.flush();
// System.out.println("commit");
// em.getTransaction().commit();
// after transaction
{
XADataSource xaDS = (XADataSource) ctx.lookup("java:/ExampleDS");
System.out.println("xaDS " + xaDS);
XAConnection xaconn = xaDS.getXAConnection();
Connection conn = xaconn.getConnection();
XAResource xares = xaconn.getXAResource();
System.out.println("End transaction");
xares.end(xid1, TMSUCCESS);
// prepare, commit
System.out.print("Prepare... ");
int rc1 = xares.prepare(xid1);
System.out.println(xaString(rc1));
if (rc1 == XA_OK) {
System.out.println("Commit");
xares.commit(xid1, /*onePhase*/ false);
} else if(rc1 == XA_RDONLY) {
System.out.println("Commit no necessary - operations were read only");
} else {
throw new IllegalStateException("Unexpected case!");
}
}
Here is persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="ContactBookPersistenceUnit" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/ExampleDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="current_session_context_class" value="jta"/>
<!-- <property name="hibernate.session_factory_name" value="java:/hibernate/MySessionFactory"/> optional -->
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<property name="hibernate.connection.release_mode" value="auto"/>
<!-- setting above is important using XA-DataSource on SQLServer,
otherwise SQLServerException: The function START: has failed. No transaction cookie was returned.-->
<!--property name="hibernate.cache.use_second_level_cache" value="true"/-->
<!--property name="hibernate.cache.use_query_cache" value="true"/-->
<!-- property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.infinispan.InfinispanRegionFactory"/-->
</properties>
</persistence-unit>
I believe you cannot use JTA transactions when you are outside a Java EE container (or at least there is no easy way to do this).
Use RESOURCE-LOCAL
There is nothing that requires JTA in your example: you are just looking up a data source in a Java SE environment. I think declaring your XA data source as resource-local would solve your problem. Put this in your persistence.xml:
<persistence-unit name="ContactBookPersistenceUnit" transaction-type="RESOURCE-LOCAL">
[...]
<non-jta-data-source>java:/ExampleDS</jta-data-source>
I have not tested this code directly, but I have used a similar approach in web applications written for the Apache Tomcat server.
See also: Types of EntityManagers
Use an embedded container
Another option you may investigate involves starting an embedded Java EE container in you JUnit tests. Here are a couple of pointers, with examples for Glassfish 3:
Embededing Glassfish v3 in Unit Test
Embedded Glassfish
I want to read environment variables inside persistence.xml file.
Idea is that i don't want my database details to be read from properties file as there is a change of getting properties file override.Instead i want to read details from environment variables.
Is there any way to achieve this criteria.
Iam using Spring 3 my standalone application will be deployed in unix machine.
You can update properties in a persistence unit by supplying a Map (see this).
Conveniently, environment variables can be retrieved as a Map (see this).
Put the two together and you can dynamically update properties in your persistence unit with environment variables.
EDIT: simple example...
persistence.xml...
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>
oracle.toplink.essentials.PersistenceProvider
</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="toplink.logging.level" value="INFO"/>
<property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
<property name="toplink.jdbc.url" value="jdbc:oracle:thin:#myhost:l521:MYSID"/>
<property name="toplink.jdbc.password" value="tiger"/>
<property name="toplink.jdbc.user" value="scott"/>
</properties>
</persistence-unit>
code that updates persistence.xml "default" unit with environment variables...
Map<String, String> env = System.getenv();
Map<String, Object> configOverrides = new HashMap<String, Object>();
for (String envName : env.keySet()) {
if (envName.contains("DB_USER")) {
configOverrides.put("toplink.jdbc.user", env.get(envName)));
}
// You can put more code in here to populate configOverrides...
}
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("default", configOverrides);
I don't think this will cover EMs created via injection. Worse, I think EMs created through EMF can only be EXTENDED (eg equivalent to the annotation #PersistenceContext(type = PersistenceContextType.TRANSACTION) opposed to EXTENDED) so that if one requires a transaction EM, one must use injection.
I'm wondering if its possible to physically rewrite the persistence.xml file at runtime. Problem one being, ability to rewrite the file (permissions, being able to get to it in META-INF etc), and second, rewriting it before its opened for the first time by JPA (which I thinking happens the first time an injected EM field is actually referenced by application code)
You could use this working example.
It gets all properties defined in the persistence.xml from the PersistenceUnitInfo instance which is obtained from the EntityManagerFactory (by using eclipseLink specific implementations). These properties get replaced with the values defined in environment variables.
This is my SLSB:
#Stateless
public class MyService {
PersistenceContext(unitName = "abc")
EntityManager em;
public boolean exists(int id) {
return this.em.find(Employee.class, id) != null;
}
}
This is my persistence.xml (I'm using Glassfish v3):
<persistence>
<persistence-unit name="abc">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MyDS</jta-data-source>
<properties>
<property name="hibernate.archive.autodetection" value="class" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQLInnoDBDialect" />
</properties>
</persistence-unit>
</persistence>
Now I'm trying to create a test, using OpenEJB embedded container. This is my test class:
class MyServiceText {
#Test
public void testChecksExistence() throws Exception {
Properties properties = new Properties();
properties.setProperty(
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory"
);
InitialContext ic = new InitialContext(properties);
// actual testing skipped
}
}
I would like to use HSQL for testing. How can I instruct OpenEJB that my persistence unit "abc" has to point to HSQL during testing? Shall I create a new version of persistence.xml? Shall I use openejb.xml? I'm lost in their examples and documentation.. :(
It's a Maven-3 project.
I would suggest placing a file named jndi.properties in src/test/resources for your OpenEJB configuration. This will then be available in the test classpath, you can then use the no-argument contructor of InitialContext to lookup datasources and ejbs. An example configuration looks like this, I'm using mysql for my datasource:
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory
myDS=new://Resource?type=DataSource
myDS.JdbcDriver=com.mysql.jdbc.Driver
myDS.JdbcUrl=jdbc:mysql://127.0.0.1:3306/test
myDS.JtaManaged=true
myDS.DefaultAutoCommit=false
myDS.UserName=root
myDS.Password=root
OpenEJB should then automatically replace the reference in persistence.xml with this datasource, if this is the only datasource then this should work even if the names are different.
Edit: Persistence unit settings
According to the documentation you referenced it should also be possible to configure persistence unit properties through jndi.properties:
abc.hibernate.hbm2ddl.auto=update
abc.hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
I haven't tested this myself since I'm using mysql for both tests and normal executions, only with different database names. Please let me know if this works, I've been thinking about replacing mysql in my testcases too.