To get more familiar with the new Java EE 6 System I have created a little demo application that should be able to store some user input in a postgres database.
I am using:
- glassfish 3.1
- Postgres 9.1
- OpenJPA 2.1.1
For this purpose I wrote following entity:
#Entity
public class User implements Serializable
{
#Id
#GeneratedValue(generator="user_seq",strategy=GenerationType.SEQUENCE)
#SequenceGenerator(name="user_seq", sequenceName="user_seq",allocationSize=1)
public Long id;
The database is completely empty and the connection pool is configured in glassfish. My persistence.xml looks like this:
<persistence-unit name="myDatabase" transaction-type="JTA" >
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>jdbc/myDatabase</jta-data-source>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" />
<property name="openjpa.jdbc.DBDictionary" value="postgres" />
</properties>
</persistence-unit>
Due to the fact, that I dont want to insert all db-objects myself, I activated the automatic schema creation in my jpa properties. With the first start openjpa (or maybe postgre) creates a new sequence named 'user_seq' as mentioned in the annotations of the entity.
This works fine. But then openjpa wants to create the table 'user' and throws following exception:
Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException:
FEHLER: Syntaxfehler bei »User«
Position: 14 {stmnt 31631786 CREATE TABLE User
(id BIGINT NOT NULL, userlastlogin VARCHAR(255), username VARCHAR(255),
userpassword VARCHAR(255), PRIMARY KEY (id))} [code=0, state=42601]
The SQL-Statement seems to be fine. If I create the table 'user' myself, I get the ReportingSQLException that openjpa is not able to insert the entity in the existing table.
First I thought that openjpa and postgre both wants to create the sequence with activated schema-creation. So changed the property to:
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(Sequences=false)" />
Unfortunately this did not work either.
What else can I say.. Hm.. My DAO looks like this:
#Stateless
public class DAOService
{
#PersistenceContext
protected EntityManager em;
public <T> T create(T t)
{
this.em.persist(t);
this.em.flush();
this.em.refresh(t);
return t;
}
Thanks for your help.
Perhaps 'User' is a reserved keyword on postgres? Can you try specifying the name of your Entity to be User0 or something?
Related
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>
I have a vanilla maven WAR project, using the Java EE web profile, that executes its unit/integration tests using OpenEJB. During the OpenEJB start-up, instead of using the data source defined in jndi.properties, OpenEJB creates its own:
INFO - Auto-creating a Resource with id 'Default JDBC Database' of type 'DataSource for 'scmaccess-unit'.
INFO - Creating Resource(id=Default JDBC Database)
INFO - Configuring Service(id=Default Unmanaged JDBC Database, type=Resource, provider-id=Default Unmanaged JDBC Database)
INFO - Auto-creating a Resource with id 'Default Unmanaged JDBC Database' of type 'DataSource for 'scmaccess-unit'.
INFO - Creating Resource(id=Default Unmanaged JDBC Database)
INFO - Adjusting PersistenceUnit scmaccess-unit <jta-data-source> to Resource ID 'Default JDBC Database' from 'jdbc/scmaccess'
INFO - Adjusting PersistenceUnit scmaccess-unit <non-jta-data-source> to Resource ID 'Default Unmanaged JDBC Database' from 'null'
And then, further below, when it's time to create the table - as per the create-drop strategy defined on the app's persistence.xml file - I see several errors like this:
(...) Internal Exception: java.sql.SQLSyntaxErrorException: type not found or user lacks privilege: NUMBER
Error Code: -5509
The jndi.properties file:
##
# Context factory to use during tests
##
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory
##
# The DataSource to use for testing
##
scmDatabase=new://Resource?type=DataSource
scmDatabase.JdbcDriver=org.hsqldb.jdbcDriver
scmDatabase.JdbcUrl=jdbc:hsqldb:mem:scmaccess
##
# Override persistence unit properties
##
scmaccess-unit.eclipselink.jdbc.batch-writing=JDBC
scmaccess-unit.eclipselink.target-database=Auto
scmaccess-unit.eclipselink.ddl-generation=drop-and-create-tables
scmaccess-unit.eclipselink.ddl-generation.output-mode=database
And, the test case:
public class PersistenceTest extends TestCase {
#EJB
private GroupManager ejb;
#Resource
private UserTransaction transaction;
#PersistenceContext
private EntityManager emanager;
public void setUp() throws Exception {
EJBContainer.createEJBContainer().getContext().bind("inject", this);
}
public void test() throws Exception {
transaction.begin();
try {
Group g = new Group("Saas Automation");
emanager.persist(g);
} finally {
transaction.commit();
}
}
}
Looks like eclipselink is trying to create a column with the type NUMBER and that type does not exist in HSQL. Did you specify that type in your mappings? If yes then fix that.
Otherwise it might help to add
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.create-ddl-jdbc-file-name" value="createDDL_ddlGeneration.jdbc"/>
<property name="eclipselink.drop-ddl-jdbc-file-name" value="dropDDL_ddlGeneration.jdbc"/>
<property name="eclipselink.ddl-generation.output-mode" value="both"/>
to your persistence.xml so you can see what create table statements are exactly generated. If eclipselink is using NUMBER on it's own for certain columns you can tell it to use something else by using the following annotations on the corresponding fields.
#Column(columnDefinition="NUMERIC")
I am just getting started with EJB development, so I created a test system that consists of the following three projects:
Actually, the HelloWorldBeanRemote interface does also exist in the EJBTest project. I haven't found a way to import it from the other project without runtime exceptions.
The EJBTestInterfaces is a plain java project that contains only the remote interface. EJBTest contains the program logic. HelloWorldBean is a session bean. Its constructor sets the created field to the current time. In the sayHello() method it uses an injected PersistenceManager to retrieve the TestEntity with id 0 (or creates it if it does not exist), increments the ´hit` variable and returns it:
#PersistenceContext(name="manager1")
private EntityManager em;
#Override
public String sayHello() {
String info;
if (em == null)
info = "Entity Manager is null";
else {
TestEntity entity;
try {
entity = em.find(TestEntity.class, 0);
entity.setHits(entity.getHits() + 1);
em.merge(entity);
info = "Hit entity " + entity.getHits() + " times.";
} catch(Exception x) {
entity = new TestEntity();
em.persist(entity);
info = "Never used entity bean before.";
}
}
return "Hello! I was created at " + created.toString() + "<br>" + info;
}
The persistence unit is defined in the persistence.xml as follows:
<persistence>
<persistence-unit name="manager1">
<jta-data-source>java:jboss/datasources/AppointmentDS</jta-data-source>
<jar-file>../EJBTest.jar</jar-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<!-- also tried value="validate" -->
</properties>
</persistence-unit>
</persistence>
It uses an embedded database defined in the standalone.xml:
<datasource jndi-name="java:jboss/datasources/AppointmentDS" pool-name="AppointmentDS" enabled="true" use-java-context="true">
<connection-url>jdbc:h2:file:[path to file]</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
The servlet basically outputs the return value of sayHello():
doGet(...) {
//get initial context ...
bean = (HelloWorldBeanRemote)initialContext.lookup(name);
output.write(bean.sayHello());
}
If I now call the servlet via a web browser, I get the expected output: The creation date and "Never used entity bean before." If I refresh the page, the creation date does not change, but the hit count increments. I can restart the servlet project without changing this behaviour. The hit count increments steadily.
However, if I restart the EJB project, everything is reset to zero. It is the expected behaviour for the creation date, but the hit count should be read from the database. But it is not.
I can see the created database files in the specified directory and they seem to contain data (I just opened the file in a text editor).
Am I supposed to use the session bean the way I did? I am not sure if I have to close the bean after the request (so the transaction can commit).
How can I make the EJB project read persisted data from the database file?
You need to change hibernate.hbm2ddl.auto value from create-drop to validate or some other value. create-drop will delete whole schema when the SessionFactory is closed and recreate it again on open.
It might be that H2 resets the DB once you restart your EJB project, that is, when the last connection is gone.
Stop your EJB project and connect to the DB to see if the expected data is still there.
I have a code that works perfectly on WAS 7 but fail when i run it in WAS 8.0.0.5. I am using JPA 2.0 with openJPA as my provider. Calling persist on my em throws a nested exception. Has anyone ever managed to write a JPA program in WAS 8.0.0.5
here is the Exception
WTRN0074E: Exception caught from before_completion synchronization operation: org.apache.openjpa.persistence.PersistenceException: DB2 SQL Error: SQLCODE=-204, SQLSTATE=42704, SQLERRMC=.OPENJPA_SEQUENCE_TABLE, DRIVER=3.58.81 {prepstmnt -1559269434 SELECT SEQUENCE_VALUE FROM .OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS [params=?]}
The SQLCODE=-204 points that something is missing. The log keeps printing THAKHANI.OPENJPA_SEQUENCE_TABLE which makes think that maybe the table is missing. You could also check to make sure the DB2 user that JPA is using has permissions to create tables and run SELECT statements on them.
I manage to resolve the problem by selecting Identity as my primary key generation mechanism when generating entities from tables. I also add the following in my persistence.xml.
<properties>
<!-- OpenJPA specific properties -->
<property name="openjpa.TransactionMode" value="managed"/>
<property name="openjpa.ConnectionFactoryMode" value="managed"/>
<property name="openjpa.jdbc.DBDictionary" value="db2"/>
<property name="openjpa.jdbc.Schema" value=<SchemaName>/>
</properties>
I have a perfectly working application client deployed to a GlassFish v2 server inside an ear with some EJBs, Entities, etc. I'm using eclipselink.
Currently I have in my persistence.xml:
<persistence-unit name="mysource">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/mysource</jta-data-source>
<class>entities.one</class>
<class>entities.two</class>
...
<properties>
<property name="eclipselink.target-server" value="SunAS9"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
And this works fine when I inject the EntityManager into the EJB:
#PersistenceContext(unitName="mysource")
private EntityManager em;
Now I have a requirement to dynamically switch persistence units/databases.
I figure I can get an EntityManager programatically:
em = Persistence.createEntityManagerFactory("mysource").createEntityManager();
but I get the following error:
Unable to acquire a connection from driver [null], user [null] and URL [null]
Even "overriding" javax.persistence.jtaDataSource" to "jdbc/mysource" in a Map and calling createEntityManagerFactory("mysource", map) doesn't make a difference.
What am I missing?
You are trying to circumvent the container with creating an entity manager programmatically and this means you'll most probably create a non-JTA data source (as it's outside the container, the transaction type should be RESOURCE_LOCAL), thus your original config is useless.
Try injecting an entity manager with a different unitName property or create a RESOURCE_LOCAL transaction type persistence unit.