6

We have an application which uses a Spring container on the server which uses EJB3 entities and Hibernate to persist data in a PostgreSQL database.

I'd like to add another connection to a separate database using a separate EntityManager, but the vendor of the DBMS (Trifox Vortex) does not have a DataSource type driver.

Can the Hibernate EntityManager use a simple JDBC-compliant driver to implement EJB3 JPA persistence of Entities?

Do I need to have a specific Hibernate Dialect for the DBMS I'm connecting to?

For what it's worth, here's the XML definition of our current Entity Manager Factory in Spring:

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="UniWorks-EntityPersistenceUnit"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaDialect">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
    </property>
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="${db.showsql}"/>
        <property name="generateDdl" value="${db.generate}"/>
        <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect"/>
      </bean>
    </property>
    <property name="jpaProperties">
      <props>
        <prop key="hibernate.hbm2ddl.auto">${db.hbm2ddl}</prop>
      </props>
    </property>
  </bean>

UPDATE/PROGRESS: 21/3/13

After much to-ing and fro-ing with the Vendor, I managed to get their JDBC driver working with a simple Java test program.

Unfortunately, their JDBC driver is obviously not sufficient for Hibernate to get enough information as Spring fails to create the EntityManagerFactory with the following stack-trace:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [bundle://222.0:0/META-INF/spring/cobol.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: UniWorks-CobolPersistenceUnit] Unable to build EntityManagerFactory
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1455)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)[59:org.springframework.context:3.1.1.RELEASE]
    at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.access$1600(AbstractDelegatedExecutionApplicationContext.java:69)[90:org.springframework.osgi.core:1.2.1]
    at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$4.run(AbstractDelegatedExecutionApplicationContext.java:355)[90:org.springframework.osgi.core:1.2.1]
    at org.springframework.osgi.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)[90:org.springframework.osgi.core:1.2.1]
    at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:320)[90:org.springframework.osgi.core:1.2.1]
    at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:132)[91:org.springframework.osgi.extender:1.2.1]
    at java.lang.Thread.run(Thread.java:662)[:1.6.0_24]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: UniWorks-CobolPersistenceUnit] Unable to build EntityManagerFactory
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:677)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:132)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268)[64:org.springframework.orm:3.1.1.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)[64:org.springframework.orm:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)[58:org.springframework.beans:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)[58:org.springframework.beans:3.1.1.RELEASE]
    ... 14 more
Caused by: org.hibernate.HibernateException: Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use
    at org.hibernate.dialect.resolver.DialectFactory.determineDialect(DialectFactory.java:141)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.dialect.resolver.DialectFactory.buildDialect(DialectFactory.java:97)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:117)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2119)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2115)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1339)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)[80:com.springsource.org.hibernate:3.3.2.GA]
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:669)[80:com.springsource.org.hibernate:3.3.2.GA]
    ... 19 more
Caused by: java.sql.SQLException: getDatabaseMajorVersion: Not supported by VORTEXjdbc.
    at vortex.sql.vortexDbMetaData.getDatabaseMajorVersion(vortexDbMetaData.java:362)
    at org.hibernate.dialect.resolver.DialectFactory.determineDialect(DialectFactory.java:131)[80:com.springsource.org.hibernate:3.3.2.GA]
    ... 26 more

So it looks like I might have to write my own dialect sub-class. Anybody give me any pointers as to where to start looking, or what existing sub-class to extend?

DuncanKinnear
  • 4,563
  • 2
  • 34
  • 65

2 Answers2

3

This should work (SimpleDriverDataSource + org.springframework.orm.jpa.vendor.Database.DEFAULT):

   <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="org.h2.Driver"/> <!-- put your driver here -->
        <property name="url" value="jdbc:h2:mem:test"/> <!-- put your jdbc url here -->
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

            <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="TestPersistenceUnit"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false"/>
                <property name="database" value="DEFAULT"/>
            </bean>
        </property>
    </bean>
Michail Nikolaev
  • 3,733
  • 22
  • 18
  • Thanks for your answer. I'm going to try and set up a working example to test your theory before I give the bounty, but if it doesn't look like I'll get there before the deadline, I'll give you it anyway. Cheers. – DuncanKinnear Mar 11 '13 at 21:26
  • I've given you the bounty as the code you provided has certainly got me much further than I expected. At this stage I can see (from the stack trace) that Spring is calling hibernate which is creating the simple driver data source, but at the moment the actual driver itself is throwing an error about the username and password, so I think we're nearly there. – DuncanKinnear Mar 17 '13 at 22:27
  • thanks. Maybe you could provide some stacktrace and your datasource settings? (in my code default user and password for H2 database used) – Michail Nikolaev Mar 17 '13 at 22:31
  • I've got past the user/password error (JDBC URL was incorrect), and now I have a licensing issue. Just waiting on the proper eval license from the supplier. Cheers. – DuncanKinnear Mar 18 '13 at 03:40
  • Can you have a look at my question update with progress to see if you have any further ideas? – DuncanKinnear Mar 21 '13 at 02:14
  • 1
    Possible you will required to implement custom Dialet class (such as here - http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/package-summary.html) and register it (http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/session-configuration.html#configuration-optional-dialects) – Michail Nikolaev Mar 21 '13 at 07:18
0

Hibernate can use connection pools other than a DataSource. So you could use c3p0 or proxool or dbcp or even your own connection pool. Hibernate does have a built in connection pool, though it is only meant for testing/prototyping; it would allow you to just pass in the driver name, url, username, password and it would set up some (very) simple pooling.

There are a few different options to achieve this. First, Hibernate always handles obtaining and releasing connections through a unified contract : org.hibernate.engine.jdbc.connections.spi.ConnectionProvider. So one option is to pass in your own ConnectionProvider implementation.

Another option is to pass in the driver, url, username, password and a ConnectionProvider to use. For the driver, url, username and password both Hibernate and JPA define mechanisms to do that. For JPA you'd use either persistence.xml or the settings javax.persistence.jdbc.driver, javax.persistence.jdbc.url, javax.persistence.jdbc.user and javax.persistence.jdbc.password. As far as the ConnectionProvider, by default Hibernate will choose the non-supported, not-intended-for-production one. But to tell Hibernate a different one to use, you would use the hibernate.connection.provider_class setting. Hibernate has built-in support for integrating with both c3p0 and proxool as back end connection pools (Hibernate will set up and manage the c3p0/proxool pool for you and use that pool for connection management).

As the other answer points out, another option is to use a DataSource wrapper around your driver and connection info.

Sorry, I do not know Spring so I do not know all the Spring-specific ways to specify persistence.xml or JPA settings or Hibernate settings.

Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46