Using Spring as a JPA Container
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">
<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="开发者_如何学GojpaAdapter"
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.
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?
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 thenew
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
It's worth noting the following from the Spring documentation
@EnableTransactionManagement and only looks for @Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services. See Section 17.2, “The DispatcherServlet” for more information.
What this means when you are using portlets is that you must enable annotation-driven transactions with in your portlet context. This may possibly be your problem.
This hit me today - I had a multi-portlet app and wanted to set up all the database beans in the applicationcontext. That was fine and worked in all my tests.
However when deployed to the portlet container, none of my portlet-specific beans were aware of the transaction - the reason was because I lacked the in my portletcontext. Clear as day once I read the manual....
精彩评论