Override NativeQuery at Runtime - jpa

I am using OpenJPA with SqlServer in my project and I have a need to use native SqlServer syntax for a particular query. For this, I have been using the NativeQuery annotation with great results.
However, the issue comes when I need to run a unit test with Derby as my database. As it turns out, Derby does not support the exact syntax of my NativeQuery. My thought is to swap out the NativeQuery with a "Derbified" version to run the test. However, I have not been able to find a way to do this.
Is there any way to override or redefine a NativeQuery for an entity at runtime?

I would use persistence.xml to define two peristence-unit elements (one for SqlServer and another for Derby) with dedicated orm.xml containing named-native-query.
persistence.xml
<persistence version="2.0"
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">
<persistence-unit name="sqlserver-pu">
<mapping-file>META-INF/orm-sqlserver.xml</mapping-file>
...
</persistence-unit>
<persistence-unit name="derby-pu">
<mapping-file>META-INF/orm-derby.xml</mapping-file>
...
</persistence-unit>
</persistence>
orm-sqlserver.xml
<entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
<named-native-query name="findFirst" result-class="com.tyler.example.order">
<query>SELECT TOP 1 * FROM Order</query>
</named-native-query>
</entity-mappings>
orm-derby.xml
<entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
<named-native-query name="findFirst" result-class="com.tyler.example.order">
<query>SELECT * FROM Order FETCH FIRST ROW ONLY</query>
</named-native-query>
</entity-mappings>
With such an approach you have improved interoperability of your code as entities (portable across databases) are decoupled from queries (vendor specific). All you need is to select a proper persistence unit at runtime and execute a given query (they must have the same name).
Another approach that comes into my mind is to define a named native query twice for each entity using #NamedNativeQuery annotation with different name and query attributes, but at runtime you would then probably need some "ifology" to determine a proper one.

Related

How to create tables from entity classes in JPA on server Startup?

I am supposed to check for existence of tables on server startup. If they did not exist I have to create them using entity classes. Is this even possible?
I am using Eclipse and my server is wildfly10. I am connecting to Oracle 11g xe but I don't think it is database that is causing issues
Anyway, this is what I have done so far
My persistence.xml:
<?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="Lab5">
<properties>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
</properties>
</persistence-unit>
</persistence>
Without above property my database query throws Exception. But what is really strange, is that with this property query returns empty list, but table does not exist in database.
Also, I noticed warning showing on server startup
HHH000431: Unable to determine H2 database version, certain features may not work
I tried various fixes for this warning found on stackoverflow and other sites but they did not help. I don't even know if this is related to my problem
JPA 2.1 made DDL generation a part of the specification. You can use the "javax.persistence.schema-generation.database.action" persistence property with a "create" value to have 2.1 providers create the database schema for you during deployment. JPA can be used to generate or even run custom scripts should you need to modify the table creation/tear down and population process.
The problem you are likely encountering is your persistence unit does not specify a datasource, leaving it up to the container to figure out which you want to connect to, and Wildfly must be defaulting to an H2 database. The tables will be setup in this database, but not the Oracle XE database you are expecting - which is why queries return no values and yet in your console, the tables don't even exist.
You need to setup a datasource to your database in the server, and then point your persistence unit to it using the <non-jta-data-source> or <jta-data-source>. Your persistence.xml is incomplete, so I would urge you to look at a demo persistence unit first.
This will likely depend on the implementation you're including with your project. Assuming you're using hibernate for persistence, you'll need to add the following:
...
<persistence-unit ...>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
...
<properties>
<property name = "hibernate.hbm2ddl.auto" value="create" />
...
</properties>
</persistence-unit>
Note that this will assume that your connection is formed with a user having the required privileges in the db for table creation. Take a look at the hibernate docs here.

How to create H2 table before adding records in JPA

My project involves JPA and following in my persistence.xml file
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="rest-jpa">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>java:/comp/env/jdbc/restDB</jta-data-source>
<class>org.wso2.as.ee.Student</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>
</persistence-unit>
</persistence>
My H2 database is a in memory database.
It gives an error of table not found when I try to find for a record before adding one. I think that the table is created only when I add a record to the database. How can I create the table before any record is added?
You can make use of following JPA properties:
Generates and executes DROP TABLE and CREATE TABLE scripts everytime you run the application:
javax.persistence.schema-generation.database.action=drop-and-create
Loads some predefined data after creating database:
javax.persistence.sql-load-script-source="META-INF/loadData.ddl"
Creating schema should not be delayed until an entity is used (at least in case of using JPA), so you can use JPA properties which guarantee this behaviour. If you still find this behaviour occuring, you should make a bug ticket at your JPA provider bugtracker.

In JBoss/WildFly should I enable JTA on data source to use with JPA?

In JBoss/WildFly, when configuring a data source, there is a JTA option, which is disabled by default:
<datasource jta="false" jndi-name="java:/wt/testds" pool-name="testds" enabled="true" use-ccm="false">
...
</datasource>
Now I want to associate this data source with JPA using JTA transaction type:
<?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="test" transaction-type="JTA">
<jta-data-source>java:/wt/testds</jta-data-source>
</persistence-unit>
</persistence>
Do I also need to enable JTA on the data source?
Yes, of course you need to enable JTA on a datasource if you want to have jta transactions!
Your XML/JBoss/Wildfly config file will look like this:
<datasource jta="true" ...
In our webapp persistence-unit, the datasource looks like this:
<jta-data-source>java:jboss/datasources/CoreDS</jta-data-source>
The transaction-type="JTA" isn't necessary, at least not in my setup (Wildfly 8.1).
In your Java code you can go like this to use transactions:
#TransactionManagement(TransactionManagementType.CONTAINER) // class level
public class ...
...
#PersistenceContext(unitName = "CoreJPA")
EntityManager em;
#Resource
private EJBContext ejbContext;
...
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) // method level
public void doSomething(...)
And if you need to rollback, you do this:
try {
...
} catch (Throwable t) {
log.error("Exception in create work order: " + t.getMessage());
ejbContext.setRollbackOnly();
throw t;
}
There are a lot of resources about this that can be found using Google.
I've just experienced a problem related to this issue.
I was running a container managed transaction involving approximately 20,000 inserts into a MySQL database.
The transaction failed randomly, sometimes after around 3,500 inserts, other times after around 6,000 inserts, etc.
After investigation I found that the JTA option on the WildFly datasource definition was set to false.
Changing this setting to true fixed the problem, so I would agree with #user3472929 that JTA should be set to true in the datasource definition unless you have some specific reason not to.
I think you should use jta. And if you set jta to false in the container configuration file, jta will be disabled for JPA, so the transaction-type for JPA will be "RESOURCE_LOCAL", which has some nasty side effect. By the way, jta is true in the container configuration file by default.

In java EE 6: When could I change properties on EntityManager?

I want to use proxy authentication to an Oracle Express database in a simple web app with java EE 6 on Glassfish. Eclipselink 2.3.0 doc here says:
If a JEE and JTA managed EntityManager is used, specifying a proxy user/password can be more difficult, as the EntityManager and JDBC connection is not under the applications control. The persistence unit properties can still be specified on the EntityManager. As long as this is done before the EntityManager has established a database connection, this will still work.
My code is as below, but I guess the database connection is already established when the #Postconstruct init() method is called, since the last property is not changed from default true to false.
Also, I get ORA-00942: table or view does not exist from the save() method. The user pool in the jdbc connection pool defined in glassfish-resources.xml has not acces to the tables, as has proxy user user_a.
#Stateless
public class Boundary {
#PersistenceContext EntityManager em;
#PostConstruct
private void init() {
em.setProperty("eclipselink.oracle.proxy-type", OracleConnection.PROXYTYPE_USER_NAME);
em.setProperty(OracleConnection.PROXY_USER_NAME, "pool[user_a]");
em.setProperty(OracleConnection.PROXY_USER_PASSWORD, "pool");
em.setProperty(OracleConnection.PROXY_ROLES, "pool_user");
em.setProperty("eclipselink.jdbc.exclusive-connection.mode", "Always");
em.setProperty("eclipselink.jdbc.exclusive-connection.is-lazy", "false");
}
public void save() {
em.merge(new AnEntity());
}
}
I have not much experience here, but it would be really useful if proxy authentication could be made that simple, so I spent some time trying.
In a JavaEE managed context, is it possible to set properties on an EntityManager before it gets connected?
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
<persistence-unit name="ProxyUserPU" transaction-type="JTA">
<jta-data-source>SecondTry</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="eclipselink.target-database" value="Oracle"/>
<property name="eclipselink.jdbc.exclusive-connection.is-lazy" value="true"/>
</properties>
</persistence-unit>
</persistence>
I don't think PostConstruct is the correct place for this.
A new EntityManager is bound to the persistence context on every transaction boundary, so you need to set these property at the start of every transactional SessionBean method. You could probably also use SessionEvents in EclipseLink to configure the properties.
If you properties are fixed, you could also put them in your persistence.xml.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Auditing#Use_Oracle_proxy_authentication_to_allow_a_shared_connection_pool_and_a_user_context

How to detach an entity (JPA 2.0/EclipseLink/JBoss)

I need to detach some entity objects from the database to make them unmanaged. I use EclipseLink persistence provider, which method EntityManager.detach() is exactly one I need. The problem is that JBoss throws at runtime following exception (when execution passes to detach()):
javax.ejb.EJBTransactionRolledbackException: Unexpected Error
java.lang.NoSuchMethodError: javax.persistence.EntityManager.detach(Ljava/lang/Object;)V
Other methods like persist, merge, find work fine. I tried Hibernate and know that its Session provides a special method evict(), which detaches entity, but EclipseLink has no such method.
Example of using detach():
#PersistenceContext(unitName="Course7-ejbPU")
protected EntityManager manager;
(...)
Query query;
List<Message> resultList;
query = manager.createNamedQuery("Message.getUserInputMessageList");
query.setParameter("login", login);
query.setMaxResults(5);
resultList = query.getResultList();
for (Message message : resultList)
if (message.getContent().length() > 50)
{
manager.detach(message);
message.setContent(message.getContent().substring(0, 50) + "...");
}
Persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
<persistence-unit name="Course7-ejbPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>java:/Course7ds</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.target-server" value="JBoss"/>
</properties>
</persistence-unit>
</persistence>
Library with provider data is included into ear archive.
EclipseLink version is 2.2.0 (tested with 2.3.2 - no difference), JBoss server version 5.1.0. Any suggestions will be appreciated.
This exception shows that you're not using JPA2, but JPA1. You should probably use a more recent version of JBoss, that ships with JPA2.
You compiled your code with JPA 2.0 classes, but you run it with JPA 1.0. This is why the JVM doesn't find the detach method.
In reaction to your comment: no, the detach method is not useless for JPA 1.0 user: it's just it has not been created yet. You can however erase all the L1 cache by calling clean() on the entitymanager, which will detach all your managed entities...
You can still be able to detach an entity by using persistence provider specific code.
It is not because the entity manager does not provide a function yet, that the jpa providers hasn't implemented it yet.
If you can couple a little bit your code to your jpa provider:
You can call the em.getDelegate() method that will return you an EclipseLink entity manager implementation (check in debug the returned value and cast it) which may perhaps give you the possibility to detach your entity.
The method may not be named detach() -> for Hibernate it's evict().