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.
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
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
v
Purpose• Server-side Resources
• PersistenceUnit/PersistenceContext injection
• Managed Entities and Remote Interfaces
• Persistence Context Propagation
vi
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}"/>
...
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
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
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
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
6
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
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.*;
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
10
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
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);
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
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
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_,
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 "
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;
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
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
20
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
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_
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=?,
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();
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]
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)
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
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
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
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 ***
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_,
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