Stateful Session Bean to persist Entity
In this example we shall show you how to use a Stateful Session Bean to persist an Entity. In a stateful session bean, the instance variables represent the state of a unique client-bean session. The state is retained for the duration of the client-bean session. If the client removes the bean or terminates, the session ends and the state disappears. This transient nature of the state is not a problem, however, because when the conversation between the client and the bean ends there is no need to retain the state.
Here, we are creating a stateful bean to persist an entity, as described below:
The Data Transfer (Domain) object
Class Employee
is an entity, that is a lightweight persistence domain object. Typically an entity represents a table in a relational database, and each entity instance corresponds to a row in that table. The persistent state of an entity is represented either through persistent fields or persistent properties. These fields or properties use object/relational mapping annotations to map the entities and entity relationships to the relational data in the underlying data store. The class is annotated with the javax.persistence.Entity
annotation, it has a public or protected, no-argument constructor and it implements the Serializable interface.
package com.javacodegeeks.snippets.enterprise; import java.io.Serializable; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Employee implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String name; private String surname; private String title; private Double salary; private Date created; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", surname=" + surname + ", title=" + title + ", salary="+salary+ ", created=" + created+"]"; } }
Create the EJB implementation class.
The CalculatorService
is an EJB implementation class that is a stateful session bean with a few methods. It is annotated with the javax.ejb.Stateful
annotation. The class has a default public constructor. It uses the javax.annotation.PostConstruct
annotation to perform any initialization and javax.annotation.PreDestroy
annotation as a callback notification to signal that the instance is in the process of being removed by the container. It also uses the javax.ejb.Remove
annotation in a method to indicate to the container that the bean is to be removed by the container after completion of the method. It also uses the javax.ejb.PrePassivate
annotation in another method to designate it to receive a callback before the bean is passivated. The javax.ejb.PostActivate
annotation is used in a method to designate it to receive a callback after the bean has been activated.
A Persistent Context of EXTENDED Type can be used. Usually, an EntityManager lives and dies within a JTA transaction. Once the transaction is finished, all persistent objects are detached from the EntityManager and are no longer managed. Any local caching the EntityManager instance had done is lost. In addition you can define long-living EntityManagers that live beyond the scope of a JTA transaction. This is called an Extended Persistence Context. When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed. Extended persistence contexts can only be used within Stateful session beans.
The CalculatorService
has a local interface that defines the bean’s business and life cycle methods, decorated with the @Local
annotation. It also has a remote interface decorated with the @Remote
annotation, that can run on a different machine and a different Java virtual machine (JVM) than the CalculatorService
.
package com.javacodegeeks.snippets.enterprise; import java.util.Collection; import java.util.Date; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.ejb.PrePassivate; import javax.ejb.PostActivate; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.Query; @Stateful public class EmployeeService implements EmployeeServiceLocal, EmployeeServiceRemote { /* * With a Stateful Session Bean a Persistent Context of EXTENDED Type can be used. * Usually, an EntityManager lives and dies within a JTA transaction. Once the * transaction is finished, all persistent objects are detached from the EntityManager * and are no longer managed. Any local caching the EntityManager instance had done is lost. * In addition you can define long-living EntityManagers that live beyond the scope of a JTA * transaction. This is called an Extended Persistence Context. When you specify that an * injected EntityManager is an extended persistence context, all object instances remain * managed. Extended persistence contexts can only be used within Stateful session beans. */ @PersistenceContext(unitName = "TutorialPU", type = PersistenceContextType.EXTENDED) EntityManager entityManager; public EmployeeService() { } public Employee createEmployee(String name, String surname, String title, double salary) { Employee employee = new Employee(); employee.setName(name); employee.setSurname(surname); employee.setTitle(title); employee.setSalary(salary); employee.setCreated(new Date()); entityManager.persist(employee); return employee; } public void removeEmployee(long id) { Employee employee = findEmployee(id); if (employee != null) { entityManager.remove(employee); } } public Employee promoteEmployee(long id, String newTitle, double newSalary) { Employee employee = entityManager.find(Employee.class, id); if (employee != null) { employee.setTitle(newTitle); employee.setSalary(newSalary); } return employee; } public Employee findEmployee(long id) { return entityManager.find(Employee.class, id); } public Collection<Employee> findAllEmployees() { Query query = entityManager.createQuery("SELECT e FROM Employee e"); return (Collection<Employee>) query.getResultList(); } // Lifecycle operations @PostConstruct public void PostConstruct() { System.out.println("PostConstruct"); } @PostActivate public void PostActivate() { System.out.println("PostActivate"); } @PrePassivate public void PrePassivate() { System.out.println("PrePassivate"); } @PreDestroy public void shutdown() { System.out.println("PreDestroy"); } @Remove public void remove() { System.out.println("Remove"); } }
The EJB local interface (suitable for in VM communication)
package com.javacodegeeks.snippets.enterprise; import java.util.Collection; import javax.ejb.Local; @Local public interface EmployeeServiceLocal { public Employee createEmployee(String name, String surname, String title, double salary); public void removeEmployee(long id); public Employee promoteEmployee(long id, String newTitle, double newSalary); public Employee findEmployee(long id); public Collection<Employee> findAllEmployees(); }
The EJB remote interface (suitable for intra VM communication)
package com.javacodegeeks.snippets.enterprise; import java.util.Collection; import javax.ejb.Remote; @Remote public interface EmployeeServiceRemote { public Employee createEmployee(String name, String surname, String title, double salary); public void removeEmployee(long id); public Employee promoteEmployee(long id, String newTitle, double newSalary); public Employee findEmployee(long id); public Collection<Employee> findAllEmployees(); }
The persistence.xml file driving the JPA framework
<persistence 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" version="1.0"> <persistence-unit name="TutorialPU" > <jta-data-source>java:/DefaultDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> <!-- <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.connection.url" value="jdbc:hsqldb:data/tutorial"/> </properties> --> </persistence-unit> </persistence>
The application.xml file describing the modules in the .ear archive
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application_1_4.xsd" version="1.4"> <display-name>ExampleEJB3</display-name> <module> <java>exampleEJB3-persistence.jar</java> </module> <module> <ejb>exampleEJB3.jar</ejb> </module> </application>
The structure of the .ear archive
exampleEJB3.ear | |_exampleEJB3-persistence.jar | |_com | | |_javacodegeeks | | |_snippets | | |_enterprise | | |_Employee.class | |_META-INF | |_persistence.xml | |_exampleEJB3.jar | |_com | | |_javacodegeeks | | |_snippets | | |_enterprise | | |_EmployeeService.class | | |_EmployeeServiceLocal.class | | |_EmployeeServiceRemote.class | |_META-INF | |_META-INF |_application.xml
Run the application using a client
In CalculatorServiceClient
we connect to JBoss naming service running on local host and on default port 1099. We use the Context to set the configuration for the JBoss server, such as Context.INITIAL_CONTEXT_FACTORY
, Context.PROVIDER_URL
and Context.URL_PKG_PREFIXES
. We get the bean using the lookup(Name name)
method of Context to invoke its methods.
package com.javacodegeeks.snippets.enterprise; import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; public class EmployeeServiceClient { public static void main(String[] a) throws Exception { /* * Connecting to JBoss naming service running on local host and on * default port 1099 the environment that should be created is like the * one shown below : */ Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); env.put(Context.PROVIDER_URL, "jnp://localhost:1099"); env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); Context ctx = new InitialContext(env); // We get a reference of the remote EJB interface to invoke its business methods EmployeeServiceRemote employeeService = (EmployeeServiceRemote) ctx.lookup("exampleEJB3/EmployeeService/remote"); Employee employee = employeeService.createEmployee("Byron", "Kiourtzoglou", "Master Software Engineer", 2000d); long employeeId = employee.getId(); System.out.println(employeeService.findEmployee(employeeId)); employeeService.promoteEmployee(employeeId, "Principal Software Engineer", 3000d); System.out.println(employeeService.findEmployee(employeeId)); } }
Output:
Employee [id=1, name=Byron, surname=Kiourtzoglou, title=Master Software Engineer, salary=2000.0, created=2011-12-03 17:31:30.203]
Employee [id=1, name=Byron, surname=Kiourtzoglou, title=Principal Software Engineer, salary=3000.0, created=2011-12-03 17:31:30.203]
This was an example of how to use a Stateful Session Bean to persist an Entity.