tags:

views:

94

answers:

2

Hi,

I found this article which talks about using Spring as a JPA container:

http://java.sys-con.com/node/366275

I have never used Spring before this and am trying to make this work and hope someone can help me.

In the article it states that you need to annotate a Spring bean with @Transactional and methods/fields with @PersistenceContext in order to provide transaction support and to inject an entity manager.

Is there something the defines a bean as a "Spring Bean"? I have a bean class which implements CRUD operations on entities using generics:

@Transactional
public class GenericCrudServiceBean implements GenericCrudService
{
    @PersistenceContext(unitName="MyData")
    private EntityManager em;

    @Override
    @PersistenceContext
    public <T> T create(T t)
    {
        em.persist(t);
        return t;
    }

    @Override
    @PersistenceContext
    public <T> void delete(T t)
    {
        t = em.merge(t);
        em.remove(t);
    }
...
...
...
    @Override
    @PersistenceContext
    public List<?> findWithNamedQuery(String queryName)
    {
        return em.createNamedQuery(queryName).getResultList();
    }
}

Originally I only had this peristence context annotation:

@PersistenceContext(unitName="MyData")
private EntityManager em;

but had a null em when findWithNamedQuery was invoked. Then I annotated the methods as well, but em is still null (no injection?).

I was wondering if this had something to do with my bean not being recognized as "Spring".

I have done configuration as best I could following the directions in the article including setting the following in my context.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:tx="http://www.springframework.org/schema/tx"
    tx:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"&gt;

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="MyData" />
        <property name="dataSource" ref="dataSource" />
        <property name="loadTimeWeaver"
            class="org.springframework.classloading.ReflectiveLoadTimeWeaver" />
        <property name="jpaVendorAdapter" ref="jpaAdapter" />
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:MySID" />
        <property name="username" value="user" />
        <property name="password" value="password" />
        <property name="initialSize" value="3" />
        <property name="maxActive" value="10" />
    </bean>

    <bean id="jpaAdapter"
        class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
        <property name="databasePlatform"
            value="org.eclipse.persistence.platform.database.OraclePlatform" />
        <property name="showSql" value="true" />
    </bean>

    <bean
        class="org.springframework.ormmjpa.support.PersistenceAnnotationBeanPostProcessor" />
    <tx:annotation-driven />
</beans>

I guessed that these belonged in the context.xml file because the article never specifically said which file is the "application context" file. If this is wrong, please let me know.

A: 

In order for a class to be a spring bean it has to be:

  • either mapped as <bean id=".." class=".."> or annotated with @Component, @Service, or similar stereotype annotations
  • not instantiated by you. If you want spring to wire your dependencies, you must let it instantiate your classes. So no new GenericCrudServiceBean() (there are "tricks" to enable dependency injection even when using the new operator, but they are for special cases)

In order not to need to instantiate your classes, you must put something "in front" of your controllers and services. For web-frameworks this would be a Servlet, or addition to the servlet, that handles your classes via the ApplicationContext of spring - in Spring MVC DispatcherServlet is used - in JSF a custom spring ELResolver is used

Depending on your framework, look for "X spring integration".

For your case - Portlets - spring have the Portlet MVC

Bozho
So, if I add to @Service to my class I've met the first requirement. How would I go about meeting the second considering the GenericCrudServiceBean is being used in a portlet deployed on Liferay/Tomcat? When/how would Spring instantiate it?
sdoca
you'd have something to sit in front and intercept requests. For example a dispatcher servlet. Read the relevant parts of the docs.
Bozho
Thanks for the suggestions. I searched for any reference to using Spring and portlets since that's the framework I'm using. It appears to not be supported.
sdoca
@sdoca on the contrary. See my update
Bozho
A: 

In the article it states that you need to annotate a Spring bean with @Transactional and methods/fields with @PersistenceContext in order to provide transaction support and to inject an entity manager.

That's correct and you shouldn't add @PersistenceContext on your methods, only on the private EntityManager em attribute.

Is there something the defines a bean as a "Spring Bean"?

That's a bean managed by the Spring container (i.e. Spring manages its lifecycle, wires it with other Spring beans, etc). This implies of course that you are creating a Spring container at some point.

I guessed that these belonged in the context.xml file because the article never specifically said which file is the "application context" file. If this is wrong, please let me know.

This belongs in an application context file which is just an XML file and the name doesn't really matter as long as you tell the Spring container to load it. And actually, that's the big question: how do you run your code?

The article runs the sample from a test case which extends a Spring class providing Spring support (by this I mean that it will create the Spring container for you) and allowing to tell Spring which application context file(s) to load (in this case, my-spring-config.xml) by providing a getConfigLocations method:

package org.bookguru;

import org.springframework.test.jpa.AbstractJpaTests;

public class BookInventorySystemTest extends AbstractJpaTests {

    private BookInventorySystem bookInventorySystem;

    public void setBookInventorySystem(
       BookInventorySystem bookInventorySystem) {
       this.bookInventorySystem = bookInventorySystem;
    }

    protected String[] getConfigLocations() {
       return new String[] {"/my/path/my-spring-config.xml"};
    }
} 

So, how do you run your code?

Pascal Thivent
My code is running as a portlet in Liferay/Tomcat. It's because we're using the Tomcat bundle for Liferay (and not deploying to a JEE container), that I was looking at using Spring as a JPA to provide the injection of the em and transaction management.
sdoca
If I had a class that extended a Spring class (which one?) that then creates the Spring container and that class made use of GenericCrudServiceBean, would this also meet the second requirement of Bozho's post? Would be nice to kill two birds with one stone...
sdoca
@sdoca I don't have enough knowledge of portlets and liferay to answer this.
Pascal Thivent
Thanks Pascal, I appreciate you taking the time to look at this.
sdoca