Hibernate JTA : Read DB connection parameters per environment - java

I am writing a javaEE application, using hibernate. The application will be running on multiple environments (dev, qa, prod etc.) & will have separate dbs's associated with each of them. I would like to set the hibernate properties like jdbc-url, username , password etc. separately for each of these environments.
My current persistence.xml looks like :
<persistence-unit name="PU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<validation-mode>CALLBACK</validation-mode>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
<property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/>
<property name="hibernate.connection.url" value="jdbc:oracle:thin:#host/schema"/>
<property name="hibernate.connection.username" value="abc"/>
<property name="hibernate.connection.password" value="***"/>
</properties>
</persistence-unit>
I am using the persistence unit as follows in my java code:
#PersistenceContext(unitName = "PU")
private EntityManager em;
Is there a way that I can inject the hibernate properties, which are stored in separate properties files, into EntityManager for different environments ?
Please note that I am using JTA and hence cannot use EntityManagerFactory. Also I am not & do not want to use spring.

It is really a BAAAAAAD idea to include environment-specific information in your application WAR/EAR bundle (either by including multiple configs, or by creating different bundles for different environment). Different database, for example, should be maintained in the container.
In your case, your persistence.xml should look like:
<persistence>
<persistence-unit name="PU">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/fooAppDs</jta-data-source>
<properties>
... ...
</properties>
</persistence-unit>
</persistence>
and of course you should have corresponding resource-ref for jdbc/fooAppDs.
By doing so, you can deploy your application in any environment. You just need to create correct datasource in your container and assign it to jdbc/fooAppDs.
Another approach I believe will work, though I will not recommend, is by creating hibernate.cfg.xml in classpath. You may want to have a local file system location and add that in classpath, instead of putting the file in your JAR/WAR/EAR.

Since you do not want to use external library such as Spring to bootstrap your persistence units, why not use your build system to do this. If you are using maven, you can use mix of maven filtering and profiles to filter based on properties file or if you are using any other build tool, you can add a task (or equivalent) to copy file contents from different files to actual file depending upon some external system/environmental variable.

We used maintain property files for each environment such as DEV,QA,PROD,UAT etc in different files and copy one of them during build.
Ant build
<property environment="env" />
<!-- ***** COMMAND LINE ARGUMENTS DEMOED HERE -->
<property name="build_type" value= "${env.build_type}"/>
<copy todir="deploy">
<fileset dir="src_dir"/>
<globmapper from=${env.build_type}".persistence.xml" to="persistence.xml"/>
</copy>
Run build like this
ant -Denv.build_type=PROD
This will copy PROD.persistence.xml to persistence.xml
ant -Denv.build_type=DEV
This will copy DEV.persistence.xml to persistence.xml

With Spring Profile you could initiate entity manager beans depending upon the active profile that will refer to the persistence.xml like dev-persistence.xml, test-persisitence.xml, prod-persistence.xml of your environment. And you can set Active profile using web.xml. Mostly the web.xml won't change that much so you can keep the web.xml in your repository with the spring profile active property set for that enviornment.

you can configure your spring-config.xml file as following
MYSQL
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/kaya_db" />
<property name="username" value="root" />
<property name="password" value="nxtlife" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="packagesToScan" value="com.nxtlife.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">
</bean>
2.Similar for other database like oracle,postgre with different name of datasource,sessionfactory and transactionmanager.
3. Finally you can get object of session factory using following sessionfactory name
#Modifier("sessionfactoryname")
#Autowired
private SessionFactory obj;
similar for different database

Related

Create database script mySQL

I have dynamic web java project where I am using spring-MVC/hibernate. In my xml config file I have a bean for setting up my sessionFactory (all dependencies - jdbc url etc. for my local database). Is there a way I can send this project + database to my friend and it will work ? I am wondering because in xml config file I have the exact jdbc url, hostname, port etc. which doesnt have to be the same on my friend's laptop but is there a way to create some script that will create database with the exactly same properties as my local database ?
EDIT - here is config file
<!-- Define Database DataSource / connection pool -->
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java_task?useSSL=false" />
<property name="user" value="me" />
<property name="password" value="me" />
<!-- these are connection pool properties for C3P0 -->
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="30000" />
</bean>
<!-- Define Hibernate session factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.javatask.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
[...] is there a way to create some script that will create database
with the exactly same properties as my local database?
In the hibernate-tools jar, Hibernate provides a SchemaExport tool that supports what you want to do.
You can write a little program yourself to use it, but I think most folks don't "roll their own." I think it's common to use a plugin for your build system.
Are you using Maven? You could use something like this: https://github.com/Devskiller/jpa2ddl
Is there a way I can send this project + database to my friend and it
will work ?
Yes, u can put your code in a public repository like Github, and ur friend can clone it from there
I am wondering because in xml config file I have the exact jdbc url,
hostname, port etc. which doesnt have to be the same on my friend's
laptop but is there a way to create some script that will create
database with the exactly same properties as my local database ?
We don't know ur code, but if u have provided absolute path in ur configuration then it will fail in ur friend's laptop. For a better answer, please provide ur configuration code
You can read about absolute path here
Update
I don't see any problem in your config code. it should work well in another system. About uploading the database, u can simply export it from ur database, and put the deattached file into the folder of ur current project. Once u push ur code to the repo the database would be there too. Then the other person, will get it again by the other codes. Again there is no problem.
Your firend must get sure that he has Mysql, and it is working on port 3306. But, there shouldn't be any problem. why do u think, it would fail?

How turn off eclipselink sql statement logging in persistence.xml?

I've configured EclipseLink as JPA provider for my Spring application. The only problem is, it spams the console with SQL statements.
This is my persistence.xml configuration:
<persistence 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"
version="2.0">
<persistence-unit name="jpa-persistence" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="eclipselink.ddl-generation" value="create-or-extend-tables"/>
<property name="eclipselink.logging.level" value="INFO"/>
<property name="logging.level" value="INFO"/>
</properties>
</persistence-unit>
</persistence>
I've found references to both eclipselink.logging.level and logging.level properties in internet, but none of them seems to work. I'm using slf4j, but I don't think it's relevant because I don't need to route SQL output through slf4j, I just want to get rid of it.
How should I configure my persistence.xml to stop SQL statement logging?
The version of EclipseLink I'm using:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.4</version>
</dependency>
This is how I instantiate JPA in my Spring application:
<!-- Define EclipseLink JPA Vendor Adapter -->
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="databasePlatform"
value="org.eclipse.persistence.platform.database.MySQLPlatform" />
<property name="generateDdl" value="true" />
<property name="showSql" value="false" />
</bean>
<!-- Entity Manager Factory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="jpa-persistence"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>
It is a EclipseLink specific behavior (reference).
EclipseLink states :
By default the log output goes to System.out or your console.
To configure the output to be logged to file the persistence unit
property eclipselink.logging.file can be set:
<property name="eclipselink.logging.file" value="output.log"/>
EclipseLink's logging service is plug-able and several different
logging integrations are supported including java.util.logging. To
enable java.util.logging the persistence unit property
eclipselink.logging.logger can be set:
To summarize, you have two solutions:
defining eclipselink.logging.file to avoid polluting the console.
This is quick hack but personally, I don't use this solution because it creates multiple log files and it makes harder readability of application logs.
defining eclipselink.logging.logger. It is the most flexible solution.
With EclipseLink 2.5+ , eclipselink.logging.loger can specify :
Custom logger
Fully qualified class name of a custom logger which implements
org.eclipse.persistence.logging.SessionLog
With this solution you could log through SLF4J and you could also customize the logs in a finer way.
Here is an example from GIT :
JavaLogger
Uses java.util.logging
java.util.logging, the API to avoid...
ServerLogger
Integrates with the application server's logging
DefaultLogger
(Default) Uses EclipseLink's native logger, DefaultSessionLog

property tags in persistence.xml when using JPA

I am new to JPA and use Hibernate as the JPA provider. I came to know that we need META-INF/persistence.xml configuration file.
I successfully created a simple Java program to persist data in DB using JPA.
All fine, doubts started when I looked into the persistence.xml file to understand it better.
Sample below:
<persistence-unit name="test-jpa" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
The following is the Java code for reading the configuration:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("test-jpa");
The following are the doubts:
How do we know that Hibernate is the JPA provider? Is it inferred by seeing the property tags in the file?
In config file, there are many <property> tags, are they pre-defined which can appear in the file (for a given JPA provider) or can we randomly add any property? who reads those <property> tags?
A JPA provider would provide documentation that would tell you all of that. Doesn't yours? I'd be surprised.
You should either have a <provider> element in the persistence-unit to define which provider to use, or it would use the default for the environment that you are running in (in JavaSE you would need to have 1 and only one JPA provider in the CLASSPATH, in JavaEE the server would have its own default).
They are provider-specific. Any properties that are prefixed javax.persistence would be JPA STANDARD. The first 4 of those posted have javax.persistence variants that you should have used instead.

Hibernate 4.3 loads all hbm files even if they're not declared in persistence unit

I am trying to configure a test for hibernate 4.3 with JPA and Spring 4 using the following config:
<bean id="em" class="LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceXmlLocation"
value="classpath:integrations/hibernate4/jpa/persistence.xml" />
</bean>
Now the persistence config looks as follows:
<persistence-unit name="test" >
<class>hibernate4.jpa.JpaEntity</class>
<class>hibernate4.jpa.JpaHiLoEntity</class>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
If I run the above with hibernate 4.2.4 everything runs fine, however with 4.3 the above setup loads ALL hbm.xml resource files, some of which refer to classes that are not present on the classpath, causing ClassNotFoundException.
Does anyone know what the reason for the changed behavior is and how to stop hibernate from scanning for hbm.xml files (packagesToScan property did not work either)?
I ran into this problem as well. The hibernate.archive.autodetection property controls what Hibernate will automatically scan for:
If the property is undefined, hbm files will be included, and annotated classes will be included unless <exclude-unlisted-classes> is set to true.
If the value of the property contains "hbm" then .hbm.xml files will be included. Otherwise they won't be.
If the value contains "class", then any annotated classes on the classpath will be included. Otherwise they won't be.
If the property is defined at all, regardless of its value, it affects the behavior of Hibernate. See the org.hibernate.jpa.boot.scan.internal.StandardScanOptions constructor.
For my purposes, I needed the persistence unit to include only the entities that were explicitly listed in <class> elements. To accomplish this, I defined the property with a value of "none":
<persistence-unit name="num2" transaction-type="RESOURCE_LOCAL">
...
<properties>
...
<property name="hibernate.archive.autodetection" value="none" />
</properties>
</persistence-unit>
Unfortunately, the only documentation I could find on this property was for the stable release, whatever that is. There was no mention of it in the docs for Hibernate 4.3, but that is the version I use and I can confirm the property works.
Also you can also configure the bean em without using the file persistence.xml. e.g.:
<bean id="em"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="ยทยทยท.model">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"
p:databasePlatform="org.hibernate.dialect.SQLServerDialect" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
The exclude-unlisted-classes doesn't work if your class is in the packages to scan, e.g. you want to have it registered, but doesn't want the hbm.xml for it.
I fixed that using hibernate property hibernate.archive.autodetection set to class, making Hibernate scan only classes and not mapping files.
<properties>
<property name="hibernate.archive.autodetection" value="class" />
</properties

Seam/Hibernate: liquibase before JPA startup

I have a Java EE web application (hibernate3, seam) that I'm using in Weblogic container.
I want to introduce Liquibase for schema migrations.
Currently we use
<property name="hibernate.hbm2ddl.auto" value="update"/>
which we want to drop because it can be dangerous.
I want the migration to automatically happen at deployments, so I'm using the servlet listener integration.
In web.xml, the first listener is:
<listener>
<listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
</listener>
Sadly, this listener comes into play after the Hibernate initialization and it throws missing table errors (because the schema is empty).
I'm google-ing like a boss for hours and I'm a bit confused now.
Thanks in advance
UPDATE
If I set <property name="hibernate.hbm2ddl.auto" value="none" />, liquibase finishes it's job successfully and the app starts up as expected. If I set validate, it seems like hibernate schema validation takes place before liquibase and it cries because of missing tables.
UPDATE
It seems like Seam initializes Hibernate, but Liquibase listener is listed before SeamListener, so I have no clue on how to enable schema validation and liquibase at the same time...
My understanding is that the LiquibaseServletListener requires the path to change log file which is passed using liquibase.changelog context param. So you already have a change log generated or am I missing something here ?
You can take a look at the liquibase hibernate integration library provided by Liquibase.
This library works with both the classic hibernate configuration (via .cfg and .xml files) as well as JPA configuration via persistence.xml.
AFAIK, generating the changelog and running the change log are two seperate process. Liquibase hibernate integration library helps in generating the change log from the diff of current state of entities in persistence unit and the current database state.
How to determine the order of listeners in web.xml
You should place:
<listener>
<listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
</listener>
before ORM or framework other related listeners.
I use Spring beans LiquiBase activation to reduce DB authentication data duplication by using already provided datasource bean:
<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource" />
<property name="changeLog" value="classpath:sql/master.sql" />
<property name="defaultSchema" value="PRODUCT" />
</bean>
To restrict order use depends-on attribute:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="liquibase">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="product.domain" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
</props>
</property>
</bean>

Categories