EJB 3.0 and Spring 2.5
Join the DZone community and get the full member experience.
Join For FreeWhy is it that developers from these two communities don't like to see eye to eye? I have been using both Spring from its inception, and EJB's from 2001. Just like everyone else did, I just dreaded the huge amount of XML we had to write for both of these; configuration files in Spring, and deployment descriptors in EJB 2.x. However, Java 5 came to our rescue and now annotations have mostly replaced XML files in both these. But, after having used these two latest versions Spring 2.5 and EJB 3.0, I think that they complement each other, rather than compete with each other. There are certain features which are powerful in Spring, and equal number of features powerful on the EJB side as well.
Many developers fail to understand, that Spring is a popular non-standard framework created by Spring Source, while EJB 3.0 is a specification which is supported by major JEE vendors. There are a few developers with whom I have worked earlier, prefer to use standard specification, and they chose EJB2.x/ and are moving now to EJB 3.0. However, there isn't anything stopping you from using Spring along with EJB, is it? The complaint I have heard many times is: We can't use non-standard framework". The same developers who complain about Spring being non-standard use home grown frameworks, which was and will be forever so hard to even decipher. How can it be a specification when a couple of developers write several thousand lines of code?
Also, a recent article published here at Javalobby by Adam Bien showed the trend moving towards EJB 3.0, right? Anyway, in this article we will see how by adding a few lines in your Spring configuration file, you can seamlessly use EJB 3.0 components within your Spring application.
Back to our EJB 3.0 and Spring 2.5 article, we are going to see how easy and simple it is to access EJB 3.0 components in Spring by using its powerful dependency injection mechanism to inject an instance of our Customer session bean. This Customer session bean, in turn uses the Entity Manager for the CRUD operations on the Customer Entity.
Here are the detailed steps:
Step 1: Create a simple JPA Entity.
The Java Persistence API (JPA) is defined as part of the Java EE 5 specification. Creating entities using JPA is as simple as creating a POJO with a few annotations as shown below:
package com.ejb.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
*
* @author meerasubbarao
*/
@Entity
@Table(name = "CUSTOMER", catalog = "", schema = "ADMIN")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "CUSTOMER_ID")
private Long customerId;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@Column(name = "MIDDLE_NAME")
private String middleName;
@Column(name = "EMAIL_ID")
private String emailId;
public Customer() {
}
public Customer(Long customerId) {
this.customerId = customerId;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
}
Step 2: Create a EJB 3.0 Session bean.
This session beans uses the Entity Manager for the Create Read Update Delete (CRUD) operations for the Customer Entity we created in Step 1. The CRUD opertions are also published as web methods by adding a few basic annotations.
The Interface:
package com.ejb.service;
import com.ejb.domain.Customer;
import java.util.Collection;
import javax.ejb.Remote;
/**
*
* @author meerasubbarao
*/
@Remote
public interface CustomerService {
Customer create(Customer info);
Customer update(Customer info);
void remove(Long customerId);
Collection<Customer> findAll();
Customer[] findAllAsArray();
Customer findByPrimaryKey(Long customerId);
}
The Implementation Class:
package com.ejb.service;
import com.ejb.domain.Customer;
import java.util.Collection;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.jws.WebMethod;
/**
*
* @author meerasubbarao
*/
@WebService(name = "CustomerService", serviceName = "CustomerService", targetNamespace = "urn:CustomerService")
@SOAPBinding(style = SOAPBinding.Style.RPC)
@Stateless(name = "CustomerService")
public class CustomerServiceImpl implements CustomerService {
@PersistenceContext
private EntityManager manager;
@WebMethod
public Customer create(Customer info) {
this.manager.persist(info);
return info;
}
@WebMethod
public Customer update(Customer info) {
return this.manager.merge(info);
}
@WebMethod
public void remove(Long customerId) {
this.manager.remove(this.manager.getReference(Customer.class, customerId));
}
public Collection<Customer> findAll() {
Query query = this.manager.createQuery("SELECT c FROM Customer c");
return query.getResultList();
}
@WebMethod
public Customer[] findAllAsArray() {
Collection<Customer> collection = findAll();
return (Customer[]) collection.toArray(new Customer[collection.size()]);
}
@WebMethod
public Customer findByPrimaryKey(Long customerId) {
return (Customer) this.manager.find(Customer.class, customerId);
}
}
Step 3. Compile, Package and Deploy to an application server.
There are no server specific annotations in either the Entity class or the Session bean. Package the entity class, the session bean interface, implemetation class along with the persistence.xml file in a JAR file. Since I am deploying this application to GlassFish, I am using the default persistence provider which is TopLink. Start your application server, and deploy this JAR to it. The contents of persistence.xml file is as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="SpringAndEJBPU" transaction-type="JTA">
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<jta-data-source>spring-ejb</jta-data-source>
<properties>
<property name="toplink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
Once your application is deployed, make sure you check the JNDI name of your session bean. In GlassFish, click on the JNDI Browsing toolbar button to view the JNDI names.
Step 4: Test the Stateless Session beans.
Since my session beans are published as web services, I can just easily test them using either the test page provided by GlassFish application server, or by using SoapUI and make sure that my Customer entity can be persisted using the CustomerService session bean. I am using the default test page provided by Glass Fish for web services:
Step 5: Create a Spring Bean.
Here, I define a simple CustomerManager interface, and an implementation class; just to show how things are wired using Spring.
package com.spring.service;
import com.ejb.domain.Customer;
/**
*
* @author meerasubbarao
*/
public interface CustomerManager {
public void addCustomer(Customer customer);
public void removeCustomer(Long customerId);
public Customer[] listCustomers();
}
package com.spring.service;
import com.ejb.domain.Customer;
import com.ejb.service.CustomerService;
/**
*
* @author meerasubbarao
*/
public class CustomerManagerImpl implements CustomerManager {
CustomerService customerService;
public void setCustomerService(CustomerService customerService) {
this.customerService = customerService;
}
public void removeCustomer(Long customerId) {
customerService.remove(customerId);
}
public Customer[] listCustomers() {
return customerService.findAllAsArray();
}
public void addCustomer(Customer customer) {
customerService.create(customer);
}
}
Step 6: Injecting the EJB 3.0 Session bean into our Spring Beans.
As seen above, I am using setter injection to inject an instance of Customer Service and in turn invoke a method on the Stateless session bean. How do we inject an EJB into a Spring bean? It is quite simple as shown below in the Spring configuration file:
<?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:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<jee:jndi-lookup id="customerService" jndi-name="com.ejb.service.CustomerService">
</jee:jndi-lookup>
<bean id="manageCustomer"
class="com.spring.service.CustomerManagerImpl">
<property name="customerService" ref="customerService" />
</bean>
</beans>
The most important aspect here is the wiring of the EJB within the Spring configuration using the <jee:jndi-lookup> element in the jee schema.
<jee:jndi-lookup id="customerService" jndi-name="com.ejb.service.CustomerService">
</jee:jndi-lookup>
Next, we need to wire this with our Spring bean which is done as shown below:
<bean id="manageCustomer"
class="com.spring.service.CustomerManagerImpl">
<property name="customerService" ref="customerService" />
</bean>
Step 7: Test
How do we know that it works. Lets create a simple class and test.
package com.spring.client;
import com.ejb.domain.Customer;
import com.spring.service.CustomerManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAndEJBMain {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("SpringXMLConfig.xml");
CustomerManager service = (CustomerManager) context.getBean("manageCustomer");
Customer customer = new Customer();
customer.setFirstName("Meera");
customer.setLastName("Subbarao");
customer.setMiddleName("B");
customer.setEmailId("meera@springandejb.com");
customer.setCustomerId(new Long(1));
service.addCustomer(customer);
for (Customer cust : service.listCustomers()) {
System.out.println(cust.getFirstName());
System.out.println(cust.getLastName());
System.out.println(cust.getMiddleName());
System.out.println(cust.getEmailId());
}
service.removeCustomer(new Long(1));
}
}
And here is the sample output from my IDE:
We can go back to our GlassFish web services test page, and test the findAll method. It should have two entities.
In this article, we saw how quick and easy it was to create JPA entities, persist them using Entity Manager from within our session bean. We also saw how easy it was to publish web services by adding a few annotations to our session beans. Next, we saw how to create a simple Spring bean, inject our session bean, and finally call methods on this session bean from our Spring application.
Spring isn't in my opinion a replacement for EJB 3.0, you can mix and match EJB 3.0 and Spring 2.5 components to get the best of both.
Opinions expressed by DZone contributors are their own.
Comments