How to integrate LiquiBase + Spring + Hibernate without using hibernate.xml configuration file - spring

I already read a lot of posts regarding integration between LiquiBase, Spring and Hibernate, but none of them apply to my situation witch is:
I'm starting a new project that uses Spring and Hibernate, so I was looking into a way to manage the database changes during the project lifetime. First I started using hbm2ddl but then realized that people say that this isn't a very good idea in production environments, so I came to the conclusion that LiquiBase was the way to go (so I think).
The problem is that I'm not using a hibernate.xml config file (and all the examples I found using LiquiBase use a hibernate.xml), since I'm using java annotations in my POJO/DB classes and my hibernate configuration is made like this
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException
{
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
Properties jpaProterties = new Properties();
jpaProterties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
entityManagerFactoryBean.setJpaProperties(jpaProterties);
return entityManagerFactoryBean;
}
I also found posts from 2 years ago saying that this option would only be available in version 2.0 (the current one), and I was wondering if this is already implemented. If so, how do I use it in a ANT script?
I need to create the original database DDL and the following database change logs and import them into the production DB.
EDIT : I'm using:
Liquibase 2.0.5
Liquibase Hibernate 2.0.0
Hibernate 4.1.4
Spring 3.1.1
Spring Data JPA 1.1.1

Did you look at http://www.liquibase.org/documentation/spring.html?
It mentions dataSource and no hibernate.xml.
For initial generation you can use command line mode and generateChangeLog.
See http://www.liquibase.org/documentation/command_line.html.
Here is a minimal hibernate.xfg.xml you're going to need:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="entityManagerFactoryBean">
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3307/test</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
</session-factory>
</hibernate-configuration>

Related

Using Hazelcast for both Spring Session and 2 Level Cache (LC2) with Hibernate

So I want to use Hazelcast in my web application for both 2 level caching (hibernate layer) and spring session, the setup is very simple I want to be able to use NearCache configurations with a server running on the network.
I first ran into a compatibility problem recent version of Hazelcast 4.* is not yet supported in spring session, so I was happy to use the supported version: 3.12.6...
below is my hazelcast-client.xml and I have properly configured it to be used by spring.hazelcast.config=file://... when I start my application the Hazelcast instance is not created, so I decided well I should create the ClientConfig and HazelcastInstance beans myself using the code below:
#Bean()
ClientConfig clientConfig() throws IOException{
try(final InputStream stream = Files.newInputStream(Paths.get("proper-path/hazelcast-client.xml"))){
com.hazelcast.client.config.XmlClientConfigBuilder builder = new com.hazelcast.client.config.XmlClientConfigBuilder(stream);
return builder.build();
}
}
#Bean()
HazelcastInstance hazelcastInstance(final ClientConfig clientConfig){
return HazelcastClient.newHazelcastClient(clientConfig);
}
Now i have a problem, I can't add the code below so I can use it with sessions:
final MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
hazelcastInstance.getConfig().getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
hazelcast-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast-client xmlns="http://www.hazelcast.com/schema/client-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/client-config
http://www.hazelcast.com/schema/client-config/hazelcast-client-config-3.12.xsd">
<group>
<name>GroupName</name>
<password>pass</password>
</group>
<instance-name>${application.container.name}</instance-name>
<properties>
<property name="hazelcast.client.shuffle.member.list">true</property>
<property name="hazelcast.client.heartbeat.timeout">60000</property>
<property name="hazelcast.client.heartbeat.interval">5000</property>
<property name="hazelcast.client.event.thread.count">5</property>
<property name="hazelcast.client.event.queue.capacity">1000000</property>
<property name="hazelcast.client.invocation.timeout.seconds">120</property>
</properties>
<client-labels>
<label>web-app</label>
<label>${application.container.name}</label>
</client-labels>
<network>
<cluster-members>
<address>127.0.0.1</address>
</cluster-members>
<outbound-ports>
<ports>5801</ports>
</outbound-ports>
<smart-routing>true</smart-routing>
<redo-operation>true</redo-operation>
<connection-timeout>60000</connection-timeout>
<connection-attempt-period>3000</connection-attempt-period>
<connection-attempt-limit>2</connection-attempt-limit>
<socket-options>
<tcp-no-delay>false</tcp-no-delay>
<keep-alive>true</keep-alive>
<reuse-address>true</reuse-address>
<linger-seconds>3</linger-seconds>
<buffer-size>128</buffer-size>
</socket-options>
</network>
<executor-pool-size>40</executor-pool-size>
<native-memory enabled="false" allocator-type="POOLED">
<size unit="MEGABYTES" value="128"/>
<min-block-size>1</min-block-size>
<page-size>1</page-size>
<metadata-space-percentage>40.5</metadata-space-percentage>
</native-memory>
<load-balancer type="random"/>
<flake-id-generator name="default">
<prefetch-count>100</prefetch-count>
<prefetch-validity-millis>600000</prefetch-validity-millis>
</flake-id-generator>
<connection-strategy async-start="true" reconnect-mode="ASYNC">
<connection-retry enabled="true">
<initial-backoff-millis>2000</initial-backoff-millis>
<max-backoff-millis>60000</max-backoff-millis>
<multiplier>3</multiplier>
<fail-on-max-backoff>true</fail-on-max-backoff>
<jitter>0.5</jitter>
</connection-retry>
</connection-strategy>
</hazelcast-client>
You need to configure the map on the server side, which means you need to have some of the Spring classes on member's classpath. But keep in mind that you need this configuration only when HazelcastIndexedSessionRepository#findByIndexNameAndIndexValue is used.
Also in client mode, do not forget to deploy necessary classes and enable user code deployment for members. Otherwise session updates will fail:
// member
config.getUserCodeDeploymentConfig().setEnabled(true)
.setClassCacheMode(UserCodeDeploymentConfig.ClassCacheMode.ETERNAL);
// client
clientConfig.getUserCodeDeploymentConfig().setEnabled(true).addClass(Session.class)
.addClass(MapSession.class).addClass(SessionUpdateEntryProcessor.class);
But I recommend to include spring-session-hazelcast on member's cp rather than user code deployment. This will satisfy both of the needs above.
Finally, if hazelcast-client.xml exists in one of the known paths in your project (e.g. under resources/), the client will be created with this configuration. You do not need to create ClientConfig bean in that case.

Spring session with JDBC integration

I am new in Spring Session and want to use embedded database to store session information. I follow all the steps in
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/httpsession-jdbc.html using spring-session-jdbc of version 1.2.0.but using Spring-web of 3.2.4 only
The following error is shown repeatly:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "spring_session" does not exist 'Position: 13
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1927)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:561)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:419)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:365)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:824)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:818)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:589)
... 21 more'
This issue already haunted me for days. Please help.
Here is the xml configuration
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessi‌​onConfiguration"/>
<jdbc:embedded-database id="dataSource02" type="H2">
<jdbc:script location="classpath:org/springframework/session/jdbc/schema-h2.sql"/>
</jdbc:embedded-database>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource02"/>
</bean>
Your problem stems from the fact that you use multiple data sources, since the provided stacktrace includes PostgreSQL JDBC driver classes while you are configuring a secondary, embedded data source to storing sessions.
Spring Session configuration picks up your primary data source (PostgreSQL) and expects to find session tables there.
I'd suggest you to use your primary data source to store session data, but if you insist on having secondary/embedded data source for that purpose you need to override JdbcOperationsSessionRepository bean provided by JdbcHttpSessionConfiguration#sessionRepository with your own instance which is created using your secondary data source and its appropriate tra saction manager. Note that bean must be named sessionRepository in order to override one from JdbcHttpSessionConfiguration.

Transaction Management using Hibernate with weblogic

Our application is deployed on weblogic
we use Hibernate and spring
The session factory is created through a user defined class which creates SessionFactories by reading the hibernate.cfg.xml file
So these are the steps :
Our custom class is defined in spring application context and we inject the format of theCFG xml file in this class constructor :
<bean id="myCustomFactoryCreator"
class="com.xyz.CustomFactoryCreator"
singleton="true">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" >
<property name="staticField">
<value>com.xyz.MyConstants.HIBERNATE_CFG_XML</value>
</property>
</bean>
</constructor-arg>
</bean>
In MyConstants.java:
public static final String HIBERNATE_CFG_XML = ".hibernate.cfg.xml";
So this way in the constructor - the code will iterate over all such files ending with ".hibernate.cfg.xml"
I will have different files for different clients :
"Client1.hibernate.cfg.xml" and "Client2.hibernate.cfg.xml" and so on ......
The code will iterate over all such files and create SessionFactories and then embed them into Springs HibernateTemplate and then store them in a map against the client name as key value pair :
i.e. Client1 , HibernateTemplate1
Client2 , HibernateTemplate2 and so on
I hope you get the picture
This bean "myCustomFactoryCreator" is then injected into my Dao:
<bean id="myCustomDao"
class="com.xyz.MyCustomDao"
lazy-init="true">
<property name="criteriaConverter" ref="criteriaConverter" />
<property name="myFactory" ref="myCustomFactoryCreator" />
</bean>
An example of my Hibernate.cfg.xml is as : ( I have multiple such files where schema name changes for each client )
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">MYDS</property>
<property name="show_sql">true</property>
<property name="default_schema">CLIENT1SCHEMA</property>
</session-factory>
In my Dao - based on the client login - I get the appropriate HibernateTemplate and fire selects against the database
Here we are using "DetachedCriteria" - sample code as follows :
List<T> inObjList=HibernateTemplate.findByCriteria(DetachedCriteria, 0, 100);
So this is all fine and this works well
This was when we were using Hibernate prior to 4.x
Thanks for your patience till now - will now get to the problem
With Hibernate 4.x - I cannot any longer use Springs Hibernate Template
Even if there is a way to use it - for reasons beyond my control I will not be allowed to use it - so I must go the SessionFactory route
So following the same logic as above - I stored in a map clientName and SessionFactory as against storing HibernateTemplate
The difference was that now I could no longer use - HibernateTemplate.findByCriteria()
So what I did was got the SessionFactory from the map and then tried to get a session:
Session session=testSessionFactory.getCurrentSession();
This failed with the error at above line with the exception :
org.hibernate.HibernateException: No CurrentSessionContext configured!
So did a bit of googling and looking at solutions in stackoverflow - I added the following line in my CFG files - I am not 100% sure if it is correct but used it assuming that mine being a managed environment where I am using weblogic managed datasource it would be JTA:
<property name="hibernate.current_session_context_class">jta</property>
This got me by the first exception - but then I hit the second exception :
org.hibernate.HibernateException: createCriteria is not valid without active transaction
at the following line of code :
Criteria subSelectCriteria =session.createCriteria(getType());
I come from the humble jdbc world - so I thought no big deal - I will simply add some transaction ala jdbc in the code - so I added this :
Transaction tx = null;
session.beginTransaction();
Criteria subSelectCriteria =session.createCriteria(getType());
tx.commit();
It failed at the above commit with the error :
Caused by: org.hibernate.TransactionException: unable to commit against JDBC connection
at org.hibernate.engine.tx.internal.jdbc.JdbcTransaction.doCommit(JdbcTransaction.java:116)
at org.hibernate.engine.tx.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:180)
Caused by: java.sql.SQLException: Cannot call Connection.commit in distributed transaction.
Transaction Manager will commit the resource manager when the distributed tx is committed.
at weblogic.jdbc.wrapper.JTSConnection.commit(JTSConnection.java:663)
So then in my Hibernate CFG file I added the following line and removed the transaction management in my Java code in my Dao :
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
This gave the error :
org.hibernate.HibernateException: No TransactionManagerLookup specified
So please advise what I should do - and thanks for bearing with me on a long post
NOTE - I did find a solution where I can define each SessionFactory as a bean in spring and inject each of these into a
org.springframework.orm.hibernate4.HibernateTransactionManager
However that would mean for each SessionFactory I would need to manually define a bean and inject the HibernateTransactinManager
Thank you !

Spring Boot / Spring Data import.sql doesn't run Spring-Boot-1.0.0.RC1

I've been following the development of Spring Boot, and sometime between the initial version 0.0.5-BUILD-SNAPSHOT and the current version I am using 1.0.0.RC1 I am no longer running my import.sql script.
Here is my configuration for LocalContainerEntityManager and JpaVendorAdapter
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("foo.*");
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
return hibernateJpaVendorAdapter;
}
Interesting the hibernate.hbm2ddl.auto still seems to run, which I think is part of the definition of my SpringBootServletInitializer
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
However, I also noticed that the tables generated no longer have underscores and changed their shape when generated?
However, that could be the result of updating my org.postgresql version like so:
Previously:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.2-1004-jdbc41</version>
</dependency>
Now:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.3-1100-jdbc41</version>
</dependency>
I also had to change pggetserialsequence to pg_get_serial_sequence to get the script to run at all from pgadmin?
I guess I'm confusing what's going on, but most importantly I want to get back to having my import.sql run.
I have been following the sample project: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-data-jpa
And their import.sql isn't running either on 1.0.0-BUILD-SNAPSHOT
The import.sql script is a Hibernate feature I think (not Spring or Spring Boot). It must be running in the sample otherwise the tests would fail, but in any case it only runs if ddl-auto is set to create the tables. With Spring Boot you should ensure that spring.jpa.hibernate.ddl-auto is set to "create" or "create-drop" (the latter is the default in Boot for an embedded database, but not for others, e.g. postgres).
If you want to unconditionally run a SQL script, By default Spring Boot will run one independent of Hibernate settings if you put it in classpath:schema.sql (or classpath:schema-<platform>.sql where <platform> is "postgres" in your case).
I think you can probably delete the JpaVendorAdapter and also the LocalContainerEntityManagerFactoryBean (unless you are using persistence.xml) and let Boot take control. The packages to scan can be set using an #EntityScan annotation (new in Spring Boot).
The default table naming scheme was changed in Boot 1.0.0.RC1 (so nothing to do with your postgres dependency). I'm not sure that will still be the case in RC2, but anyway you can go back to the old Hibernate defaults by setting spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy.
Hey I came across similar issue. My sql script was not getting invoked initially. Then I tried renaming the file from "import.sql" to "schema.sql", it worked. May be give this a shot. My code can be found here - https://github.com/sidnan/spring-batch-example
In addition to what was already said, it's worth noting you can use the data.sql file to import/intialize data into your tables. Just put your data.sql into the root of the classpath (eg: if you're running a Spring Boot app, you put it in the src/main/resources path).
Like was said before, use it together with the property ddl-auto=create-drop, so that it won't crash trying to insert the existing data.
You can also set up which specific file to execute using the spring.datasource.data property. Check out more info here: http://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
Note: the schema.sql mentioned before would contain the whole DB definition. If you want to use this, ensure that Hibernate doesn't try to construct the DB for you based on the Java Entities from your project. This is what de doc says:
If you want to use the schema.sql initialization in a JPA app (with
Hibernate) then ddl-auto=create-drop will lead to errors if Hibernate
tries to create the same tables. To avoid those errors set ddl-auto
explicitly to "" (preferable) or "none"

Where to store database passwords in a spring application or use JNDI?

Seems like a simple task. I have a webapp which requires a database connection. I'd like to be able to drop an updated .war file on this app server and load a new version without having to re-edit an applicationConfig.xml file to specify the database connection parameters for production.
Is using the container to setup the data source and then referencing it from JNDI the preferred way to go? I think it is cleaner having it all defined in the spring .xml file, but I can't come up with a clean way to allow the production password to be set only once as we roll out new versions.
So, how do you specify your database connection information in a spring application so that you can upgrade it without having to re-edit the files?
If you use JNDI, how do you handle setting up of your tests since the JNDI is not going to be available outside of the container?
Thanks!
The typical way to externalize database connection properties is to store them in a .properties file and load using <context:property-placeholder .../> . Then you can have different .properties files for testing and production.
If you choose JNDI, you can use a Spring's mock JNDI support for testing.
One approach is for your Spring configuration file to be composed of fragments related to specific layers in your application.
One such fragment could contain your DataSource defintion. For production, this fragment would use a jee:jndi-lookup. And then for test, have a different fragment would use a DriverManagerDataSource ?
Update:
If you want to change the datasource after deployment, then you can use this technique, along with changing the which datasource is injected into your other beans using a PropertyPlaceholderConfigurer as explained in an old post I wrote
eg:
<bean class="foo.bar.SomeClassNeedingDataSource"">
<property name="dataSource" ref="${the.datasource.to.inject}" />
</bean>
<jee:jndi-lookup id="jndiDataSource" ... />
<bean id="driverManagerDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
...
</bean>
# the properties file
the.datasource.to.inject = jndiDataSource
#the.datasource.to.inject = driverManagerDataSource

Resources