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")
Related
We have a working Spring Boot (2.1.3) application. For local development we use
jpa:
hibernate:
ddl-auto: create-drop
Now we need to generate a ddl file for our db-guys (preferable during build). I'll tried by setting these additional properties:
javax:
persistence:
schema-generation:
create-source: metadata
action: create
create-target: create.sql
With these settings in place (ddl-auto changed to none) i started up my application. While it started fine there is not "create.ddl" to found.
Because i want the ddl file to be generated during build i added a test:
#RunWith(SpringRunner.class)
#DataJpaTest
#TestPropertySource(locations = "classpath:/testproperties/ddlgenerate.yml")
#AutoConfigureTestDatabase(replace = Replace.NONE)
public class GenerateDDL {
#Autowired
private EntityManager em;
#Test
public void generateDDL(){
em.close();
em.getEntityManagerFactory().close();
}
}
I read somewhere that the ddl should be generated during instantiation of EntityManager?!
The referenced classpath:/testproperties/ddlgenerate.yml only contains
spring:
jpa:
properties:
javax:
persistence:
schema-generation:
create-source: metadata
action: create
create-target: create.sql
Log indicates that the properties are loaded. The test is green but still no ddl file.
So how to get a ddl file generated (preferable during build)?
The problem is that #TestPropertySource does not support yaml files as source. All is working as soon as yaml is converted to properties file.
I am using Weblogic 12c and I have configured my datasources like this:
Notice that they are all deployed on target "AdminServer". Also, the "Name" field and "JNDI Name" field are identical. For example, suppose one datasource's name is "MYDS".
Now, when I try to acquire that datasource through Spring Data JPA like this:
#Bean
#Primary
public DataSource businessDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
dataSource.setResourceRef(true);
dataSource.setJndiName("MYDS");
dataSource.afterPropertiesSet();
return (DataSource) dataSource.getObject();
}
I get the following error when trying to deploy the war file to weblogic:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [javax.sql.DataSource]: Factory method
'businessDataSource' threw exception; nested exception is
javax.naming.NameNotFoundException: Unable to resolve 'MYDS'. Resolved
''; remaining name 'MYDS'
Also I see this error:
DEBUG o.s.jndi.JndiObjectFactoryBean - Converted JNDI name
[java:comp/env/MYDS] not found - trying original name [MYDS].
javax.naming.NameNotFoundException: While trying to look up
comp/env/MYDS in /app/myapp/webclient/404708050.; remaining name
'comp/env/MYDS'
Just looking at the source for setResourceRef and it states :
public void setResourceRef(boolean resourceRef)
Set whether the lookup occurs in a Java EE container,
i.e. if the prefix "java:comp/env/" needs to be added if the JNDI name doesn't already contain it.
Default is "false".
Note: Will only get applied if no other scheme (e.g. "java:") is given.
If you are setting it to true have you tried adding java:comp/env/ to the JNDI name ?
I am trying to migrate an application from EJB3 + JTA + JPA (EclipseLink). Currently, this application makes use of application managed persistent context due to an unknown number of databases on design time.
The application managed persistent context allows us to control how to create EntityManager (e.g. supply different datasources JNDI to create proper EntityManager for specific DB on runtime).
E.g.
Map properties = new HashMap();
properties.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");
//the datasource JNDI is by configuration and without prior knowledge about the number of databases
//currently, DB JNDI are stored in a externalized file
//the datasource is setup by operation team
properties.put(PersistenceUnitProperties.JTA_DATASOURCE, "datasource-jndi");
properties.put(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false");
properties.put(PersistenceUnitProperties.SESSION_NAME, "xxx");
//create the proper EntityManager for connect to database decided on runtime
EntityManager em = Persistence.createEntityManagerFactory("PU1", properties).createEntityManager();
//query or update DB
em.persist(entity);
em.createQuery(...).executeUpdate();
When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback.
Now, I am trying to migrate this application to Spring Boot.
The problem I encounter is that there is no transaction started even after I annotate the method with #Transactional(propagation = Propagation.REQUIRED).
The Spring application is packed as an executable JAR file and run with embadded Tomcat.
When I try to execute those update APIs, e.g. EntityManager.persist(..), EclipseLink always complains about:
javax.persistence.TransactionRequiredException: 'No transaction is currently active'
Sample code below:
//for data persistence
#Service
class DynamicServiceImpl implements DynamicService {
//attempt to start a transaction
#Transactional(propagation = Propagation.REQUIRED)
public void saveData(DbJndi, EntityA){
//this return false that no transaction started
TransactionSynchronizationManager.isActualTransactionActive();
//create an EntityManager based on the input DbJndi to dynamically
//determine which DB to save the data
EntityManager em = createEm(DbJndi);
//save the data
em.persist(EntityA);
}
}
//restful service
#RestController
class RestController{
#Autowired
DynamicService service;
#RequestMapping( value = "/saveRecord", method = RequestMethod.POST)
public #ResponseBody String saveRecord(){
//save data
service.saveData(...)
}
}
//startup application
#SpringBootApplication
class TestApp {
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
}
persistence.xml
-------------------------------------------
<persistence-unit name="PU1" transaction-type="JTA">
<properties>
<!-- comment for spring to handle transaction??? -->
<!--property name="eclipselink.target-server" value="WebLogic_10"/ -->
</properties>
</persistence-unit>
-------------------------------------------
application.properties (just 3 lines of config)
-------------------------------------------
spring.jta.enabled=true
spring.jta.log-dir=spring-test # Transaction logs directory.
spring.jta.transaction-manager-id=spring-test
-------------------------------------------
My usage pattern does not follow most typical use cases (e.g. with known number of DBs - Spring + JPA + multiple persistence units: Injecting EntityManager).
Can anybody give me advice on how to solve this issue?
Is there anybody who has ever hit this situation that the DBs are not known in design time?
Thank you.
I finally got it work with:
Enable tomcat JNDI and create the datasource JNDI to each DS programmatically
Add transaction stuff
com.atomikos:transactions-eclipselink:3.9.3 (my project uses eclipselink instead of hibernate)
org.springframework.boot:spring-boot-starter-jta-atomikos
org.springframework:spring-tx
You have pretty much answered the question yourself: "When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback".
WebLogic is compliant with the Java Enterprise Edition specification which is probably why it worked before, but now you are using Tomcat (in embedded mode) which are NOT.
So you simply cannot do what you are trying to do.
This statement in your persistence.xml file:
<persistence-unit name="PU1" transaction-type="JTA">
requires an Enterprise Server (WebLogic, Glassfish, JBoss etc.)
With Tomcat you can only do this:
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
And you need to handle transactions by your self:
myEntityManager.getTransaction.begin();
... //Do your transaction stuff
myEntityManager.getTransaction().commit();
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.
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?