Hibernate+Spring accessing Websphere data source

STeIN_Ott picture STeIN_Ott · Nov 8, 2012 · Viewed 7.3k times · Source

I'm working on a portal project, where I'm forced to use WebSphere Portal, Spring portlet MVC and Hibernate. I don't have much of experience in configuring neither Spring nor Hibernate, so every kind of help is much-much appreciated.
I have created a JDBC data source on WebSphere 7.0.0.25 (with Portal 6.1 installed on it) with JNDI name jdbc/eshop. Then I specified JAAS authentication alias and set it to be container-managed authentication alias. Test connection attempt was successful, so I guess the configuration of the data source was appropriate. My next step was making resource reference in web.xml:

<resource-ref>    
    <description>DB Connection</description>    
    <res-ref-name>eshop</res-ref-name>   
    <res-type>javax.sql.DataSource</res-type>    
    <res-auth>Container</res-auth>   
</resource-ref>

To bind res-ref-name to actual JNDI name in ibm-web-bnd I added this line:

<resource-ref name="eshop" binding-name="java:comp/env/jdbc/eshop" />

Now my Hibernate Entity:

@Entity
@Table(name = "HIBERNATE_TEST", schema = "SCHEME_NAME")
public class HibernateTest implements java.io.Serializable {

private short id;
private String text;

public HibernateTest() {
}

public HibernateTest(String text) {
    this.text = text;
}


@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "ID")
public short getId() {
    return this.id;
}

public void setId(short id) {
    this.id = id;
}

@Column(name = "TEXT", nullable = false, length = 10)
public String getText() {
    return this.text;
}

public void setText(String text) {
    this.text = text;
}

HibernateTestDAO:

@Repository("hibernateTestDAO")
@Transactional
public class HibernateTestDAO  implements GenericDAO<HibernateTest> {
@Autowired
private SessionFactory sessionFactory;

private Session sess;

public HibernateTestDAO ()  {

}

public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public void setSessionFactory(SessionFactory sf) {
    sessionFactory = sf;
}

public boolean add(HibernateTest item) {
    boolean isAdded = false;
    try {
        sess = sessionFactory.getCurrentSession();
        sess.persist(item);
        isAdded =  true;
    } catch (Exception e) {
        e.printStackTrace();
    } 
    return isAdded;
}

public boolean edit(HibernateTest item) {
    boolean isEdited = false;
    try {
        sess = sessionFactory.getCurrentSession();
        item = (HibernateTest)sess.merge(item);
        sess.update(item);
        isEdited =  true;
    } catch (Exception e) {
        e.printStackTrace();
    } 
    return isEdited;
}

public boolean delete(HibernateTest item) {
    boolean isDeleted = false;
    try {
        sess = sessionFactory.getCurrentSession();
        item = (HibernateTest)sess.merge(item);
        sess.delete(item);
        isDeleted =  true;
    } catch (Exception e) {
        e.printStackTrace();
    } 
    return isDeleted;
}

@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public List<HibernateTest> getAll() {
    List<HibernateTest> l = new ArrayList<HibernateTest>(0);
    try {
        sess = sessionFactory.getCurrentSession();
        Query q = sess.createQuery("FROM HibernateTest");
        l = (List<HibernateTest>) q.list();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return l;
}

@Override
@Transactional(readOnly = true)
public HibernateTest getByID(long id) {
    try {
        sess = sessionFactory.getCurrentSession();
        return (HibernateTest)sess.get(HibernateTest.class, new Short((short)id));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
}

As you can see, I don't want to use program-managed transactions. I have learned, that Spring has WebSphereUowTransactionManager class to manage transactions of WebSphere data sources. I have tried this config (applicationContext.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config /> 
    <context:component-scan base-package="test.dao,test.entities,test.portlet.controller" />

        <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/eshop"/>
    <property name="lookupOnStartup" value="false"/>
    <property name="cache" value="true" />
    <property name="proxyInterface"  value="javax.sql.DataSource" />
    </bean>

    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory"     class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="annotatedClasses">
        <list>
            <value>test.entities.HibernateTest</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
          <prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
          <prop key="hibernate.show_sql">true</prop>
          <!--           IBM WAS SPECIFIC             -->          
          <prop key="hibernate.connection.datasource">jdbc/eshop</prop>
          <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</prop>
          <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</prop>
          <prop key="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform</prop> 
          <!--           /END IBM WAS SPECIFIC        -->
        </props>
    </property>
    </bean>

    <bean id="transactionManager"
    class="org.springframework.transaction.jta.WebSphereUowTransactionManager">
        <property name="userTransactionName" value="java:comp/UserTransaction" />
    </bean>  

    <bean id="hibernateTestDAO" class="test.dao.HibernateTestDAO" /> 

    </beans> 

, but it leads to this exception chain:

Error 500: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is org.springframework.transaction.TransactionSystemException: JTA UserTransaction is not available at JNDI location [java:comp/UserTransaction]; nested exception is org.springframework.jndi.TypeMismatchNamingException: Object of type [class com.ibm.ws.tx.jta.UserTransactionImpl] available at JNDI location [java:comp/UserTransaction] is not assignable to [javax.transaction.UserTransaction]

I know that the typical solution in this situation is to remove jta.jar from the classpath, but I don't have this jar in the classpath! I just don't get how IBM could use some vendor-specific library to create UserTransaction class, but I should get this worked, so I need your help to configure my application.

Also I want to add that I worked around this issue for about 3 days, tried different configs, but the was no success, sadly. So I decided to use a working (as author said) config from this source as a starting point asking the question here. I have run out of ideas trying to fix this, so I need help.

UPDATE

I could solve the problem with Hibernate's complains about current transaction: there was no <tx:annotation-driven transaction-manager="transactionManager" /> element specified, so I suppose that UOWManager didn't know when I want transaction to start. But as it turned out, it was not the end of my difficulties. The code of the portlet controller is below:

public class TestDIController extends AbstractController implements ApplicationContextAware{

private ApplicationContext appCtx;

@Override
public ModelAndView handleRenderRequest(RenderRequest request,
        RenderResponse response) throws Exception {
    appCtx = getApplicationContext();
    System.out.println(UserTransaction.class.getClassLoader());
    @SuppressWarnings("rawtypes")
    GenericDAO cd = (GenericDAO)appCtx.getBean("hibernateTestDAO");
    ModelAndView m = new ModelAndView("test").addObject("list", cd.getAll());
    return m;
}
}

When ModelAndView instance tries to add new object to itself, HibernateTestDAO's line of code l = (List<HibernateTest>) q.list(); throws the following exception

org.hibernate.exception.SQLGrammarException: Could not open connection

... some stack trace...

Caused by: java.sql.SQLException: [jcc][t4][10205][11234][3.58.81] Null userid is not supported. ERRORCODE=-4461, SQLSTATE=42815DSRA0010E: SQL State = 42815, Error Code = -4 461

And this error is confusing, because I have specified Container-managed authentication alias, see the picture below

enter image description here where 'auth' is my JAAS-J2C authentication alias. And when I test connection to the data source from the administrative console, it's allways successful! I do my best searching the answer to this, but up at this point there is no results. For example, the problem described here is pretty same to mine, but the solution doesn't suit me, because the element <lookup-name> is undefined in my web.xml. May be I should configure some J2C options, resource adapters on WebSphere, may be there is some missed configuration lines in applicationContext, who can help? I'm really confused this time.

Answer

Marcin Płonka picture Marcin Płonka · Nov 8, 2012

com.ibm.ws.tx.jta.UserTransactionImpl implements javax.transaction.UserTransaction. Your code is fine. Most probably you're using parent-last ClassLoader policy and have javax.transaction.UserTransaction in one of jars in your application.

Try to get rid of that javax.transaction.UserTransaction from your application.