jpa server-side deployment -...

38
JPA Server-Side Deployment Revision: v2018-10-31 Built on: 2018-12-05 08:45 EST Copyright © 2018 jim stafford ([email protected]) This presentation provides coverage of how to package a JPA persistence unit within an EJB or WAR deployed to the server. It covers the resources involved and how they can be injected into the beans of the data tier as well as various issues that can arise and suggested solutions.

Upload: duongphuc

Post on 12-Aug-2019

229 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

JPA Server-Side Deployment

Revision: v2018-10-31Built on: 2018-12-05 08:45 EST

Copyright © 2018 jim stafford ([email protected])

This presentation provides coverage of how to package a JPA persistence unit within an EJB or

WAR deployed to the server. It covers the resources involved and how they can be injected into

the beans of the data tier as well as various issues that can arise and suggested solutions.

Page 2: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction
Page 3: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

iii

Purpose ............................................................................................................................ v

1. Server-side Resources ................................................................................................ 1

1.1. SQL DataSource (defined in Server - standalone.xml) ........................................... 1

1.2. Server-side Persistence Units .............................................................................. 1

1.2.1. transaction-type=JTA (default) .................................................................. 1

1.2.2. transaction-type=RESOURCE_LOCAL ...................................................... 2

1.3. persistence.xml Placement .................................................................................. 2

1.3.1. EJB persistence.xml Placement ................................................................ 2

1.3.2. WAR persistence.xml Placement ............................................................... 3

1.4. Reference External @Entities .............................................................................. 4

1.4.1. Reference External @Entities: EAR Deploy ............................................... 4

1.4.2. Reference External @Entities: WAR Deploy .............................................. 4

1.5. Summary ............................................................................................................ 5

2. Persistence Unit/Context Injection .............................................................................. 7

2.1. @PersistenceContext Injection ............................................................................ 7

2.2. @PersistenceUnit Injection .................................................................................. 8

2.3. Context and Dependency Injection (CDI) .............................................................. 8

2.4. Summary ............................................................................................................ 9

3. Managed Entities and Remote Interfaces .................................................................. 11

3.1. Problem: Provider Proxy Classes Marshaled to Client ......................................... 11

3.1.1. Potential Solution: Add JPA Provider Classes to Client Classpath .............. 11

3.1.2. More Scenario Details ............................................................................ 11

3.1.3. Candidate Solution: Cleansed DTOs ....................................................... 12

3.2. Problem: Lazy Load Exception .......................................................................... 13

3.2.1. Lazy Load Scenario Details .................................................................... 14

3.2.2. Candidate Solution: Load thru "Touching" Object Tree in Remote Facade ... 15

3.2.3. Candidate Solution: Load thru Fetching Object Tree in Query .................... 16

3.2.4. Candidate Solution: Abstract Remote Interface View with DTO .................. 17

3.3. Summary .......................................................................................................... 19

4. Persistence Context Propagation .............................................................................. 21

4.1. Stateless Persistence Context Interaction ........................................................... 21

4.1.1. Stateless EJB Example Check-in ............................................................ 21

4.1.2. Stateless EJB Example Client Check-in ................................................... 22

4.1.3. EJB Gets Available Rooms from DB ........................................................ 22

4.1.4. EJB Gets Specific Room ........................................................................ 22

4.1.5. EJB Adds Guest .................................................................................... 23

4.1.6. EJB associates Guest with Room ............................................................ 23

4.2. Stateful Facade Persistence Context Interaction ................................................. 24

4.2.1. Example Stateful Reservation EJB Caches Guest Requests for Client ........ 24

4.2.2. Example Stateful Reservation EJB Acting on Cached State ....................... 24

4.2.3. Stateful EJB Example Client Check-in ..................................................... 25

4.2.4. Stateful EJB Persists Guests Prior to Active JTA Transaction .................... 25

4.2.5. Stateless EJB Populates Propagated Persistence Context with Rooms ..... 26

Page 4: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

JPA Server-Side Deployment

iv

4.2.6. Stateful EJB Method Activates Transaction and flush()es Guests in

EntityManager Cache ....................................................................................... 26

4.2.7. Stateful EJB uses pre-loaded Rooms and Guests without accessing DB

(until association) ............................................................................................. 27

4.3. Transaction Rollbacks ....................................................................................... 28

4.3.1. Stateless Transaction Rollback ............................................................... 28

4.3.2. Stateful Transaction Rollback .................................................................. 29

4.4. Pessamistic Locking .......................................................................................... 31

4.5. Summary .......................................................................................................... 32

Page 5: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

v

Purpose• Server-side Resources

• PersistenceUnit/PersistenceContext injection

• Managed Entities and Remote Interfaces

• Persistence Context Propagation

Page 6: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

vi

Page 7: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 1.

1

Server-side Resources

1.1. SQL DataSource (defined in Server -

standalone.xml)• Physical connections to database are placed into a pool

• Logical connections are borrowed and returned from/to the pool

<subsystem xmlns="urn:jboss:domain:datasources:1.0">

<datasources>

<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-

context="true">

<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>

<driver>h2</driver>

<security>

<user-name>sa</user-name>

<password></password>

</security>

</datasource>

<drivers>

<driver name="h2" module="com.h2database.h2">

<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>

</driver>

</drivers>

</datasources>

</subsystem>

1.2. Server-side Persistence Units• persistence.xml

• Two transaction types -- one is primary path

1.2.1. transaction-type=JTA (default)• No connection information - using pool from jta-data-source

• Properties included define how to work with DB -- not how to connect

• Likely the only transaction-type used

<?xml version="1.0" encoding="UTF-8"?>

<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_2_0.xsd" version="2.0">

<persistence-unit name="ejbjpa-hotel" transaction-type="JTA">

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

...

<properties>

<property name="hibernate.dialect" value="${hibernate.dialect}"/>

...

Page 8: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 1. Server-side Resources

2

</properties>

</persistence-unit>

</persistence>

1.2.2. transaction-type=RESOURCE_LOCAL• No connection pooling

• Propertites include connection information

• Requires bean-managed transactions

• @TransactionManagement(TransactionManagementType.BEAN)

• Used for obscure situations - likely rarely used

<?xml version="1.0" encoding="UTF-8"?>

<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_2_0.xsd" version="2.0">

<persistence-unit name="ejbjpa-hotel-rl" transaction-type="RESOURCE_LOCAL">

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

...

<properties>

<!-- connection properties -->

<property name="javax.persistence.jdbc.url" value="${jdbc.url}"/>

<property name="javax.persistence.jdbc.driver" value="${jdbc.driver}"/>

<property name="javax.persistence.jdbc.user" value="${jdbc.user}"/>

<property name="javax.persistence.jdbc.password" value="${jdbc.password}"/>

<property name="hibernate.dialect" value="${hibernate.dialect}"/>

...

</properties>

</persistence-unit>

</persistence>

Conform to JavaEE/EJB framework, don't bypass it

Use of transaction-type=RESOURCE_LOCAL bypasses a significant portion

of what the application server provides (e.g., Connection Resource Pooling,

Container Managed Transactions). This should only be used in limited cases and

where the implementation requires. It should not be used because of lack of

understanding of how to use the server-side JavaEE/EJB framework.

1.3. persistence.xml Placement

1.3.1. EJB persistence.xml Placement• Deployment

• META-INF/persistence.xml

• Same location as normal JAR

• Source

Page 9: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

WAR persistence.xml Placement

3

• src/main/resources/META-INF/persistence.xml

enc-config-example-ejb

|-- ejava

| `-- ejb

| `-- examples

| `-- encconfig

| |-- dto

| | `-- NCPair.class

| `-- ejb

| |-- AnnotatedEJB.class

| |-- InjectedEJB.class

| |-- JNDIReader.class

| |-- JNDIReaderRemote.class

| `-- XMLConfiguredEJB.class

`-- META-INF

`-- persistence.xml

1.3.2. WAR persistence.xml Placement• Deployment

• WEB-INF/classes/META-INF/persistence.xml

• Inside JARs within WEB-INF/lib also allowed

• Source

• src/main/resources/META-INF/persistence.xml

ejb-jpa-example-war

|-- META-INF

`-- WEB-INF

|-- classes

| |-- info

| | `-- ejava

| | `-- examples

| | `-- ejb

| | `-- ejbjpa

| | |-- dto

| | | |-- FloorDTO.class

| | | `-- RoomDTO.class

| | `-- ejb

| | |-- HotelInitEJB.class

| | |-- HotelInitRemote.class

| | |-- HotelMgmtEJB.class

| | |-- HotelMgmtLocal.class

| | |-- HotelMgmtRemote.class

| | |-- ReservationEJB.class

| | `-- ReservationRemote.class

| `-- META-INF

| |-- ejb-jar.xml

| |-- jboss-ejb3.xml

| `-- persistence.xml

`-- lib

Page 10: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 1. Server-side Resources

4

`-- ejb-jpa-example-blimpl-4.0.0-SNAPSHOT.jar

1.4. Reference External @Entities• to have persistence.xml reference @Entity class(es) outside of local archive

1.4.1. Reference External @Entities: EAR Deploy• jar-file element able to reference external JAR using a deterministic, portable path in EAR

ejbsessionBankEAR-4.0.0-SNAPSHOT

|-- ejbsessionBankEJB.jar

|-- ejbsessionBankWAR-4.0.0-SNAPSHOT.war

|-- lib

| |-- ejava-util-4.0.0-SNAPSHOT.jar

| `-- ejbsessionBankImpl-4.0.0-SNAPSHOT.jar

`-- META-INF

`-- application.xml

<?xml version="1.0" encoding="UTF-8"?>

<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_2_0.xsd" version="2.0">

<persistence-unit name="ejbsessionbank">

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

<jar-file>lib/ejbsessionBankImpl-4.0.0-SNAPSHOT.jar</jar-file>

<properties>

<property name="hibernate.dialect" value="${hibernate.dialect}"/>

...

</properties>

</persistence-unit>

</persistence>

1.4.2. Reference External @Entities: WAR Deploy• Can automatically locate Entities within WEB-INF classes

• Cannot automatically locate Entities within WEB-INF/lib JARs

• jar-file element not usable in WARs

ejb-jpa-example-war

|-- META-INF

`-- WEB-INF

...

`-- lib

`-- ejb-jpa-example-blimpl-5.0.0-SNAPSHOT.jar

Page 11: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Summary

5

<?xml version="1.0" encoding="UTF-8"?>

<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_2_0.xsd" version="2.0">

<persistence-unit name="ejbjpa-hotel">

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

<!-- located in WEB-INF/lib/ejb-jpa-example-blimpl-${project.version}.jar -->

<class>info.ejava.examples.ejb.ejbjpa.bo.Guest</class>

<class>info.ejava.examples.ejb.ejbjpa.bo.Room</class>

<class>info.ejava.examples.ejb.ejbjpa.bo.Floor</class>

<properties>

<property name="hibernate.dialect" value="${hibernate.dialect}"/>

...

</properties>

</persistence-unit>

</persistence>

Note

There is no need to declare "class" entity references into a properly configured

"jar-file" that is located in the classpath of a deployed EAR. Use "class" entity

references when using a WEB-INF/classes/META-INF deployment of a WAR and

classes come from externally provided JAR -- or if you really need to only include

specific entities in the archive.

1.5. Summary• Replace JDBC ConnectionManager properties with sharable DataSource reference

• Direct use of JDBC ConnectionManager is allowed but not advised

• Can replace entity class enumerations with jar-file reference for EAR deploy

Page 12: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

6

Page 13: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 2.

7

Persistence Unit/Context Injection@PersistenceContext

Injected with EntityManager

Transaction Scoped (default)

• persistence context only sees a single Tx

• container injects EntityManager with Tx active

@PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.TRANSACTION)

private EntityManager em;

Extended Scope

• persistence context may see multiple Tx

• only relevant for Stateful EJBs

@PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.EXTENDED)

private EntityManager em;

@PersistenceUnit

Injected with EntityManagerFactory

• May be used to implement BEAN-managed transactions

2.1. @PersistenceContext Injection• @PersistenceContext.unitName is the name used within the persistence.xml

• @PersistenceContext.name would be the ENC name normally defined in ejb-jar.xml

@Stateless

public class HotelMgmtEJB implements HotelMgmtRemote, HotelMgmtLocal {

@PersistenceContext(unitName="ejbjpa-hotel")

private EntityManager em;

private HotelDAO dao;

private HotelMgmt hotelMgmt;

@PostConstruct

public void init() {

dao = new JPAHotelDAO();

((JPAHotelDAO)dao).setEntityManager(em);

hotelMgmt = new HotelMgmtImpl();

((HotelMgmtImpl)hotelMgmt).setHotelDao(dao);

}

• @PersistenceContext.synchronization

• SynchronizationType.SYNCHRONIZED (default) -- EntityManager automatically joined with

jtaTransaction

Page 14: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 2. Persistence Unit/C...

8

• SynchronizationType.UNSYNCHRONIZED -- EntityManager manually joined with

jtaTransaction. Useful for Stateful Session EJBs where interaction with EntityManager may

span multiple client method calls.

• @PersistenceContext.type

• PersistenceContextType.TRANSACTION (default) - EntityManager transactions

automatically managed by container and will invoke each root method once per transaction

• PersistenceContextType.EXTENDED - EntityManager transactions may span multiple calls.

Useful for Stateful Session EJBs to retain state during a complete session

2.2. @PersistenceUnit Injection• Persistence unit (EntityManagerFactory) being injected from a JTA-managed source

• i.e., the transaction must be managed at JTA level

• BEAN-managed transactions means JTA transaction controlled thru injected UserTransaction

• Method programmatically controlling scope of JTA transaction

• em.joinTransaction() called on EntityManager created outside scope of JTA transaction

@Singleton

@Startup

@TransactionManagement(TransactionManagementType.BEAN)

public class HotelInitEJB implements HotelInitRemote {

@PersistenceUnit(unitName="ejbjpa-hotel")

private EntityManagerFactory emf;

@Resource

private UserTransaction tx;

@Override

public void businessMethod() {

EntityManager em=emf.createEntityManager();

HotelDAO dao = new JPAHotelDAO();

((JPAHotelDAO)dao).setEntityManager(em);

tx.begin();

em.joinTransaction(); //tells the EM to join the JTA Tx

...

tx.commit();

em.close();

}

}

2.3. Context and Dependency Injection (CDI)

Newer technique -- JavaEE's answer to Spring Configuration

• Define qualifier annotation

package ejava.examples.jndidemo;

import java.lang.annotation.Retention;

import java.lang.annotation.Target;

import static java.lang.annotation.RetentionPolicy.*;

import static java.lang.annotation.ElementType.*;

Page 15: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Summary

9

import javax.inject.Qualifier;

@Qualifier

@Retention(RUNTIME)

@Target({METHOD, FIELD, PARAMETER, TYPE})

public @interface JndiDemo {

}

• Define producer of Persistence Context

import javax.enterprise.inject.Produces;

import javax.persistence.EntityManager;

import javax.persistence.PersistenceContext;

public class SchedulerResources {

@PersistenceContext(unitName="jndidemo")

@Produces

@JndiDemo

public EntityManager em;

• Define injection point

@Stateless

public class TrainSchedulerEJB

extends SchedulerBase implements TrainSchedulerRemote {

@Inject @JndiDemo

private EntityManager em;

Qualifier not always necessary

The Qualifier annotation is only necessary when the deployed application contains

multiple producers of an EntityManager.

2.4. Summary• @PersistenceContext - EntityManager

• @PersistenceUnit - EntityManagerFactory

• peek at CDI injection

Page 16: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

10

Page 17: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 3.

11

Managed Entities and Remote

Interfaces

3.1. Problem: Provider Proxy Classes Marshaled to

Client• Problem occurs when managed @Entity classes used as DTOs

• Provider places proxy classes on managed entities to watch for changes

• Provider classes can get marshaled back to client

• Client encounters ClassNotFoundException when deserializing RMI object with provider class

javax.ejb.EJBException: java.lang.ClassNotFoundException:

org.hibernate.proxy.pojo.javassist.SerializableProxy

3.1.1. Potential Solution: Add JPA Provider Classes to Client

Classpath• Client must add hibernate-core in classpath to avoid ClassNotFoundException

<!-- used if hibernate entities re-used as DTOs -->

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-core</artifactId>

<scope>test</scope>

</dependency>

3.1.2. More Scenario Details

3.1.2.1. Example: @Entity Class Returned to Client

@Entity

public class Room implements Serializable {

@Id

@Column(name="ROOM_NUMBER")

private int number;

@ManyToOne(optional=false, fetch=FetchType.LAZY)

@JoinColumn(name="FLOOR_ID")

private Floor floor;

@OneToOne(optional=true, fetch=FetchType.LAZY)

@JoinColumn(name="OCCUPANT_ID")

private Guest occupant;

• Room has mandatory reference to Floor and optional reference to Guest

Page 18: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 3. Managed Entities a...

12

• fetch=LAZY references most likely will be proxies implemented by JPA provider classes

3.1.2.2. Example: EJB @Remote Method Returns Query Result

Directly to Client

@Override

public List<Room> getAvailableRooms(Integer level, int offset, int limit) {

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Room> qdef = cb.createQuery(Room.class);

Root<Room> r = qdef.from(Room.class);

...

em.createQuery(qdef)

.setFirstResult(offset);

.setMaxResults(limit);

.getResultList();

}

• Room entities returned from query are passed to client

3.1.2.3. Example: Provider Proxy Classes Marshaled to Client

72 List<Room> rooms = hotelMgmt.getAvailableRooms(null, 0, 1);

75 Room room = rooms.get(0);

77 logger.info("what floor class is this??? {}", room.getFloor().getClass());

78 assertFalse("floor was not proxy", room.getFloor().getClass().equals(Floor.class));

80 Guest guest = new Guest("Cosmo Kramer");

81 guest = hotelMgmt.checkIn(guest, room);

82 logger.info("final guest: {}:{}", guest.getClass(), guest);

HotelMgmtEJBIT:77 - what floor class is this???

class info.ejava.examples.ejb.ejbjpa.bo.Floor_$$_jvst9a8_0

HotelMgmtEJBIT:82 - final guest:

class info.ejava.examples.ejb.ejbjpa.bo.Guest:Guest [id=11, name=Cosmo Kramer]

• Room returned with proxy class (Floor_$$_jvst9a8_0)between Room and Floor

• Requires client to have hibernate-core in classpath

3.1.3. Candidate Solution: Cleansed DTOs• Allows @Entity classes to be used as pure POJOs -- these have never been managed

• Contains no provider proxy classes

• No requirement to have client update classpath

3.1.3.1. Example: EJB Copies Managed @Entity to new Instance

@Override

public List<Room> getCleanAvailableRooms(Integer level, int offset, int limit) {

List<Room> rooms = getAvailableRooms(level, offset, limit);

return toClean(rooms);

Page 19: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Problem: Lazy Load Exception

13

}

/**

* This helper method will instantiate new entity classes to re-use as DTOs.

* This is done to remove hibernate-proxy classes that are part of the managed

* entity.

*/

private List<Room> toClean(List<Room> rooms) {

if (rooms==null) { return null; }

List<Room> cleanRooms = new ArrayList<Room>(rooms.size());

for (Room room : rooms) {

Floor floor = room.getFloor();

Floor cleanFloor = new Floor(floor.getLevel());

Room cleanRoom = new Room(cleanFloor, room.getNumber());

cleanFloor.withRoom(cleanRoom);

Guest occupant = room.getOccupant();

if (occupant!=null) {

Guest cleanOccupant = new Guest(occupant.getId());

cleanOccupant.setName(occupant.getName());

cleanRoom.setOccupant(cleanOccupant);

}

cleanRooms.add(cleanRoom);

}

return cleanRooms;

}

• EJB Remote facade creating new instances of @Entity classes

3.1.3.2. Example: Client Receives Pure POJOs without Provider

Classes

this looks like a good floor: class info.ejava.examples.ejb.ejbjpa.bo.Floor

final guest: class info.ejava.examples.ejb.ejbjpa.bo.Guest:Guest [id=17, name=Cosmo Kramer]

• Client now gets Room.floor without provider proxy class in between

Package "Cleansing" methods in separate Helper Class

The "cleansing" method was shown as a helper method for simplicity. It is advised

to encapsulate that within a separate helper class so that it is easily used by other

Remote Facades that must cleans their returned objects.

3.2. Problem: Lazy Load Exception• Caller attempts to access an Entity property that has not yet been loaded from the database

• Provider unable to fullfil that request

• DB session closed before necessary references resolved

• Common when using @Entities across Persistence Unit boundaries

• Not unique to RMI

Page 20: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 3. Managed Entities a...

14

3.2.1. Lazy Load Scenario Details

3.2.1.1. Floor @Entity

@Entity

public class Floor implements Serializable {

@Id

@Column(name="LEVEL", nullable=false)

int level;

@OneToMany(mappedBy="floor",

fetch=FetchType.LAZY,

cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH},

orphanRemoval=true)

@OrderBy("number")

List<Room> rooms;

• Rooms fetch=LAZY

• Rooms must be fetched within same DB session when using that collection

3.2.1.2. Standard @Entity Access

@Stateless

public class HotelMgmtEJB implements HotelMgmtRemote, HotelMgmtLocal {

...

@Override

public Floor getFloor(int level) {

return em.find(Floor.class, level);

}

• Floor being marshaled directly back to client without addressing LAZY fetches

3.2.1.3. Remote Client Causing LAZY-load Exception

112 Floor floor = hotelMgmt.getFloor(0);

113 assertNotNull("floor not found", floor);

114 try {

115 logger.info("foor has {} rooms", floor.getRooms().size());

116 fail("did not get lazy-load exception");

117 } catch (LazyInitializationException expected) {

118 logger.info("got expected exception:{}", expected.toString());

119 }

HotelMgmtEJBIT:118 - got expected exception:org.hibernate.LazyInitializationException:

failed to lazily initialize a collection of role: info.ejava.examples.ejb.ejbjpa.bo.Floor.rooms,

could not initialize proxy - no Session

• Floor can be accessed

• Floor.room access causes Lazy-load exception of collection

Page 21: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Candidate Solution: Load thru "Touching" Object Tree in Remote Facade

15

3.2.2. Candidate Solution: Load thru "Touching" Object Tree in

Remote Facade• Must be done in location where Persistence Unit still accessible

• Simple/brute force technique to stimulate resolution of references

• Where do you stop???

• Repetitive round trips to DB can be expensive

3.2.2.1. Server-side Stimulates References To Be Loaded

@Override

public Floor getTouchedFloor(int level) {

Floor floor = getFloor(level);

if (floor!=null) {

//touch the managed-floor to cause lazy-loads to be resolved

floor.getRooms().isEmpty();

for (Room room: floor.getRooms()) {

Guest guest = room.getOccupant();

if (guest!=null) {

guest.getName(); //touch all occupants to cause lazy-loads to be resolved

}

}

}

return floor;

}

• Server-side code, within the DB session boundary stimulates references to be loaded prior to

marshaling back to client

3.2.2.2. Initial getFloor() queries parent FLOOR table

Floor floor = getFloor(level);

select

floor0_.LEVEL as LEVEL1_0_0_

from

EJBJPA_FLOOR floor0_

where

floor0_.LEVEL=?

3.2.2.3. Accessing Rooms Collection Causes Child Table Load

if (floor!=null) {

floor.getRooms().isEmpty();

select

rooms0_.FLOOR_ID as FLOOR_ID2_0_0_,

rooms0_.ROOM_NUMBER as ROOM_NUM1_2_0_,

rooms0_.ROOM_NUMBER as ROOM_NUM1_2_1_,

rooms0_.FLOOR_ID as FLOOR_ID2_2_1_,

Page 22: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 3. Managed Entities a...

16

rooms0_.occupant_GUEST_ID as occupant3_2_1_

from

EJBJPA_ROOM rooms0_

where

rooms0_.FLOOR_ID=?

order by

rooms0_.ROOM_NUMBER

3.2.2.4. Additional Child Table Load Occurring One-at-a-Time

for (Room room: floor.getRooms()) {

Guest guest = room.getOccupant();

if (guest!=null) {

guest.getName(); //touch all occupants to cause lazy-loads to be resolved

select

guest0_.GUEST_ID as GUEST_ID1_1_0_,

guest0_.name as name2_1_0_

from

EJBJPA_GUEST guest0_

where

guest0_.GUEST_ID=?

select

guest0_.GUEST_ID as GUEST_ID1_1_0_,

guest0_.name as name2_1_0_

from

EJBJPA_GUEST guest0_

where

guest0_.GUEST_ID=?

3.2.3. Candidate Solution: Load thru Fetching Object Tree in

Query• DAO queries crafted to support remote access patterns

• LAZY fetches resolved through "join fetch" queries

3.2.3.1. DAO Query Fetches Child Tables

@Override

public Floor getFetchedFloor(int level) {

List<Floor> floors = em.createNamedQuery("Floor.fetchFloor",

Floor.class)

.setParameter("level", level)

.getResultList();

return floors.isEmpty() ? null : floors.get(0);

}

@Entity

@NamedQueries({

@NamedQuery(name="Floor.fetchFloor",

query="select f from Floor f "

Page 23: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Candidate Solution: Abstract Remote Interface View with DTO

17

+ "join fetch f.rooms r "

+ "join fetch r.occupant "

+ "where f.level=:level")

})

public class Floor implements Serializable {

• Join fetch used to EAGER-ly load child rows

• Less trips to DB for fatch=LAZY mappings

3.2.3.2. Returned Object Tree Accessed in fewer Queries

select

floor0_.LEVEL as LEVEL1_0_0_,

rooms1_.ROOM_NUMBER as ROOM_NUM1_2_1_,

guest2_.GUEST_ID as GUEST_ID1_1_2_,

rooms1_.FLOOR_ID as FLOOR_ID2_2_1_,

rooms1_.occupant_GUEST_ID as occupant3_2_1_,

rooms1_.FLOOR_ID as FLOOR_ID2_0_0__,

rooms1_.ROOM_NUMBER as ROOM_NUM1_2_0__,

guest2_.name as name2_1_2_

from

EJBJPA_FLOOR floor0_

inner join

EJBJPA_ROOM rooms1_

on floor0_.LEVEL=rooms1_.FLOOR_ID

inner join

EJBJPA_GUEST guest2_

on rooms1_.occupant_GUEST_ID=guest2_.GUEST_ID

where

floor0_.LEVEL=?

order by

rooms1_.ROOM_NUMBER

• Still have to know "when is enough -- enough"

3.2.4. Candidate Solution: Abstract Remote Interface View with

DTO• Server-side has job to implement overall capability

• Client may have an "outsider" role

3.2.4.1. Room @Entity references Sensitive Occupant Information

@Entity

public class Room implements Serializable {

@Id

@Column(name="ROOM_NUMBER")

private int number;

...

@OneToOne(optional=true, fetch=FetchType.LAZY)

@JoinColumn(name="OCCUPANT_ID")

private Guest occupant;

Page 24: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 3. Managed Entities a...

18

• More information than the client wants/needs

• More information than what client should have

• But that is how out server-side model is designed...

3.2.4.2. @Entity Model mis-used by Client

//lets see if we can manually find a vacant room.....

Floor floor = hotelMgmt.getFetchedFloor(0);

//all floors have at least one occupant

for (Room room: floor.getRooms()) {

Guest occupant = room.getOccupant();

if (occupant!=null) {

logger.info("hey {}, are you done with room {} yet?",

occupant.getName(), room.getNumber());

//that is just rude

}

}

hey guest 1, are you done with room 0 yet?

hey guest 3, are you done with room 2 yet?

• Client only needed to know if room was occupied -- not by who

3.2.4.3. DTO Represents what Clients Can/Should Know

public class RoomDTO implements Serializable {

private int number;

private boolean occupied;

• Room DTO class is only expressing that room is occupied

3.2.4.4. Server-side constructs DTOs

@Override

public FloorDTO getFetchedFloorDTO(int level) {

Floor floor = getFetchedFloor(level);

return toDTO(floor);

}

private FloorDTO toDTO(Floor floor) {

if (floor==null) { return null; }

FloorDTO floorDTO = new FloorDTO(floor.getLevel());

if (floor.getRooms()!=null) { for (Room room: floor.getRooms()) {

floorDTO.withRoom(toDTO(room));

}}

return floorDTO;

}

private RoomDTO toDTO(Room room) {

if (room==null) { return null; }

RoomDTO roomDTO = new RoomDTO(room.getNumber());

//remote client shouldn't care who is in the room -- just if busy

Page 25: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Summary

19

roomDTO.setOccupied(room.getOccupant()!=null);

return roomDTO;

}

• Similar to @Entity cleansing since DTO classes aren't managed

• Can indirectly solve LAZY-load issue because @Entity is walked on server-side

• Must still pay attention to DB access for performance reasons

3.2.4.5. Client receives more Appropriate Abstraction

//lets see if we can manually find a vacant room.....

FloorDTO floor = hotelMgmt.getFetchedFloorDTO(0);

//all floors have at least one occupant

for (RoomDTO room: floor.getRooms()) {

if (room.isOccupied()) {

logger.info("hey whoever, are you done with room {} yet?", room.getNumber());

//still rude, but a bit more private

}

}

hey whoever, are you done with room 0 yet?

hey whoever, are you done with room 2 yet?

• Client no longer has access to who is in each room -- but server-side does

Package "toDTO" methods in separate Helper Class

The "toDTO" method was shown as a helper method for simplicity. It is advised to

encapsulate that within a separate helper class so that it is easily used by other

Remote Facades that must translate an Entity class to a DTO. Note that the reverse

is also common -- to have DTOs converted to Entity POJOs for incoming objects.

3.3. Summary• Serializing Managed @Entities

• Expand client classpath dependencies

• "Cleansing" Managed @Entities into pure POJOs

• Lazy-load Exception

• "Touching" @Entities to Stimulate Child Load

• Join Fetch to provide query support for detaching @Entities

• DTO classes

Page 26: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

20

Page 27: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4.

21

Persistence Context Propagation

Figure 4.1. Persistence and Transaction Context Propagation

• EJB will instantiate persistence context if does not yet exist

• Stateless EJBs may only have transaction-scope persistence contexts

• Stateful EJBs may have transaction-scope or extended persistence contexts

• EJBs can share persistence contexts

• Stateless EJB can propagate its tx-scope persistence context to a called EJB

• Stateful EJB can propagate its tx-scoped or extended persistence context to a called EJB

• Stateless EJB can work with extended persistence context provided by upstream Stateful

client

• Stateful EJB cannot transform propagated tx-scope persistence context into an extended

• EJB Facade can act as sharing point for common persistence context

4.1. Stateless Persistence Context Interaction• Each interaction is independent of the next except for what is stored in DB and at client

4.1.1. Stateless EJB Example Check-in

@Override

public Guest checkIn(Guest guest, Room room) throws RoomUnavailableExcepton {

Room hotelRoom = dao.getRoom(room.getNumber());

dao.addGuest(guest);

hotelRoom.setOccupant(guest);

return guest;

}

• (Error checking removed)

• Locate specific room

• Add guest to DB

• Associate room with guest

Page 28: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

22

4.1.2. Stateless EJB Example Client Check-in

List<Room> availableRooms = hotelMgmt.getAvailableRooms(null, 0, 0);

logger.debug("we have {} available rooms", availableRooms.size());

List<Guest> members = new ArrayList<Guest>(availableRooms.size());

int i=0;

for (Room room: availableRooms) {

Guest member = new Guest("member " + i++);

member = hotelMgmt.checkIn(member, room);

members.add(member);

}

• Client performs actions one at a time

4.1.3. EJB Gets Available Rooms from DB

client: hotelMgmt.getAvailableRooms(null, 0, 0);

[HotelMgmtEJB] *** HotelMgmtEJB(1543684701):init ***

[stdout] Hibernate:

[stdout] select

[stdout] room0_.ROOM_NUMBER as ROOM_NUM1_2_,

[stdout] room0_.FLOOR_ID as FLOOR_ID2_2_,

[stdout] room0_.OCCUPANT_ID as OCCUPANT3_2_

[stdout] from

[stdout] EJBJPA_ROOM room0_

[stdout] where

[stdout] room0_.OCCUPANT_ID is null

[stdout] order by

[stdout] room0_.ROOM_NUMBER asc

[HotelMgmtEJB] *** HotelMgmtEJB(1543684701):destroy ***

• Persistence context created

• Available rooms loaded into persistence context

• Result is returned

• Persistence context destroyed

4.1.4. EJB Gets Specific Room

Room hotelRoom = dao.getRoom(room.getNumber());

[HotelMgmtEJB] *** HotelMgmtEJB(613346935):init ***

[HotelMgmtEJB] checkin(guest=Guest [id=0, name=member 0], room=Room [number=1, occupant=null])

[stdout] Hibernate:

[stdout] select

[stdout] room0_.ROOM_NUMBER as ROOM_NUM1_2_0_,

[stdout] room0_.FLOOR_ID as FLOOR_ID2_2_0_,

[stdout] room0_.OCCUPANT_ID as OCCUPANT3_2_0_

[stdout] from

[stdout] EJBJPA_ROOM room0_

Page 29: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

EJB Adds Guest

23

[stdout] where

[stdout] room0_.ROOM_NUMBER=?

• Persistence context created

• Specific room loaded into persistence context

• Guest inserted into DB

Main Example Point: Rooms not Cached between Stateless

calls

This listing provides the main take-away from the stateless solution. The hotel

room(s) must be queried for from the database each time they are accessed from

the database.

1. All rooms were queried for when looking for avaiable rooms

2. Individual room was queried for when making reservation

4.1.5. EJB Adds Guest

dao.addGuest(guest);

[stdout] Hibernate:

[stdout] call next value for hibernate_sequence

[HotelMgmtEJB] *** HotelMgmtEJB(613346935):destroy ***

[stdout] Hibernate:

[stdout] insert

[stdout] into

[stdout] EJBJPA_GUEST

[stdout] (name, GUEST_ID)

[stdout] values

[stdout] (?, ?)

• Guest inserted into DB

Note

The transaction does not end until exiting the Stateless EJB method. That means

any DB constraint violations will not occur within the context of the EJB call that

caused it unless you call em.flush() (as a form of debug) prior to exiting the business

method.

4.1.6. EJB associates Guest with Room

hotelRoom.setOccupant(guest);

[stdout] Hibernate:

[stdout] update

[stdout] EJBJPA_ROOM

[stdout] set

[stdout] FLOOR_ID=?,

Page 30: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

24

[stdout] OCCUPANT_ID=?

[stdout] where

[stdout] ROOM_NUMBER=?

• Room.occupant foreign key updated

• Guest returned to client

• Persistence context destroyed

4.2. Stateful Facade Persistence Context Interaction• State can be cached in-memory on server-side

• Facade EJB propagates persistence context to called EJBs

4.2.1. Example Stateful Reservation EJB Caches Guest

Requests for Client

@Stateful

public class ReservationEJB implements ReservationRemote {

@PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.EXTENDED)

private EntityManager em;

List<Guest> guests = new LinkedList<Guest>();

@Override

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)

public int addGuest(Guest guest) {

logger.debug("addGuest={}", guest);

if (guest!=null) {

guests.add(guest);

em.persist(guest); //<== no transaction active yet

}

return guests.size();

}

• Stateful EJB injects @PersistenceContext -- propagated in downstream EJB calls

• Extended persistence context -- may be associated with zero or more transactions

• Performing in-memory actions outside of transaction boundary

4.2.2. Example Stateful Reservation EJB Acting on Cached

State

@Override

@TransactionAttribute(TransactionAttributeType.REQUIRED)

@Remove

public List<Guest> reserveRooms() throws RoomUnavailableExcepton {

List<Room> rooms = hotelMgmt.getAvailableRooms(null, 0, guests.size());

//assign each one of them a room

em.flush(); //<== flush guests persisted outside of a TX for demonstration

List<Guest> completed = new ArrayList<Guest>(guests.size());

Iterator<Room> roomItr = rooms.iterator();

Page 31: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Stateful EJB Example Client Check-in

25

for (Guest guest: guests) {

Room room = roomItr.next();

try {

guest = hotelMgmt.checkIn(guest, room); //<== will attempt to also persist guest

completed.add(guest);

} catch (RoomUnavailableExcepton ex) {

//rollback any previous reservations

ctx.setRollbackOnly();

throw ex;

}

}

return completed;

}

• Method executed within active JTA transaction with persistence context associated with

transaction

• Guests already managed but will be fully persisted in this method

• Calls to Stateless HotelMgmtEJB executed on this EJB's persistence context

• @Remove indicates stateful instance to be discarded after method called

4.2.3. Stateful EJB Example Client Check-in

int availableRooms = hotelMgmt.getAvailableRooms(null, 0, 0).size();

logger.debug("we have {} available rooms", availableRooms);

ReservationRemote checkin = (ReservationRemote) jndi.lookup(reservationJNDI);

for (int i=0; i<availableRooms; i++) {

Guest member = new Guest("member " + i);

int count=checkin.addGuest(member); //this only modifies the in-memory persistence unit

logger.debug("we have {} in our group so far", count);

}

List<Guest> guests = checkin.reserveRooms(); //this is where the DB work gets committed

• Multiple requests are issued to Stateful EJB

• Specific method(s) act on that state

4.2.4. Stateful EJB Persists Guests Prior to Active JTA

Transaction

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)

...

em.persist(guest); //<== no transaction active yet

[ReservationEJB] *** ReservationEJB(12230192):init ***

[ReservationEJB] addGuest=Guest [id=0, name=member 0]

[stdout] Hibernate:

[stdout] call next value for hibernate_sequence

[ReservationEJB] addGuest=Guest [id=0, name=member 1]

[stdout] Hibernate:

[stdout] call next value for hibernate_sequence

[ReservationEJB] addGuest=Guest [id=0, name=member 2]

Page 32: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

26

[stdout] Hibernate:

[stdout] call next value for hibernate_sequence

[ReservationEJB] addGuest=Guest [id=0, name=member 3]

[stdout] Hibernate:

[stdout] call next value for hibernate_sequence

• em.persist() outside of transaction causing causing sequence call but no table inserts

4.2.5. Stateless EJB Populates Propagated Persistence Context

with Rooms

List<Room> rooms = hotelMgmt.getAvailableRooms(null, 0, guests.size());

[HotelMgmtEJB] *** HotelMgmtEJB(1162892707):init ***

[stdout] Hibernate:

[stdout] select

[stdout] room0_.ROOM_NUMBER as ROOM_NUM1_2_,

[stdout] room0_.FLOOR_ID as FLOOR_ID2_2_,

[stdout] room0_.OCCUPANT_ID as OCCUPANT3_2_

[stdout] from

[stdout] EJBJPA_ROOM room0_

[stdout] where

[stdout] room0_.OCCUPANT_ID is null

[stdout] order by

[stdout] room0_.ROOM_NUMBER asc limit ?

[HotelMgmtEJB] *** HotelMgmtEJB(1162892707):destroy ***

• Downstream Stateless HotelMgmtEJB queried for rooms

• Rooms loaded into persistence context

4.2.6. Stateful EJB Method Activates Transaction and flush()es

Guests in EntityManager Cache

@TransactionAttribute(TransactionAttributeType.REQUIRED)

...

logger.debug("reserving {} rooms for {} guests", rooms.size(), guests.size());

em.flush(); //<== flush guests persisted outside of a TX for demonstration

[ReservationEJB] reserving 4 rooms for 4 guests

[stdout] Hibernate:

[stdout] insert

[stdout] into

[stdout] EJBJPA_GUEST

[stdout] (name, GUEST_ID)

[stdout] values

[stdout] (?, ?)

• SQL inserts issued to DB during flush (requires transaction active)

Page 33: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Stateful EJB uses pre-loaded Rooms and Guests without accessing DB (until association)

27

4.2.7. Stateful EJB uses pre-loaded Rooms and Guests without

accessing DB (until association)

guest = hotelMgmt.checkIn(guest, room); //<== will attempt to also persist guest

[HotelMgmtEJB] *** HotelMgmtEJB(1086999670):init ***

[HotelMgmtEJB] checkin(guest=Guest [id=65, name=member 0], room=Room [number=1, occupant=null])

[HotelMgmtEJB] *** HotelMgmtEJB(1086999670):destroy ***

[HotelMgmtEJB] *** HotelMgmtEJB(99312186):init ***

[HotelMgmtEJB] checkin(guest=Guest [id=66, name=member 1], room=Room [number=100, occupant=null])

[HotelMgmtEJB] *** HotelMgmtEJB(99312186):destroy ***

[HotelMgmtEJB] *** HotelMgmtEJB(545116383):init ***

[HotelMgmtEJB] checkin(guest=Guest [id=67, name=member 2], room=Room [number=102, occupant=null])

[HotelMgmtEJB] *** HotelMgmtEJB(545116383):destroy ***

[HotelMgmtEJB] *** HotelMgmtEJB(605810979):init ***

[HotelMgmtEJB] checkin(guest=Guest [id=68, name=member 3], room=Room [number=201, occupant=null])

[HotelMgmtEJB] *** HotelMgmtEJB(605810979):destroy ***

[stdout] Hibernate:

[stdout] update

[stdout] EJBJPA_ROOM

[stdout] set

[stdout] FLOOR_ID=?,

[stdout] OCCUPANT_ID=?

[stdout] where

[stdout] ROOM_NUMBER=?

[stdout] Hibernate:

...

[ReservationEJB] *** ReservationEJB(12230192):destroy ***

• Stateless HotelMgmtEJB accesses Rooms from EntityManager first-level cache -- no additional

DB access

• Stateless HotelMgmtEJB updates Room.occupant FK to reference Guest -- who is also already

managed and in first-level cache

• JTA Transaction committed by container after Stateful EJB method exits

• Stateful EJB and persistence context also destroyed after method exits

Main Example Point: Rooms accessed from Cache from

Stateful caller

This listing provides the main take-away from the Stateful solution. The hotel

room(s) are queried from up front, maintained in the EXTENDED_CONTEXT

Persistence Context within the Stateful Session EJB, and accessed from that

cache by the called Stateless Session EJB. This is the same Stateless Session

EJB that accessed the DB each time when accessed without a caller cache.

1. All rooms were queried for when looking for avaiable rooms

2. Individual room accessed from cache when making reservation

Page 34: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

28

4.3. Transaction Rollbacks• Part of transaction ACID properties

Atomic

All or nothing

Consistent

Valid state

Isolation

Visibility of incomplete changes

Durable

Not forgotten once committed

Figure 4.2. Stateless EJB will Reject New Check-in when Occupied

@Override

public Guest checkIn(Guest guest, Room room) throws RoomUnavailableExcepton {

Room hotelRoom = dao.getRoom(room.getNumber());

if (hotelRoom==null) {

throw new RoomUnavailableExcepton(String.format("room [%d] does not exist", room.getNumber()));

}

if (hotelRoom.getOccupant()!=null) {

throw new RoomUnavailableExcepton(String.format("room is occupied by %s", hotelRoom.getOccupant()));

}

dao.addGuest(guest);

hotelRoom.setOccupant(guest);

return guest;

}

• Rejects checkin when invalid

• Completes check-in when valid

4.3.1. Stateless Transaction Rollback• Transaction per call, no rolling back previous calls

Figure 4.3. Client uses up all Available Rooms

List<Guest> members = new ArrayList<Guest>(availableRooms.size());

int i=0;

for (Room room: availableRooms) {

Guest member = new Guest("member " + i++);

member = hotelMgmt.checkIn(member, room);

members.add(member);

}

• Stateless EJB commits each of these check-ins

Page 35: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Stateful Transaction Rollback

29

Figure 4.4. Client Attempts an Invalid Check-in

//try doing it again

Room room = availableRooms.get(0);

Guest member = new Guest("member " + i++);

try {

member = hotelMgmt.checkIn(member, room);

members.add(member);

fail("fail to detect bad checkin");

} catch (RoomUnavailableExcepton ex) {

logger.debug("expected exception making too many reservations:{}", ex.toString());

}

expected exception making too many reservations:info.ejava.examples.ejb.ejbjpa.bl.RoomUnavailableExcepton:

room is occupied by Guest [id=103, name=member 0]

• Additional check-in rejected -- nothing committed

Figure 4.5. Server still has Initial Committed Check-ins

logger.info("completed reservations for {} guests", members.size());

int availableRooms2 = hotelMgmt.getAvailableRooms(null, 0, 0).size();

logger.info("hotel has {} rooms available", availableRooms2);

assertEquals("", availableRooms.size()-members.size(), availableRooms2);

completed reservations for 4 guests

hotel has 0 rooms available

• Each check-in occured in own transaction

• Later error did not impact previous completed transactions

4.3.2. Stateful Transaction Rollback• Multiple resource actions part of same transaction

• All-or-none performed

Figure 4.6. Client Issues Guests Up Front

ReservationRemote checkin = (ReservationRemote) jndi.lookup(reservationJNDI);

for (int i=0; i<availableRooms+1; i++) {

Guest member = new Guest("member " + i);

int count=checkin.addGuest(member);

logger.debug("we have {} in our group so far", count);

}

00:47:20 DEBUG HotelMgmtEJBIT:326 - we have 4 available rooms

00:47:20 DEBUG HotelMgmtEJBIT:332 - we have 1 in our group so far

00:47:20 DEBUG HotelMgmtEJBIT:332 - we have 2 in our group so far

00:47:20 DEBUG HotelMgmtEJBIT:332 - we have 3 in our group so far

Page 36: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

30

00:47:20 DEBUG HotelMgmtEJBIT:332 - we have 4 in our group so far

00:47:20 DEBUG HotelMgmtEJBIT:332 - we have 5 in our group so far

• Same stateful process as before -- but with one additional Guest (one too many)

Figure 4.7. Client Attempts to Check-in all Guests at Once

try {

checkin.reserveRooms();

fail("too many check-ins not detected");

} catch (RoomUnavailableExcepton ex) {

logger.debug("expected exception making too many reservations:{}", ex.toString());

}

• Client attempts to check-in to all Rooms

• Checked exception will be thrown

Figure 4.8. Stateful EJB Attempts to flush() Guests and Reserve all Rooms

@TransactionAttribute(TransactionAttributeType.REQUIRED)

...

em.flush(); //<== flush guests persisted outside of a TX for demonstration

[ReservationEJB] reserving 4 rooms for 5 guests

[stdout] Hibernate:

[stdout] insert

[stdout] into

[stdout] EJBJPA_GUEST

[stdout] (name, GUEST_ID)

[stdout] values

[stdout] (?, ?)

...

• Five (5) Guests are flushed to database prior to the rollback

Figure 4.9. Stateful EJB rolls back Transaction on Stateless HotelMgmtEJB

Exception

@Resource

private SessionContext ctx;

try {

guest = hotelMgmt.checkIn(guest, room); //<== will attempt to also persist guest

completed.add(guest);

} catch (RoomUnavailableExcepton ex) {

//rollback any previous reservations

ctx.setRollbackOnly();

throw ex;

}

[ReservationEJB] *** ReservationEJB(271512057):destroy ***

Page 37: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Pessamistic Locking

31

expected exception making too many reservations:info.ejava.examples.ejb.ejbjpa.bl.RoomUnavailableExcepton:

found on 4 out of 5 required

• Persisted Guests removed from database as a part of transaction rollback

• Early check-ins never flushed to DB -- discarded as part of rollback

Figure 4.10. Client Checks and finds Rooms Still Available -- all Check-ins

Rolled Back

int availableRooms2 = hotelMgmt.getAvailableRooms(null, 0, 0).size();

logger.info("hotel has {} rooms available", availableRooms2);

assertEquals("unexpected room count", availableRooms, availableRooms2);

hotel has 4 rooms available

• All check-ins associated with Rooms removed as a part of rollback

4.4. Pessamistic Locking• One concurrency strategy

• Assume failure and take steps to prevent

• Create/wait-for a lock on data up-front, prior to getting the data

• More expensive when failure assumption over estimated

• Opposite of Optimistic Locking concurrency strategy

• Assume success and account for failure

• Attempt action and detect if action not performed

• More efficient when failures do not occur

• Some failures can be delegated back to client for resolution

Figure 4.11. Stateful Scenario Ontaining Pessamistic Lock with Rooms

@Override

@TransactionAttribute(TransactionAttributeType.REQUIRED)

@Remove

public List<Guest> reserveRoomsPessimistic() throws RoomUnavailableExcepton {

List<Room> rooms = em.createQuery(...)

.setFirstResult(0)

.setMaxResults(guests.size())

.setLockMode(LockModeType.PESSIMISTIC_WRITE)

//can also be set globally as persistence.xml property

.setHint("javax.persistence.lock.timeout", 5000)

.getResultList();

return reserveRooms(rooms);

}

[stdout] select

[stdout] room0_.ROOM_NUMBER as ROOM_NUM1_2_,

Page 38: JPA Server-Side Deployment - webdev.jhuep.comjcs/ejava-javaee/coursedocs/content/pdf/ejb-jpa-book.pdfJPA Server-Side Deployment iv 4.2.6. Stateful EJB Method Activates Transaction

Chapter 4. Persistence Contex...

32

[stdout] room0_.FLOOR_ID as FLOOR_ID2_2_,

[stdout] room0_.OCCUPANT_ID as OCCUPANT3_2_

[stdout] from

[stdout] EJBJPA_ROOM room0_

[stdout] where

[stdout] room0_.OCCUPANT_ID is null

[stdout] order by

[stdout] room0_.ROOM_NUMBER asc limit ? for update --notice the FOR UPDATE

• setLockMode(LockModeType.PESSIMISTIC_WRITE) - locks row (or table) for remainder of

transaction

• select ... FOR UPDATE issued for query

• competing client thread is blocked until end of transaction

4.5. Summary• Persistence Context Propagation

• Rollbacks (ACID)

• Pessamistic Locking