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

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.

Related

Table not found with H2

My Java EE application cannot find tables. I am using WildFly (as the application server) and H2 (as the DB, in the embedded mode).
The error is:
org.h2.jdbc.JdbcSQLException: Table "MY_TABLE" not found
Look at my table creation:
create table "MY_TABLE" (
-- ...
);
See how my entity is defined:
#Entity
#Table(name = "MY_TABLE")
public class MyTable {
// ...
}
This is how I call JPA (this causes the exception):
#PersistenceContext
private EntityManager entityManager;
// ...
entityManager.find(MyTable.class, 1);
My persistence.xml is:
<persistence-unit name="myapp" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/myappDS</jta-data-source>
</persistence-unit>
And the standalone.xml in my WildFly:
<datasource jndi-name="java:jboss/datasources/myappDS" pool-name="myappDS" enabled="true" use-java-context="true">
<connection-url>jdbc:h2:~/myapp;SCHEMA=PUBLIC</connection-url>
<driver>h2</driver>
</datasource>
<drivers>
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
</driver>
</drivers>
Everything looks good so why the exception? Do I need to flush something? Or set schema somewhere?
If I configure another datasource (having the same DB structure) in WildFly (for example Postgres), everything works fine. That would mean that the datasource configuration is the place causing the error.
(Yes, I am totally sure the DB is not empty and the connection URL is correct as I have tried it from an SQL client.)
Are you running the application as a different user than you are testing to connect with? In that case the the ~ in the connection path will resolve to different home folders, and thus different databases.
Otherwise I would suggest connecting with the Shell in the h2 jar file and run show tables to verify that the table exists and with the correct casing. Start the shell by running:
java -cp h2*.jar org.h2.tools.Shell
Where do you store your script for the table creation? Is it in classpath of the application?
In my test setup I let hibernate generate the tables on startup (and dropping it on shutdown) and using an import.sql script for the test-data generation, which is in the folder src/main/resources.
<persistence-unit name="myapp" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/myappDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>

Override NativeQuery at Runtime

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.

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.

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().

ejb3-using-2-persistence-units-within-a-transaction

I am having problems connecting to 2 persistence units from within the same transaction using following tech stack,
WLS 10.3.x, Eclipselink 2.1, Oracle 11g JDBC driver, Informix 10 JDBC driver
Using inputs from this SO post I made the oracle datasource XA compliant and the Informix ds "Emulate 2-phase commit" and things started to work. However, now I am getting a strange problem.
I am using standalone java client to invoke my ejb 3 SLSB which in turn invokes the JPA entities. The problem I am facing is it works the first time, the second time it does not throw any exception but does not update the data in either databases and the 3rd time it throws an exception stating "Transaction has already been committed" as if the application server JTA transaction manager is holding on to the original transaction context. Please note these 3 invocations are separate and sequential wherein every invocations completes with the client exiting the client process. The problem is very consistent and happens in the exact same sequence every time I restart the app server.
Appreciate any input!
<persistence-unit name="TopLinkDB" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/oracleDS</jta-data-source>
<class>com.home.domain.Property</class>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10" />
</properties>
</persistence-unit>
<persistence-unit name="TopLinkINFO" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/infoDS</jta-data-source>
<class>com.home.domain.GlobalNumber</class>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10" />
</properties>
</persistence-unit>