JPA MetaDataException for different datasources - java

I have the problem with JPA Criteria API while using in my project different datasource persistance.
There are two PU uses different datasources:
<persistence-unit name="analysis" transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<non-jta-data-source>AnalysisDS</non-jta-data-source>
<class>entity1</class>
<class>entity2</class>
<class>entity3</class>
and
<persistence-unit name="reaction" transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<non-jta-data-source>ReactionDS</non-jta-data-source>
<class>someEntity1</class>
<class>someEntity2</class>
<class>someEntity3</class>
Spring load it, in applicationContext
<bean id="defaultAnalysysDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
lazy-init="default">
<property name="jndiName" value="AnalysisDS"/>
<property name="lookupOnStartup" value="false"/>
<property name="cache" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="defaultReactionDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean"
lazy-init="default">
<property name="jndiName" value="ReactionDS"/>
<property name="lookupOnStartup" value="false"/>
<property name="cache" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
In my DAO I can work with this PU with EntityManager, for example for
ReactionDS I'Am using
#PersistenceContext(unitName = "reaction")
private EntityManager entityManager;
And all work done - simple query's and JPQL expressions.
But when I want to introduce to my DAO JPA Criteria API
Like this :
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
...
I have exception after getCriteriaBuilder() method works:
Caused by: <openjpa-2.4.0-r422266:1674604 fatal user error> org.apache.openjpa.util.MetaDataException: Errors encountered while resolving metadata. See nested exceptions for details.
at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:675)
at org.apache.openjpa.meta.MetaDataRepository.getMetaDataInternal(MetaDataRepository.java:418)
at org.apache.openjpa.meta.MetaDataRepository.getMetaData(MetaDataRepository.java:389)
at org.apache.openjpa.persistence.meta.MetamodelImpl.(MetamodelImpl.java:86)
at org.apache.openjpa.persistence.EntityManagerFactoryImpl.getMetamodel(EntityManagerFactoryImpl.java:348)
at org.apache.openjpa.persistence.EntityManagerFactoryImpl.getCriteriaBuilder(EntityManagerFactoryImpl.java:332)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
...
Caused by: <openjpa-2.4.0-r422266:1674604 fatal user error> org.apache.openjpa.util.MetaDataException: Table "ANALYSIS.ENTITY1" given for "entity1" does not exist.
at org.apache.openjpa.jdbc.meta.MappingInfo.createTable(MappingInfo.java:532)
at org.apache.openjpa.jdbc.meta.ClassMappingInfo.getTable(ClassMappingInfo.java:317)
at org.apache.openjpa.jdbc.meta.ClassMappingInfo.getTable(ClassMappingInfo.java:339)
at org.apache.openjpa.jdbc.meta.strats.FullClassStrategy.map(FullClassStrategy.java:73)
at org.apache.openjpa.jdbc.meta.ClassMapping.setStrategy(ClassMapping.java:392)
at org.apache.openjpa.jdbc.meta.RuntimeStrategyInstaller.installStrategy(RuntimeStrategyInstaller.java:55)
at org.apache.openjpa.jdbc.meta.MappingRepository.prepareMapping(MappingRepository.java:410)
at org.apache.openjpa.meta.MetaDataRepository.preMapping(MetaDataRepository.java:769)
at org.apache.openjpa.meta.MetaDataRepository.resolve(MetaDataRepository.java:658)
... 147 more
The problem root cause in JPA, because his trying to use a tables from Analys in Reaction PU and extracts all meta-classes for entities that are located in different datasources, but access to them is doing in one.
But when I granted select on Entity1 to ReactionDS - all works done. (because I can use Select * from Analysis.Entity1 from reaction)
The question - how to make the metamodel classes to choose working only within the specified DS in EntityManager (in current example - Reaction, not together with Analysis) ?
p.s Database is Oracle, using Weblogic 12.1.3 and OpenJpa2.4.
Metamodel is generated automatically with maven plugin on compile:
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<processors>
<processor>org.apache.openjpa.persistence.meta.AnnotationProcessor6</processor>
</processors>
<optionMap>
<openjpa.metamodel>true</openjpa.metamodel>
</optionMap>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>${openjpa.version}</version>
</dependency>
</dependencies>
</plugin>

I think you may be confused by your Spring Framework datasource declarations.
These beans do not define your datasource, they only provide a way for other Spring components to access the datasources that have been configured in your server. JPA does not use these at all.
Therefore, your problem lies in the datasources that you have defined in your WebLogic server. It looks like you have defined both datasources to reference the same database instance.

Related

Configure HSQLDB with maven

I'm developing a Spring application where everything is configured with maven (in pom.xml). My application uses a PostgreSQL database, but unit tests use an in-memory HSQLDB database.
I just run into an issue with TEXT columns because they are not supported natively by HSQLDB. In my entity class I have :
private #Column(columnDefinition = "text") String propertyName;
This works fine with Postgres, but HSQLDB is generating the following error : type not found or user lacks privilege: TEXT. The table is not created, and of course as a result most of my tests fail.
I found that I need to activate PostgreSQL compatibility in order for this to work by setting sql.syntax_pgs to true.
My question is : where do I put this setting ? I would like to put it in pom.xml because everything is configured there, but I don't know where.
For exemple I have :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.profiles.active=test</argLine>
</configuration>
</plugin>
Can I somehow add an <argLine> with this setting ?
When you add hsqldb dependency it uses default connection properties. You can override these properties in property file or through other configuration as per your requirement. You can set "sql.syntax_pgs=true" to HSQLDB connection url. For example in case of spring boot this will be like below.
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.datasource.url=jdbc:hsqldb:mem:PUBLIC;sql.syntax_pgs=true</argLine>
</configuration>
</plugin>
you can set it in the Datasource configuration as given here
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:PUBLIC;sql.syntax_pgs=true" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>

Getting a org.springframework.remoting.RemoteAccessException: Could not access remote service when making a few calls one after the other?

I am using weblogic work managers to make a call from my webservice to an EJB (within the same ear) that calls an external websservice.
If I make 1 call it always work perfectly so I know that the setup is correct to call the service...
But as soon as I add another call(s) to it it sometimes fail (from the EJB to the web service) with this exception on the EJB to external web service call (the more calls I add to the work managers the more it fails)
org.springframework.remoting.RemoteAccessException: Could not access remote service [SomeServicePort]; nested exception is java.net.MalformedURLException
at org.springframework.remoting.rmi.RmiClientInterceptorUtils.convertRmiAccessException(RmiClientInterceptorUtils.java:190)
at org.springframework.remoting.jaxrpc.JaxRpcPortClientInterceptor.handleRemoteException(JaxRpcPortClientInterceptor.java:737)
at org.springframework.remoting.jaxrpc.JaxRpcPortClientInterceptor.doInvoke(JaxRpcPortClientInterceptor.java:595)
at org.springframework.remoting.jaxrpc.JaxRpcPortClientInterceptor.invoke(JaxRpcPortClientInterceptor.java:562)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy112.obtainClinicalCode(Unknown Source)
...
Caused by: java.net.MalformedURLException
at java.net.URL.<init>(URL.java:601)
at java.net.URL.<init>(URL.java:464)
at java.net.URL.<init>(URL.java:413)
at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:133)
...
My service is configured in Spring (2.5) in my application context as follows
<bean id="MyService"
class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"
lazy-init="true" scope="prototype">
<property name="serviceFactoryClass">
<value>org.apache.axis.client.ServiceFactory</value>
</property>
<property name="wsdlDocumentUrl">
<value>myWSDLPath...</value>
</property>
<property name="namespaceUri">
<value>com.controller</value>
</property>
<property name="serviceName">
<value>Mine</value>
</property>
<property name="portName">
<value>MinePort</value>
</property>
<property name="portInterface">
<value>com.MineServiceRemote</value>
</property>
<property name="serviceInterface">
<value>com.MineService</value>
</property>
<property name="lookupServiceOnStartup" value="false" />
</bean>
I am using Java 5 and Weblogic 10. I added logging before and after the calls to the external webservice from the EJB: I can see that multiple calls via the workmanagers are made to the EJB. But inside the EJB it seems to throw the exception. Just to isolate if there was anything wrong with the workmanager setup but that seems to be fine.
The problem seems to be in the EJB making the call to the external web service.
Why would I get this error for concurrent calls but not 1 at a time?
The problem was with my bean definded in the spring config.
Instead of defining it like that I saved the WSDL to my project and use WSDL2Code defined in my POM to generate a stub for me
<plugin>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-wsdl2code-maven-plugin</artifactId>
<version>1.5.4</version>
<executions>
<execution>
<id>myWebService</id>
<goals>
<goal>wsdl2code</goal>
</goals>
<configuration>
<packageName>PackageWhereIWantTheCodeToBeGeneratedTo</packageName>
<wsdlFile>${project.basedir}/src/main/resources/pathToWhereMyWsdlIsSavedInMyProject</wsdlFile>
<databindingName>jaxbri</databindingName>
<syncMode>sync</syncMode>
<generateTestcase>false</generateTestcase>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-jaxbri</artifactId>
<version>1.5.4</version>
</dependency>
</dependencies>
</plugin>
Then in my code I create a new stub and invoke the methods that has been generated for me

Generate java classes with JAXB2 in another maven project

Can someone please help me with my problem?
I have two maven projects A and B.
I want project A to contain all my model classes and in project B i'm creating a contract-first web service with Spring WS. So in project B, I use maven-jaxb2-plugin to generate classes from my schema. It happens that the generate classes in my webservice project (project A) are identical to the ones in my model project (project A) (with no XML annotations).
Because i don't want to have duplicate classes in my web service project (project B), i decided to make this project depends on the model project (project A) and what i want next is, not anymore generate classes to the webservice project but to the model project (project A).
Do you think there isn't another way to do this ?
Can someone please help me doing this if it's possible?
Project A
package project.a;
public class Client {
//...
}
Project B
Class
package project.b;
public class Compte {
//This class manipulates a Client object
//...
}
JAXB2 Maven Plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<outputDirectory>src/main/java</outputDirectory>
<packageName>project.a</packageName>
<schemaDirectory>src/main/webapp/WEB-INF/schemas</schemaDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
<plugin>
Schema
<element name="client">
<complexType>
<sequence>
<!-- -->
</sequence>
</complexType>
</element>
Project B spring bean configuration
<bean class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller"/>
<property name="unmarshaller" ref="marshaller"/>
<property name="defaultUri" value="http://localhost:8080/project/" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="project.a"/>
</bean>
You don't have to recreate model classes again. Just place them in Project A and then import those beans into Project B as:
<import resource="classpath:projectABeanDefinitionFile.xml" />
Assuming you are implementing/going to #XmlRootElement on Compte class (else suggest to go through a tutorial like here),
then change project B file as:
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound" value="project.b.Compte"/>
</bean>

Do I need <class> elements in persistence.xml?

I have very simple persistance.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
<class>pl.michalmech.eventractor.domain.User</class>
<class>pl.michalmech.eventractor.domain.Address</class>
<class>pl.michalmech.eventractor.domain.City</class>
<class>pl.michalmech.eventractor.domain.Country</class>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
and it works.
But when I remove <class> elements application doesn't see entities (all classes are annotated with #Entity).
Is there any automatic mechanism to scan for #Entity classes?
The persistence.xml has a jar-file that you can use. From the Java EE 5 tutorial:
<persistence>
<persistence-unit name="OrderManagement">
<description>This unit manages orders and customers.
It does not rely on any vendor-specific features and can
therefore be deployed to any persistence provider.
</description>
<jta-data-source>jdbc/MyOrderDB</jta-data-source>
<jar-file>MyOrderApp.jar</jar-file>
<class>com.widgets.Order</class>
<class>com.widgets.Customer</class>
</persistence-unit>
</persistence>
This file defines a persistence unit
named OrderManagement, which uses a
JTA-aware data source jdbc/MyOrderDB. The jar-file and class elements specify managed persistence classes: entity classes, embeddable classes, and mapped superclasses. The jar-file element specifies JAR files that are visible to the packaged persistence unit that contain managed persistence classes, while the class element explicitly names managed persistence classes.
In the case of Hibernate, have a look at the Chapter2. Setup and configuration too for more details.
EDIT: Actually, If you don't mind not being spec compliant, Hibernate supports auto-detection even in Java SE. To do so, add the hibernate.archive.autodetection property:
<persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
<!-- This is required to be spec compliant, Hibernate however supports
auto-detection even in JSE.
<class>pl.michalmech.eventractor.domain.User</class>
<class>pl.michalmech.eventractor.domain.Address</class>
<class>pl.michalmech.eventractor.domain.City</class>
<class>pl.michalmech.eventractor.domain.Country</class>
-->
<properties>
<!-- Scan for annotated classes and Hibernate mapping XML files -->
<property name="hibernate.archive.autodetection" value="class, hbm"/>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
In Java SE environment, by specification you have to specify all classes as you have done:
A list of all named managed persistence classes must be specified in Java SE environments to insure portability
and
If it is not intended that the annotated persistence classes contained in the root of the persistence unit be included in the persistence unit, the exclude-unlisted-classes element should be used. The exclude-unlisted-classes element is not intended for use in Java SE environments.
(JSR-000220 6.2.1.6)
In Java EE environments, you do not have to do this as the provider scans for annotations for you.
Unofficially, you can try to set <exclude-unlisted-classes>false</exclude-unlisted-classes> in your persistence.xml. This parameter defaults to false in EE and truein SE. Both EclipseLink and Toplink supports this as far I can tell. But you should not rely on it working in SE, according to spec, as stated above.
You can TRY the following (may or may not work in SE-environments):
<persistence-unit name="eventractor" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
Do I need Class elements in persistence.xml?
No, you don't necessarily. Here is how you do it in Eclipse (Kepler tested):
Right click on the project, click Properties, select JPA, in the Persistence class management tick Discover annotated classes automatically.
For those running JPA in Spring, from version 3.1 onwards, you can set packagesToScan property under LocalContainerEntityManagerFactoryBean and get rid of persistence.xml altogether.
Here's the low-down
You can provide for jar-file element path to a folder with compiled classes. For example I added something like that when I prepared persistence.xml to some integration tests:
<jar-file>file:../target/classes</jar-file>
for JPA 2+ this does the trick
<jar-file></jar-file>
scan all jars in war for annotated #Entity classes
Hibernate doesn't support <exclude-unlisted-classes>false</exclude-unlisted-classes> under SE, (another poster mentioned this works with TopLink and EclipseLink).
There are tools that will auto-generate the list of classes to persistence.xml e.g. the Import Database Schema wizard in IntelliJ. Once you've got your project's initial classes in persistence.xml it should be simple to add/remove single classes by hand as your project progresses.
Not sure if you're doing something similar to what I am doing, but Im generating a load of source java from an XSD using JAXB in a seperate component using Maven. Lets say this artifact is called "base-model"
I wanted to import this artifact containing the java source and run hibernate over all classes in my "base-model" artifact jar and not specify each explicitly. Im adding "base-model" as a dependency for my hibernate component but the trouble is the tag in persistence.xml only allows you to specify absolute paths.
The way I got round it is to copy my "base-model" jar dependency explictly to my target dir and also strip the version of it. So whereas if I build my "base-model" artifact it generate "base-model-1.0-SNAPSHOT.jar", the copy-resources step copies it as "base-model.jar".
So in your pom for the hibernate component:
<!-- We want to copy across all our artifacts containing java code
generated from our scheams. We copy them across and strip the version
so that our persistence.xml can reference them directly in the tag
<jar-file>target/dependency/${artifactId}.jar</jar-file> -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
<configuration>
<includeArtifactIds>base-model</includeArtifactIds>
<stripVersion>true</stripVersion>
</configuration>
</plugin>
Then I call the hibernate plugin in the next phase "process-classes":
<!-- Generate the schema DDL -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>hibernate3-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>generate-ddl</id>
<phase>process-classes</phase>
<goals>
<goal>hbm2ddl</goal>
</goals>
</execution>
</executions>
<configuration>
<components>
<component>
<name>hbm2java</name>
<implementation>annotationconfiguration</implementation>
<outputDirectory>/src/main/java</outputDirectory>
</component>
</components>
<componentProperties>
<persistenceunit>mysql</persistenceunit>
<implementation>jpaconfiguration</implementation>
<create>true</create>
<export>false</export>
<drop>true</drop>
<outputfilename>mysql-schema.sql</outputfilename>
</componentProperties>
</configuration>
</plugin>
and finally in my persistence.xml I can explicitly set the location of the jar thus:
<jar-file>target/dependency/base-model.jar</jar-file>
and add the property:
<property name="hibernate.archive.autodetection" value="class, hbm"/>
It's not a solution but a hint for those using Spring:
I tried to use org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean with setting persistenceXmlLocation but with this I had to provide the <class> elements (even if the persistenceXmlLocation just pointed to META-INF/persistence.xml).
When not using persistenceXmlLocation I could omit these <class> elements.
I'm not sure this solution is under the spec but I think I can share for others.
dependency tree
my-entities.jar
Contains entity classes only. No META-INF/persistence.xml.
my-services.jar
Depends on my-entities. Contains EJBs only.
my-resources.jar
Depends on my-services. Contains resource classes and META-INF/persistence.xml.
problems
How can we specify <jar-file/> element in my-resources as the version-postfixed artifact name of a transient dependency?
How can we sync the <jar-file/> element's value and the actual transient dependency's one?
solution
direct (redundant?) dependency and resource filtering
I put a property and a dependency in my-resources/pom.xml.
<properties>
<my-entities.version>x.y.z-SNAPSHOT</my-entities.version>
</properties>
<dependencies>
<dependency>
<!-- this is actually a transitive dependency -->
<groupId>...</groupId>
<artifactId>my-entities</artifactId>
<version>${my-entities.version}</version>
<scope>compile</scope> <!-- other values won't work -->
</dependency>
<dependency>
<groupId>...</groupId>
<artifactId>my-services</artifactId>
<version>some.very.sepecific</version>
<scope>compile</scope>
</dependency>
<dependencies>
Now get the persistence.xml ready for being filtered
<?xml version="1.0" encoding="UTF-8"?>
<persistence ...>
<persistence-unit name="myPU" transaction-type="JTA">
...
<jar-file>lib/my-entities-${my-entities.version}.jar</jar-file>
...
</persistence-unit>
</persistence>
Maven Enforcer Plugin
With the dependencyConvergence rule, we can assure that the my-entities' version is same in both direct and transitive.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
Not necessarily in all cases.
I m using Jboss 7.0.8 and Eclipselink 2.7.0. In my case to load entities without adding the same in persistence.xml, I added the following system property in Jboss Standalone XML:
<property name="eclipselink.archive.factory" value="org.jipijapa.eclipselink.JBossArchiveFactoryImpl"/>

Auto generate data schema from JPA annotated entity classes

I'm using JPA (Hibernate's implementation) to annotate entity classes to persist to a relational database (MySQL or SQL Server). Is there an easy way to auto generate the database schema (table creation scripts) from the annotated classes?
I'm still in the prototyping phase and anticipate frequent schema changes. I would like to be able to specify and change the data model from the annotated code. Grails is similar in that it generates the database from the domain classes.
You can use hbm2ddl from Hibernate. The docs are here.
Generate create and drop script for given JPA entities
We use this code to generate the drop and create statements:
Just construct this class with all entity classes and call create/dropTableScript.
If needed you can use a persitence.xml and persitance unit name instead. Just say something
and I post the code too.
import java.util.Collection;
import java.util.Properties;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.dialect.Dialect;
import org.hibernate.ejb.Ejb3Configuration;
/**
* SQL Creator for Tables according to JPA/Hibernate annotations.
*
* Use:
*
* {#link #createTablesScript()} To create the table creationg script
*
* {#link #dropTablesScript()} to create the table destruction script
*
*/
public class SqlTableCreator {
private final AnnotationConfiguration hibernateConfiguration;
private final Properties dialectProps;
public SqlTableCreator(final Collection<Class<?>> entities) {
final Ejb3Configuration ejb3Configuration = new Ejb3Configuration();
for (final Class<?> entity : entities) {
ejb3Configuration.addAnnotatedClass(entity);
}
dialectProps = new Properties();
dialectProps.put("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect");
hibernateConfiguration = ejb3Configuration.getHibernateConfiguration();
}
/**
* Create the SQL script to create all tables.
*
* #return A {#link String} representing the SQL script.
*/
public String createTablesScript() {
final StringBuilder script = new StringBuilder();
final String[] creationScript = hibernateConfiguration.generateSchemaCreationScript(Dialect
.getDialect(dialectProps));
for (final String string : creationScript) {
script.append(string).append(";\n");
}
script.append("\ngo\n\n");
return script.toString();
}
/**
* Create the SQL script to drop all tables.
*
* #return A {#link String} representing the SQL script.
*/
public String dropTablesScript() {
final StringBuilder script = new StringBuilder();
final String[] creationScript = hibernateConfiguration.generateDropSchemaScript(Dialect
.getDialect(dialectProps));
for (final String string : creationScript) {
script.append(string).append(";\n");
}
script.append("\ngo\n\n");
return script.toString();
}
}
As Hibernate 4.3+ now implements JPA 2.1 the appropriate way to generate DDL scripts is to use following set of JPA 2.1 properties :
<property name="javax.persistence.schema-generation.scripts.action" value="create"/>
<property name="javax.persistence.schema-generation.create-source" value="metadata"/>
<property name="javax.persistence.schema-generation.scripts.create-target" value="target/jpa/sql/create-schema.sql"/>
As it will be run at runtime, you may want to execute this DDL generation at build.
There is no supported official maven plugin anymore for Hibernate4 probably because Hibernate team is moving to Gradle.
Anyway, this is the JPA 2.1 approach to generate this script programmatically :
import java.io.IOException;
import java.util.Properties;
import javax.persistence.Persistence;
import org.hibernate.jpa.AvailableSettings;
public class JpaSchemaExport {
public static void main(String[] args) throws IOException {
execute(args[0], args[1]);
System.exit(0);
}
public static void execute(String persistenceUnitName, String destination) {
System.out.println("Generating DDL create script to : " + destination);
final Properties persistenceProperties = new Properties();
// XXX force persistence properties : remove database target
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
// XXX force persistence properties : define create script target from metadata to destination
// persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, "true");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION, "create");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, destination);
Persistence.generateSchema(persistenceUnitName, persistenceProperties);
}
}
As you can see it's very simple !
You can now use this in an AntTask, or MAVEN build like this (for MAVEN) :
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>generate-ddl-create</id>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<!-- ANT Task definition -->
<java classname="com.orange.tools.jpa.JpaSchemaExport"
fork="true" failonerror="true">
<arg value="${persistenceUnitName}" />
<arg value="target/jpa/sql/schema-create.sql" />
<!-- reference to the passed-in classpath reference -->
<classpath refid="maven.compile.classpath" />
</java>
</target>
</configuration>
</execution>
</executions>
</plugin>
As a related note: Documentation for generating database schemas using EclipseLink JPA can be found here.
Here's an explaination of how to use the hibernate SchemaExport class to do exactly what you want.
http://jandrewthompson.blogspot.com/2009/10/how-to-generate-ddl-scripts-from.html
If you prefer configuring in Spring then this should be helpful:
<!-- CONTAINER-MANAGED JPA Entity manager factory (No need for persistence.xml)-->
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<!-- Fine Grained JPA properties Create-Drop Records -->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- The JPA vendor -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- <property name="database" value="MySQL"/> -->
<property name="showSql" value="true"/>
<!-- <property name="generateDdl" value="true"/> -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
You can use maven plugin to achieve this.
<plugin>
<!-- run command "mvn hibernate3:hbm2ddl" to generate DLL -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>hibernate3-maven-plugin</artifactId>
<version>3.0</version>
<configuration>
<hibernatetool>
<classpath>
<path location="${project.build.directory}/classes" />
<path location="${project.basedir}/src/main/resources/META-INF/" />
</classpath>
<jpaconfiguration persistenceunit="galleryPersistenceUnit" />
<hbm2ddl create="true" export="false" destdir="${project.basedir}/target" drop="true" outputfilename="mysql.sql" format="true" console="true"/>
</hibernatetool>
</configuration>
</plugin>
<property name="hibernate.hbm2ddl.auto" value="update"/>
Add the above code in the persistence.xml under properties tag.
"update" will create the table when first time you run your code, after that, only update the table structures if any changes in domain object.
With EclipseLink, you should add property:
<property name="eclipselink.ddl-generation" value="create-tables"/>
As it is said here:
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/p_ddl_generation.htm
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="appDB" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>LocalMySQL</jta-data-source>
<class>entity.Us</class>
<class>entity.Btl</class>
<class>entity.Co</class>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>

Categories