Ch11 Advanced Topics - skatscher/pro_jpa2_book GitHub Wiki
SQL Queries
JPQL is the preferred way for querying over entities, though native SQL queries are also supported by JPA. JPQL will never be able to fully encompass all SQL features.
SQL features not supported by JPQL (2.0): Inline views (subqueries in the FROM clause), hierarchical queries, access to stored procedures, additional function expressions to manipulate date and time values
sometimes to achieve the performance required it is necessary to use a hand-optimized SQL and not JPQL
Defining and executing SQL Queries
SQL queries may be defined dynamically at runtime or named in persistence unit metadata
dynamically:
use createNativeQuery() method of the EntityManager interface, passing the query string and the entity type that will be returned.
The query engine uses the ORM of the entity to figure out which result column aliases map to which entity properties. As each row is processed, the query engine instantiates a new entity instance and sets the available data into it. If column aliases of the query do not match up exactly with the ORM for the entity, SQL result set mapping metadata is required.
use @NamedNativeQuery annotation to define named SQL queries. This annotation may be placed on any entity and defines the name of the query (must be unique within the persistence unit) as well as the query text.
resultClass element: indicates the entity class if the result type is an entity
resultSetMapping element: specify the mapping name if the result requires SQL mapping
use createNamedQuery() method of EntityManager interface to create and execute the query
Every Entity has the potential to go through one or more of a defined set of lifecycle events.
In order to respond to any one or more of these events, an entity class or any of its superclasses may declare one or more methods that will be invoked by the provider when the event gets fired. These methods are called callback methods.
Lifecycle Events
event types: persisting, updating, removing, loading which correspond to basic database operations
each event type, except for loading, has a Pre event and a Post event.
PrePersist and PostPersist
The PrePersist event notifies an entity when EnityManager.persist() has been successfully invoked on it, or a new entity has been merged by EntityManager.merge()
If the PERSIST cascade option is set on a relationship of an object that has been persisted and the target object is also a new object, the PrePersist event is triggered on the target object.
PostPersist events occur when an entity is inserted. Firing of PostPersist event does not indicate that entity has committed successfully to the database, because the transaction may rollback after PostPersist.
PreRemove and PostRemove
When an EntityManager.remove() call is invoked on an entity, the PreRemove callback is triggered.
Any related entities across relationships that have been configured with the REMOVE cascade option will also get a PreRemove notification.
When the SQL for deletion of an entity finally does get sent to the database, the PostRemove event will get fired. The PostRemove event does not guarantee success as the enclosing transaction may still be rolled back.
PreUpdate and PostUpdate
Updates to mangages entities may occur at any time, either within a transaction or outside a transaction.
The PreUpdate callback is guaranteed to to be invoked only at some point before the database update. Depending on the provider implementation this is done eagerly of lazily.
PostLoad
The PostLoad callback occurs after the data for an entity is read from the database and the entity instance is contructed.
It can also happen as result of a refresh() call on the entity manager. When a relationship is set to cascade REFRESH, the entities that get cascaded to will also get loaded.
Callback Methods
Designating a method as a callback method involves two steps:
defining the method according to a given signature
annotating the method with the appropriate lifecycle event annotation
Required signature defintion:
callback method may have any name
must take no parameters and must return void
final or static methods are not valid
checked exceptions may not be thrown. Runtime exceptions may be thrown - if they are thrown while in a transaction, the transaction gets marked for rollback and subsequent lifecycle event invocations are abandoned.
Annotations match the names of the events: @PrePersist, @PostPersist, @PreUpdate, @PostUpdate, @PreRemove, @PostRemove, @PostLoad.
A method may be annotated with multiple lifecycle event annotations, but only one lifecycle annotation of a given type may be present in an entity class.
Not supported types of operations inside a callback method:
invoking methods on an entity manager or executing queries
accessing other entities
Supported types of operations inside a callback method:
Use entity listener, if you want to pull the event handling code out of the entity class into a different class.
An entity listener can define one or more callback methods.
The entity listener needs typically access to the entiy state. The entity gets passed to the callback method. For that reason the signature of callback methods needs to define a single parameter wthich is compatible to the entity type (the entity class, a superclass, or an interface implemented by the entity)
further the callback methods
must be public void
must be annotatd with the necessary event annotations
must have a no-arg constructor
should be stateless and declare no fields
Attaching Entity Listeners to Entities
Use of @EntityListeners annotation, where one or more listeners may be listed in the annotation.
When a lifecycle event occurs, the provider will iterate though each of the entity listeners in the order in which they are listed and instantiates an instance of the entiy listener class that has a method annotated which the annotation for the given event and invoke the callback method. If any of the listeners throws an exception, it will abort the callback process, causing the remaining listeners and the callback method on the entiy to not be invoked.
One or more default entity listeners may be declared in the persistence unit metadata. These will apply to all entities in he persistence unit.
Any entity may opt out of having the default entiy listeners applied to it by using the @ExcludeDefaultListeners annotation.
Inheritance and Lifecycle Events
Inheriting Callback methods
Callback methods may occur in any entity or mapped superclass, be it abstract or concrete.
Order of invokation of callback methods: in the order according to its place in the inheritance hierarchy, most general classes first.
Inheriting Entity Listeners
@EntityListeners annotation is valid on entities and mapped superclasses in the hierarchy, be it concreate or abstract. It is additive and does not redefine listeners.
order of invokation: the listeners listed in the entity superclass annotation get invoked before the listeners in the subclass.
With @ExcludeSuperclassListeners annotation entity listeners of all superclasses will not be invoked.
Validation
In Java EE 6, validation is considered a separate aspect of the application. It is standardized in JSR303 for the platform. It was also designed to function in a stand-alone Java SE environment.
Validation has an annotation model and a dynamic API that can be invoked from any layer and on almost any bean
Using Constraints
Adding contraints to an object by annotating the class, its fiels, is its JavaBean-style properties.
Validation contraints are defined in the javax.validation.contraints package.
publicclassEmployee {
@NotNullprivateintid;
@NotNull(message="Employee name must be specified")
@Size(max=40)
privateStringname;
@PastprivateDatestartDate;
}
Invoking Validation
The main API for invoking validation on an object is the javax.validation.Validator class. Passing the object to validate to the validate() method of the Validator instance.
A Validator instance may beb injected in any Java EE component that supports injection.
When the validate method fails and a contraint is not satisfied, a ValidationException is thrown with a accompanying message String.
Concurrency
A managed Entity belongs to one persistence context and should not be managed by more than one persistence context at any given time.
Enitity managers and the persistence contexts that they manage are not intended to be accessed by more than one concurrently executing thread.
Refreshing Entity State
The refresh() method of the EnitityManager interface can be used to refresh the entity, when there are changes in the database we do not have in our managed entity.
em.refresh(emp);
refresh() can only be used with managed entities. If it is not managed an IllegalArgumentException will be thrown.
refresh() works like an "undo", it overwrites the managed entity with the state in the database. Unsaved changes get lost.
Refresh operations may be cascaded across relationships with the REFRESH value included in the cascade element.
To use when there is a good chance that the transaction in which changes are made to an entity will be the only one that actually changes the entity in that interval.
The lock on the entity is not aquired until the change is actually made to the database, usually at the end of the transaction.
When the lock is acquired a check is made on the data in the database. If a change occured meanwhile, it means that the current transaction should not overwrite the changes from the intervening transaction. At this stage, it must rollback the transaction and throw a OptimisticLockException.
Versioning
entity has a dedicated persistent field or property declared in it to store the version number of the entity
provider can check the version number stored in the database to see if it matched the version that it obtained previously. If it is greater, somebody changes the entity meanwhile and an exception should be thrown.
version fields are defined by annotating the field or property on the entity with a @Version annotation. Types can be int, short, long, the corresponding wrapper types and java.sql.Timestamp.
@Versionprivateintversion;
It cannot be portably relied on that bulk updates automatically update the version field. Some vendors support it, for those who don't the version can be manually updated as part of the UPDATE statement.
version fields are only automatically updated for non-relationship fields or the owning foreign key relationship fields.
Advanced Optimistic Locking Modes
By default JPA assumes Read Committed isolation. It guarantees that any changes made inside a transaction will not be visible to other transactions until the changing transaction has been commiteed. (version locking works with Read Committed isolation)
Locking options can be specified by means of a number of different calls (each method must be invoked within a transaction)
EntityManager.lock() - Explicit method for locking of objects already in persistence context
EntityManager.refresh() - Permits a lock mode to be passed in and applies to the object in the persistence context beeing refreshed
EntityManager.find() - Permits a lock mode to be passed in and applies to the object beeing returned
Query.setLockMode() - Sets the lock mode to be in effect during execution of the query
Optimistic Read Locking
Repeatable Read isolation prevents the non-repeatable read anomaly. This is when a transaction queries for the same data twice in the same transaction, the second query returns a different version of the data than was returned in the first because another transaction modified it in the intervening time. Repeatable Read isolation level means that once a transaction has accessed data and another transaction modifies that data, at least one of the transaction must be prevented from committing (which one fails depends on implementation).
Optimistic read lock in JPA provides Repeatable Read isolation. To optimistically read-lock an entity, a lock mode of LockModeType.OPTIMISTIC can be passed to one of the locking methods. (LockModeType.OPTIMISTIC was introduced in JPA 2.0 and is really just a rename of LockModeType.READ that existed in JPA 1.0)
Optimistic Write Locking
The optimistic write lock guarantees all that the optimistic read lock does, but also pledges to increment the version field in the transaction regardless of whether a user updated the entity or not. This is equivalent of making a forced update to the entity ans this is why the option is called optimistic force increment.
To optimistically write lock and enitity a lock mode of LockModeType.OPTIMISTIC_FORCE_INCREMENT can be passes to one of the locking methods. (LockModeType.OPTMISTIC_FORCE_INCREMENT was introduced in JPA 2.0 and is really just a rename of LockModeType.WRITE that existed in JPA 1.0)
Updates to the version column do not normally occur when changes are made to a non-owned relationship. Due to this, the common case for using OPTIMISTIC_FORCE_INCREMENT is to guarantee consistency across relationship changes when in the entity relationship pointers change, but in the data model no columns in the entity table change.