Apache OpenJPA 3.2 User's Guide

Built from OpenJPA version revision 000c065dbe8ceb4589fc1f789a37b8d68af6311e.

Last updated on March 16, 2022 at 10:42 AM.


Table of Contents

1. Introduction
1. About
2. Legal
1. License
2. Notice
3. Copyrights
3.1. Apache
3.2. Serp
3.3. Sun
3.4. Other
2. Java Persistence API
1. Introduction
1. Intended Audience
2. Lightweight Persistence
2. Why JPA?
3. Java Persistence API Architecture
1. JPA Exceptions
4. Entity
1. Restrictions on Persistent Classes
1.1. Default or No-Arg Constructor
1.2. Final
1.3. Identity Fields
1.4. Version Field
1.5. Inheritance
1.6. Persistent Fields
1.7. Conclusions
2. Entity Identity
2.1. Identity Class
2.1.1. Identity Hierarchies
3. Lifecycle Callbacks
3.1. Callback Methods
3.2. Using Callback Methods
3.3. Using Entity Listeners
3.4. Entity Listeners Hierarchy
4. Conclusions
5. Metadata
1. Class Metadata
1.1. Entity
1.2. Id Class
1.3. Mapped Superclass
1.4. Embeddable
1.5. EntityListeners
1.6. Example
2. Field and Property Metadata
2.1. Explicit Access
2.2. Transient
2.3. Id
2.4. Generated Value
2.5. Embedded Id
2.6. Version
2.7. Basic
2.7.1. Fetch Type
2.8. Embedded
2.9. Many To One
2.9.1. Cascade Type
2.10. One To Many
2.10.1. Bidirectional Relations
2.11. One To One
2.12. Many To Many
2.13. Order By
2.14. Map Key
2.15. Persistent Field Defaults
3. XML Schema
4. Conclusion
6. Persistence
1. persistence.xml
2. Non-EE Use
7. EntityManagerFactory
1. Obtaining an EntityManagerFactory
2. Obtaining EntityManagers
3. Persistence Context
3.1. Transaction Persistence Context
3.2. Extended Persistence Context
4. Retrieving Properties Information
5. Closing the EntityManagerFactory
6. PersistenceUnitUtil
8. EntityManager
1. Transaction Association
2. Entity Lifecycle Management
3. Lifecycle Examples
4. Entity Identity Management
5. Cache Management
6. Query Factory
7. Entity Locking
8. Retrieving Properties Information
9. Closing
9. Transaction
1. Transaction Types
2. The EntityTransaction Interface
10. JPA Query
1. JPQL API
1.1. Query Basics
1.2. Relation Traversal
1.3. Embeddable Traversal
1.4. Fetch Joins
1.5. JPQL Functions
1.6. Polymorphic Queries
1.7. Query Parameters
1.8. Query Hints
1.8.1. Locking Hints
1.8.2. Lock Timeout Hint
1.8.3. Query Timeout Hint
1.8.4. Result Set Size Hint
1.8.5. Isolation Level Hint
1.8.6. Other Fetchplan Hints
1.8.7. Database-Specific Hints
1.8.8. Named Query Hints
1.8.9. Handling of Multiple Similar Query Hints
1.9. Ordering
1.10. Aggregates
1.11. Named Queries
1.12. Delete By Query
1.13. Update By Query
2. JPQL Language Reference
2.1. JPQL Statement Types
2.1.1. JPQL Select Statement
2.1.2. JPQL Update and Delete Statements
2.2. JPQL Abstract Schema Types and Query Domains
2.2.1. JPQL Entity Naming
2.2.2. JPQL Schema Example
2.3. JPQL FROM Clause and Navigational Declarations
2.3.1. JPQL FROM Identifiers
2.3.2. JPQL Identification Variables
2.3.3. JPQL Range Declarations
2.3.4. JPQL Path Expressions
2.3.5. JPQL Joins
2.3.5.1. JPQL Inner Joins (Relationship Joins)
2.3.5.2. JPQL Outer Joins
2.3.5.3. JPQL Fetch Joins
2.3.6. JPQL Collection Member Declarations
2.3.7. JPQL FROM Clause and SQL
2.3.8. JPQL Polymorphism
2.4. JPQL WHERE Clause
2.5. JPQL Conditional Expressions
2.5.1. JPQL Literals
2.5.2. JPQL Identification Variables
2.5.3. JPQL Path Expressions
2.5.4. JPQL Input Parameters
2.5.4.1. JPQL Positional Parameters
2.5.4.2. JPQL Named Parameters
2.5.5. JPQL Conditional Expression Composition
2.5.6. JPQL Operators and Operator Precedence
2.5.7. JPQL Comparison Expressions
2.5.8. JPQL Between Expressions
2.5.9. JPQL In Expressions
2.5.10. JPQL Like Expressions
2.5.11. JPQL Null Comparison Expressions
2.5.12. JPQL Empty Collection Comparison Expressions
2.5.13. JPQL Collection Member Expressions
2.5.14. JPQL Exists Expressions
2.5.15. JPQL All or Any Expressions
2.5.16. JPQL Subqueries
2.6. JPQL Scalar Expressions
2.6.1. Arithmetic Expressions
2.6.2. String, Arithmetic, and Datetime Functional Expressions
2.6.2.1. JPQL String Functions
2.6.2.2. JPQL Arithmetic Functions
2.6.2.3. JPQL Datetime Functions
2.6.3. Case Expressions
2.6.4. Entity Type Expressions
2.7. JPQL GROUP BY, HAVING
2.8. JPQL SELECT Clause
2.8.1. JPQL Result Type of the SELECT Clause
2.8.2. JPQL Constructor Expressions
2.8.3. JPQL Null Values in the Query Result
2.8.4. JPQL Embeddables in the Query Result
2.8.5. JPQL Aggregate Functions
2.8.5.1. JPQL Aggregate Examples
2.8.5.2. JPQL Numeric Expressions in the SELECT Clause
2.9. JPQL ORDER BY Clause
2.10. JPQL Bulk Update and Delete
2.11. JPQL Null Values
2.12. JPQL Equality and Comparison Semantics
2.13. JPQL BNF
11. JPA Criteria
1. Constructing a CriteriaQuery
2. Executing a CriteriaQuery
3. Extension to Criteria API
4. Generation of Canonical MetaModel classes
12. SQL Queries
1. Creating SQL Queries
2. Retrieving Persistent Objects with SQL
13. Mapping Metadata
1. Table
2. Unique Constraints
3. Column
4. Identity Mapping
5. Generators
5.1. Sequence Generator
5.2. Table Generator
5.3. Example
6. Inheritance
6.1. Single Table
6.1.1. Advantages
6.1.2. Disadvantages
6.2. Joined
6.2.1. Advantages
6.2.2. Disadvantages
6.3. Table Per Class
6.3.1. Advantages
6.3.2. Disadvantages
6.4. Putting it All Together
7. Discriminator
8. Field Mapping
8.1. Basic Mapping
8.1.1. LOBs
8.1.2. Enumerated
8.1.3. Temporal Types
8.1.4. The Updated Mappings
8.2. Secondary Tables
8.3. Embedded Mapping
8.4. Direct Relations
8.5. Join Table
8.6. Bidirectional Mapping
8.7. Map Mapping
9. The Complete Mappings
14. Conclusion
3. Reference Guide
1. Introduction
1. Intended Audience
2. Configuration
1. Introduction
2. Runtime Configuration
3. Command Line Configuration
3.1. Code Formatting
4. Plugin Configuration
5. OpenJPA Properties
5.1. openjpa.AutoClear
5.2. openjpa.AutoDetach
5.3. openjpa.BrokerFactory
5.4. openjpa.BrokerImpl
5.5. openjpa.Callbacks
5.6. openjpa.ClassResolver
5.7. openjpa.Compatibility
5.8. openjpa.ConnectionDriverName
5.9. openjpa.Connection2DriverName
5.10. openjpa.ConnectionFactory
5.11. openjpa.ConnectionFactory2
5.12. openjpa.ConnectionFactoryName
5.13. openjpa.ConnectionFactory2Name
5.14. openjpa.ConnectionFactoryMode
5.15. openjpa.ConnectionFactoryProperties
5.16. openjpa.ConnectionFactory2Properties
5.17. openjpa.ConnectionPassword
5.18. openjpa.Connection2Password
5.19. openjpa.ConnectionProperties
5.20. openjpa.Connection2Properties
5.21. openjpa.ConnectionURL
5.22. openjpa.Connection2URL
5.23. openjpa.ConnectionUserName
5.24. openjpa.Connection2UserName
5.25. openjpa.ConnectionRetainMode
5.26. openjpa.DataCache
5.27. openjpa.DataCacheManager
5.28. openjpa.DataCacheMode
5.29. openjpa.DataCacheTimeout
5.30. openjpa.DetachState
5.31. openjpa.DynamicDataStructs
5.32. openjpa.DynamicEnhancementAgent
5.33. openjpa.FetchBatchSize
5.34. openjpa.EncryptionProvider
5.35. openjpa.FetchGroups
5.36. openjpa.FlushBeforeQueries
5.37. openjpa.IgnoreChanges
5.38. openjpa.Id
5.39. openjpa.InitializeEagerly
5.40. openjpa.Instrumentation
5.41. openjpa.InverseManager
5.42. openjpa.LockManager
5.43. openjpa.LockTimeout
5.44. openjpa.Log
5.45. openjpa.ManagedRuntime
5.46. openjpa.Mapping
5.47. openjpa.MaxFetchDepth
5.48. openjpa.MetaDataFactory
5.49. openjpa.MetaDataRepository
5.50. openjpa.Multithreaded
5.51. openjpa.Optimistic
5.52. openjpa.OptimizeIdCopy
5.53. openjpa.OrphanedKeyAction
5.54. openjpa.NontransactionalRead
5.55. openjpa.NontransactionalWrite
5.56. openjpa.ProxyManager
5.57. openjpa.PostLoadOnMerge
5.58. openjpa.QueryCache
5.59. openjpa.QueryCompilationCache
5.60. openjpa.ReadLockLevel
5.61. openjpa.RemoteCommitProvider
5.62. openjpa.RestoreState
5.63. openjpa.RetainState
5.64. openjpa.RetryClassRegistration
5.65. openjpa.RuntimeUnenhancedClasses
5.66. openjpa.SavepointManager
5.67. openjpa.Sequence
5.68. openjpa.Specification
5.69. openjpa.TransactionMode
5.70. openjpa.UseTCCLinSelectNew
5.71. openjpa.WriteLockLevel
6. OpenJPA JDBC Properties
6.1. openjpa.jdbc.ConnectionDecorators
6.2. openjpa.jdbc.DBDictionary
6.3. openjpa.jdbc.DriverDataSource
6.4. openjpa.jdbc.EagerFetchMode
6.5. openjpa.jdbc.FetchDirection
6.6. openjpa.jdbc.JDBCListeners
6.7. openjpa.jdbc.LRSSize
6.8. openjpa.jdbc.MappingDefaults
6.9. openjpa.jdbc.MappingFactory
6.10. openjpa.jdbc.QuerySQLCache
6.11. openjpa.jdbc.ResultSetType
6.12. openjpa.jdbc.Schema
6.13. openjpa.jdbc.SchemaFactory
6.14. openjpa.jdbc.Schemas
6.15. openjpa.jdbc.SQLFactory
6.16. openjpa.jdbc.SubclassFetchMode
6.17. openjpa.jdbc.SynchronizeMappings
6.18. openjpa.jdbc.TransactionIsolation
6.19. openjpa.jdbc.UpdateManager
6.20. Compatibility with Specification
3. Logging and Auditing
1. Logging Channels
2. OpenJPA Logging
3. Disabling Logging
4. Log4J
5. Apache Commons Logging
5.1. JDK java.util.logging
6. SLF4J
7. Custom Log
8. OpenJPA Audit
8.1. Configuration
8.2. Developing custom auditing
4. JDBC
1. Using the OpenJPA DataSource
1.1. Optional Connection Pooling
1.2. Configuring the OpenJPA DataSource
1.3. Configuring Apache Commons DBCP
2. Using a Third-Party DataSource
2.1. Managed and XA DataSources
2.2. Setting the DataSource at runtime
2.2.1. Using different DataSources for each EntityManager
2.2.1.1. Benefits
2.2.1.2. Limitations
2.2.1.3. Error handling
3. Runtime Access to DataSource
4. Database Support
4.1. DBDictionary Properties
4.2. FirebirdDictionary Properties
4.3. MySQLDictionary Properties
4.4. OracleDictionary Properties
4.5. SybaseDictionary Properties
4.6. DB2 Properties
4.7. Delimited Identifiers Support
5. Setting the Transaction Isolation
6. Setting the SQL Join Syntax
7. Accessing Multiple Databases
8. Configuring the Use of JDBC Connections
9. Statement Batching
10. Large Result Sets
11. Default Schema
12. Schema Reflection
12.1. Schemas List
12.2. Schema Factory
13. Schema Tool
14. XML Schema Format
5. Persistent Classes
1. Persistent Class List
2. Enhancement
2.1. Enhancing at Build Time
2.2. Enhancing JPA Entities on Deployment
2.3. Enhancing at Runtime
2.4. Enhancing Dynamically at Runtime
2.5. Omitting the OpenJPA enhancer
3. Managed Interfaces
4. Object Identity
4.1. Datastore Identity
4.2. Entities as Identity Fields
4.3. Application Identity Tool
4.4. Autoassign / Identity Strategy Caveats
5. Managed Inverses
6. Persistent Fields
6.1. Restoring State
6.2. Typing and Ordering
6.3. Calendar Fields and TimeZones
6.4. Proxies
6.4.1. Smart Proxies
6.4.2. Large Result Set Proxies
6.4.3. Custom Proxies
6.4.4. Serialization
6.5. Externalization
6.5.1. External Values
7. Fetch Groups
7.1. Custom Fetch Groups
7.2. Custom Fetch Group Configuration
7.3. Per-field Fetch Configuration
7.4. Implementation Notes
8. Eager Fetching
8.1. Configuring Eager Fetching
8.2. Eager Fetching Considerations and Limitations
6. Metadata
1. Metadata Factory
2. Metadata Repository
3. Additional JPA Metadata
3.1. Datastore Identity
3.2. Surrogate Version
3.3. Persistent Field Values
3.4. Persistent Collection Fields
3.5. Persistent Map Fields
4. Metadata Extensions
4.1. Class Extensions
4.1.1. Fetch Groups
4.1.2. Data Cache
4.1.3. Detached State
4.2. Field Extensions
4.2.1. Dependent
4.2.2. Load Fetch Group
4.2.3. LRS
4.2.4. Inverse-Logical
4.2.5. Read-Only
4.2.6. Type
4.2.7. Externalizer
4.2.8. Factory
4.2.9. External Values
4.3. Example
4.4. XML extensions
7. Mapping
1. Forward Mapping
1.1. Using the Mapping Tool
1.2. Generating DDL SQL
1.3. Runtime Forward Mapping
2. Reverse Mapping
2.1. Customizing Reverse Mapping
3. Meet-in-the-Middle Mapping
4. Mapping Defaults
5. Mapping Factory
6. Non-Standard Joins
7. Additional JPA Mappings
7.1. Datastore Identity Mapping
7.2. Surrogate Version Mapping
7.3. Multi-Column Mappings
7.4. Join Column Attribute Targets
7.5. Embedded Mapping
7.6. Collections
7.6.1. Container Table
7.6.2. Element Join Columns
7.6.3. Order Column
7.7. One-Sided One-Many Mapping
7.8. Maps
7.8.1. Key Columns
7.8.2. Key Join Columns
7.8.3. Key Embedded Mapping
7.8.4. Examples
7.9. Indexes and Constraints
7.9.1. Indexes
7.9.2. Foreign Keys
7.9.3. Unique Constraints
7.10. XML Column Mapping
7.11. LOB Streaming
8. Mapping Limitations
8.1. Table Per Class
9. Mapping Extensions
9.1. Class Extensions
9.1.1. Subclass Fetch Mode
9.1.2. Strategy
9.1.3. Discriminator Strategy
9.1.4. Version Strategy
9.2. Field Extensions
9.2.1. Eager Fetch Mode
9.2.2. Nonpolymorphic
9.2.3. Class Criteria
9.2.4. Strategy
10. Custom Mappings
10.1. Custom Class Mapping
10.2. Custom Discriminator and Version Strategies
10.3. Custom Field Mapping
10.3.1. Value Handlers
10.3.2. Field Strategies
10.3.3. Configuration
11. Orphaned Keys
8. Deployment
1. Factory Deployment
1.1. Standalone Deployment
1.2. EntityManager Injection
2. Integrating with the Transaction Manager
3. XA Transactions
3.1. Using OpenJPA with XA Transactions
9. Runtime Extensions
1. Architecture
1.1. Broker Finalization
1.2. Broker Customization and Eviction
2. JPA Extensions
2.1. OpenJPAEntityManagerFactory
2.2. OpenJPAEntityManager
2.3. OpenJPAQuery
2.4. Extent
2.5. StoreCache
2.6. QueryResultCache
2.7. FetchPlan
2.8. OpenJPAEntityTransaction
2.9. OpenJPAPersistence
3. Object Locking
3.1. Configuring Default Locking
3.2. Configuring Lock Levels at Runtime
3.3. Object Locking APIs
3.4. Lock Manager
3.5. Rules for Locking Behavior
3.6. Known Issues and Limitations
4. Savepoints
4.1. Using Savepoints
4.2. Configuring Savepoints
5. MethodQL
6. Generators
6.1. Runtime Access
7. Transaction Events
8. Non-Relational Stores
10. Caching
1. Data Cache
1.1. Data Cache Configuration
1.1.1. openjpa.DataCache Configuration
1.1.2. Integration with JPA standard shared cache mode
1.1.3. Distributing instances across cache partitions
1.2. Data Cache Usage
1.2.1. Using the JPA standard Cache interface
1.2.2. Using the OpenJPA StoreCache extensions
1.3. Cache Statistics
1.4. Query Cache
1.5. Cache Extension
1.6. Important Notes
1.7. Known Issues and Limitations
2. Query Compilation Cache
3. Prepared SQL Cache
11. Encryption Provider
12. Remote and Offline Operation
1. Detach and Attach
1.1. Detach Behavior
1.2. Attach Behavior
1.3. Defining the Detached Object Graph
1.3.1. Detached State
1.3.2. Detached State Field
2. Remote Event Notification Framework
2.1. Remote Commit Provider Configuration
2.1.1. JMS
2.1.2. TCP
2.1.3. Common Properties
2.2. Customization
13. Slice: Distributed Persistence
1. Overview
2. Salient Features
2.1. Transparency
2.2. Scaling
2.3. Distributed Query
2.4. Data Distribution
2.5. Data Replication
2.6. Heterogeneous Database
2.7. Distributed Transaction
2.8. Collocation Constraint
3. Usage
3.1. How to activate Slice Runtime?
3.2. How to configure each database slice?
3.3. Implement DistributionPolicy interface
3.4. Implement ReplicationPolicy interface
4. Configuration Properties
4.1. Global Properties
4.1.1. openjpa.slice.DistributionPolicy
4.1.2. openjpa.slice.Lenient
4.1.3. openjpa.slice.Master
4.1.4. openjpa.slice.Names
4.1.5. openjpa.slice.ThreadingPolicy
4.1.6. openjpa.slice.TransactionPolicy
4.2. Per-Slice Properties
14. Third Party Integration
1. Apache Ant
1.1. Common Ant Configuration Options
1.2. Enhancer Ant Task
1.3. Application Identity Tool Ant Task
1.4. Mapping Tool Ant Task
1.5. Reverse Mapping Tool Ant Task
1.6. Schema Tool Ant Task
2. Apache Commons DBCP
2.1. Apache Commons DBCP Configuration Options
15. Optimization Guidelines
16. Instrumentation
1. Configuration
1.1. JMX Platform MBean Enablement
2. Custom Providers and Instruments
4. Appendices
1. JPA Resources
2. Supported Databases
1. Overview
2. Verified Database Matrix
3. Compatible Database Matrix
4. Unverified Database Matrix
5. Apache Derby
6. Borland Interbase
6.1. Known issues with Interbase
7. JDataStore
8. IBM DB2
8.1. Known issues with DB2
9. Empress
9.1. Known issues with Empress
10. H2 Database Engine
10.1. Known issues with H2 Database Engine
11. Hypersonic
11.1. Known issues with Hypersonic
12. Firebird
12.1. Known issues with Firebird
13. Informix
13.1. Known issues with Informix
14. Ingres Database
14.1. Known issues with Ingres
15. InterSystems Cache
15.1. Known issues with InterSystems Cache
16. Microsoft Access
16.1. Known issues with Microsoft Access
17. Microsoft SQL Server
17.1. Known issues with SQL Server
18. Microsoft FoxPro
18.1. Known issues with Microsoft FoxPro
19. MySQL
19.1. Using Query Hints with MySQL
19.2. Known issues with MySQL
20. MariaDB
20.1. Known issues with MariaDB
21. Oracle
21.1. Using Query Hints with Oracle
21.2. Known issues with Oracle
22. Pointbase
22.1. Known issues with Pointbase
23. PostgreSQL
23.1. Known issues with PostgreSQL
24. IBM solidDB
24.1. M-type tables vs. D-type tables
24.2. Concurrency control mechanism
25. Sybase Adaptive Server
25.1. Known issues with Sybase
3. Migration Considerations
1. OpenJPA 2.0.0
1.1. Incompatibilities
1.1.1. getProperties()
1.1.2. Detach Behavior
1.1.3. Use of private persistent properties
1.1.4. Query.setParameter()
1.1.5. Serialization of Entities
1.1.6. openjpa.jdbc.QuerySQLCache
1.2. Disabling AutoOff Collection Tracking
1.3. Internal Behavioral Differences
1.3.1. PreUpdate/PostUpdate Life Cycle Callbacks
1.3.2. createEntityManagerFactory Exceptions
1.3.3. openjpa.QueryCache default
2. OpenJPA 2.2.0
2.1. Incompatibilities
2.1.1. allocationSize Property of Sequence Generator
2.1.2. MetaModel Attributes for Arrays
2.1.3. supportsSetClob Property.
2.1.4. useNativeSequenceCache Property.
2.1.5. Cascade persist behavior
2.1.6. Life Cycle Event Manager Callback Behavior
2.1.7. shared-cache-mode Property
3. OpenJPA 2.3.0
3.1. Incompatibilities
3.1.1. MappingTool Behavior for DB2 and Derby
3.1.2. RequiresSearchStringEscapeForLike DBDictionary Property
3.1.3. Return value of aggregate functions in SELECT clause
4. OpenJPA 3.2.0
4.1. Incompatibilities
4.1.1. SUM now always returns Double
4.1.2. Invalid Column Name Changes
4.1.3. MappingTool Behavior for HSQLDB
4.1.4. Respect TIMESTAMP precision in Oracle
4.1.5. Unary Operations return types
4.1.6. PostgreSQL now supports setQueryTimeOut

List of Tables

2.1. Persistence Mechanisms
10.1. Interaction of ReadLockMode hint and LockManager
2.1. Standard JPA Properties and OpenJPA equivalents
4.1. Default delimiters for delimited identifiers
4.2. OpenJPA Automatic Flush Behavior
5.1. Externalizer Options
5.2. Factory Options
10.1. Data access methods
10.2. Pre-defined aliases
10.3. Pre-defined aliases
15.1. Optimization Guidelines
2.1. Supported Databases and JDBC Drivers
2.2. Compatible Databases and JDBC Drivers
2.3. Unverified Databases and JDBC Drivers

List of Examples

3.1. Interaction of Interfaces Outside Container
3.2. Interaction of Interfaces Inside Container
4.1. Persistent Class
4.2. Identity Class
5.1. Class Metadata
5.2. Complete Metadata
6.1. persistence.xml
6.2. Obtaining an EntityManagerFactory
7.1. Behavior of Transaction Persistence Context
7.2. Behavior of Extended Persistence Context
8.1. Persisting Objects
8.2. Updating Objects
8.3. Removing Objects
8.4. Detaching and Merging
9.1. Grouping Operations with Transactions
10.1. Query Hints
10.2. Named Query using Hints
10.3. Setting Multiple Similar Query Hints
10.4. Delete by Query
10.5. Update by Query
12.1. Creating a SQL Query
12.2. Retrieving Persistent Objects
12.3. SQL Query Parameters
13.1. Mapping Classes
13.2. Defining a Unique Constraint
13.3. Identity Mapping
13.4. Generator Mapping
13.5. Single Table Mapping
13.6. Joined Subclass Tables
13.7. Table Per Class Mapping
13.8. Inheritance Mapping
13.9. Discriminator Mapping
13.10. Basic Field Mapping
13.11. Secondary Table Field Mapping
13.12. Embedded Field Mapping
13.13. Mapping Mapped Superclass Field
13.14. Direct Relation Field Mapping
13.15. Join Table Mapping
13.16. Join Table Map Mapping
13.17. Full Entity Mappings
2.1. Code Formatting with the Application Id Tool
3.1. Standard OpenJPA Log Configuration
3.2. Standard OpenJPA Log Configuration + All SQL Statements
3.3. Logging to a File
3.4. Standard Log4J Logging
3.5. JDK Log Properties
3.6. Custom Logging Class
4.1. Properties for the OpenJPA DataSource
4.2. Properties File for a Third-Party DataSource
4.3. Managed DataSource Configuration
4.4. Setting DataSource at Runtime
4.5. Using the EntityManager's Connection
4.6. Using the EntityManagerFactory's DataSource
4.7. Specifying a DBDictionary
4.8. Specifying a Transaction Isolation
4.9. Specifying the Join Syntax Default
4.10. Specifying the Join Syntax at Runtime
4.11. Specifying Connection Usage Defaults
4.12. Specifying Connection Usage at Runtime
4.13. Enable SQL statement batching
4.14. Disable SQL statement batching
4.15. Plug-in custom statement batching implementation
4.16. Specifying Result Set Defaults
4.17. Specifying Result Set Behavior at Runtime
4.18. Schema Creation
4.19. SQL Scripting
4.20. Table Cleanup
4.21. Schema Drop
4.22. Schema Reflection
4.23. Basic Schema
4.24. Full Schema
5.1. Using the OpenJPA Enhancer
5.2. Using the OpenJPA Agent for Runtime Enhancement
5.3. Passing Options to the OpenJPA Agent
5.4. JPA Datastore Identity Metadata
5.5. Finding an Entity with an Entity Identity Field
5.6. Id Class for Entity Identity Fields
5.7. Embedded Id for Entity Identity Fields
5.8. Using the Application Identity Tool
5.9. Specifying Logical Inverses
5.10. Enabling Managed Inverses
5.11. Log Inconsistencies
5.12. Using Initial Field Values
5.13. Using a Large Result Set Iterator
5.14. Marking a Large Result Set Field
5.15. Configuring the Proxy Manager
5.16. Using Externalization
5.17. Querying Externalization Fields
5.18. Using External Values
5.19. Custom Fetch Group Metadata
5.20. Load Fetch Group Metadata
5.21. Using the FetchPlan
5.22. Adding an Eager Field
5.23. Setting the Default Eager Fetch Mode
5.24. Setting the Eager Fetch Mode at Runtime
6.1. Setting a Standard Metadata Factory
6.2. Setting a Custom Metadata Factory
6.3. Setting the Preload Property on Metadata Repository
6.4. OpenJPA Metadata Extensions
6.5. OpenJPA Schema Extensions
7.1. Using the Mapping Tool
7.2. Creating the Relational Schema from Mappings
7.3. Refreshing entire schema and cleaning out tables
7.4. Dropping Mappings and Association Schema
7.5. Create DDL for Current Mappings
7.6. Create DDL to Update Database for Current Mappings
7.7. Configuring Runtime Forward Mapping
7.8. Reflection with the Schema Tool
7.9. Using the Reverse Mapping Tool
7.10. Customizing Reverse Mapping with Properties
7.11. Validating Mappings
7.12. Configuring Mapping Defaults
7.13. Standard JPA Configuration
7.14. Datastore Identity Mapping
7.15. Overriding Complex Mappings
7.16. One-Sided One-Many Mapping
7.17. String Key, Entity Value Map Mapping
7.18. myaddress.xsd
7.19. Address.java
7.20. USAAddress.java
7.21. CANAddress.java
7.22. Showing annotated Order entity with XML mapping strategy
7.23. Showing creation of Order entity having shipAddress mapped to XML column
7.24. Sample JPQL queries for XML column mapping
7.25. Annotated InputStream and Reader
7.26. Custom Logging Orphaned Keys
8.1. Configuring Transaction Manager Integration
9.1. Evict from Data Cache
9.2. Using a JPA Extent
9.3. Setting Default Lock Levels
9.4. Setting Runtime Lock Levels
9.5. Locking APIs
9.6. Disabling Locking
9.7. Using Savepoints
9.8. Named Seq Sequence
9.9. System Sequence Configuration
10.1. Single-JVM Data Cache
10.2. Lru Cache
10.3. Data Cache Size
10.4. Data Cache Timeout
10.5. Excluding entities
10.6. Including entities
10.7. Bulk updates and cache eviction
10.8.
10.9.
10.10.
10.11. Partitioned Data Cache
10.12. Accessing the Cache
10.13. Using the javax.persistence.Cache interface
10.14. Accessing the StoreCache
10.15. StoreCache Usage
10.16. Automatic Data Cache Eviction
10.17. Configuring CacheStatistics
10.18. Accessing the QueryResultCache
10.19. Query Cache Size
10.20. Disabling the Query Cache
10.21. Query Cache Eviction Policy
10.22. Evicting Queries
10.23. Pinning, and Unpinning Query Results
10.24. Disabling and Enabling Query Caching
10.25. Query Replaces Extent
10.26. Hardcoded Selection Value in JPQL Query
10.27. Parameterized Selection Value in JPQL Query
12.1. Configuring Detached State
12.2. JMS Remote Commit Provider Configuration
12.3. TCP Remote Commit Provider Configuration
12.4. JMS Remote Commit Provider transmitting Persisted Object Ids
14.1. Using the <config> Ant Tag
14.2. Using the Properties Attribute of the <config> Tag
14.3. Using the PropertiesFile Attribute of the <config> Tag
14.4. Using the <classpath> Ant Tag
14.5. Using the <codeformat> Ant Tag
14.6. Invoking the Enhancer from Ant
14.7. Invoking the Application Identity Tool from Ant
14.8. Invoking the Mapping Tool from Ant
14.9. Invoking the Reverse Mapping Tool from Ant
14.10. Invoking the Schema Tool from Ant
14.11. Using Commons DBCP with Apache Derby
2.1. Example properties for Derby
2.2. Example properties for Interbase
2.3. Example properties for JDataStore
2.4. Example properties for IBM DB2
2.5. Example properties for Empress
2.6. Example properties for H2 Database Engine
2.7. Example properties for Hypersonic
2.8. Example properties for Firebird
2.9. Example properties for Informix Dynamic Server
2.10. Example properties for Ingres
2.11. Example properties for InterSystems Cache
2.12. Example properties for Microsoft Access
2.13. Example properties for Microsoft SQL Server
2.14. Example properties for Microsoft FoxPro
2.15. Example properties for MySQL
2.16. Using MySQL Hints
2.17. Example properties for MariaDB
2.18. Example properties for Oracle
2.19. Using Oracle Hints
2.20. Property to disable statement batching for Oracle
2.21. Property to retain connection over the lifetime of the entity manager
2.22. Example properties for Pointbase
2.23. Example properties for PostgreSQL
2.24. Example properties for IBM solidDB
2.25. Example properties for Sybase

Part 1. Introduction

Chapter 1.  About

OpenJPA is Apache's implementation of Java Persistence 2.2 API (JSR-338 JPA 2.2) specification for the transparent persistence of Java objects. This document provides an overview of the JPA standard and technical details on the use of OpenJPA.

This document is intended for OpenJPA users. It is divided into several parts:

  • The JPA Overview describes the fundamentals of the JPA specification.

  • The OpenJPA Reference Guide contains detailed documentation on all aspects of OpenJPA. Browse through this guide to familiarize yourself with the many advanced features and customization opportunities OpenJPA provides. Later, you can use the guide when you need details on a specific aspect of OpenJPA.

  • Appendices

Part 2. Java Persistence API

Table of Contents

1. Introduction
1. Intended Audience
2. Lightweight Persistence
2. Why JPA?
3. Java Persistence API Architecture
1. JPA Exceptions
4. Entity
1. Restrictions on Persistent Classes
1.1. Default or No-Arg Constructor
1.2. Final
1.3. Identity Fields
1.4. Version Field
1.5. Inheritance
1.6. Persistent Fields
1.7. Conclusions
2. Entity Identity
2.1. Identity Class
2.1.1. Identity Hierarchies
3. Lifecycle Callbacks
3.1. Callback Methods
3.2. Using Callback Methods
3.3. Using Entity Listeners
3.4. Entity Listeners Hierarchy
4. Conclusions
5. Metadata
1. Class Metadata
1.1. Entity
1.2. Id Class
1.3. Mapped Superclass
1.4. Embeddable
1.5. EntityListeners
1.6. Example
2. Field and Property Metadata
2.1. Explicit Access
2.2. Transient
2.3. Id
2.4. Generated Value
2.5. Embedded Id
2.6. Version
2.7. Basic
2.7.1. Fetch Type
2.8. Embedded
2.9. Many To One
2.9.1. Cascade Type
2.10. One To Many
2.10.1. Bidirectional Relations
2.11. One To One
2.12. Many To Many
2.13. Order By
2.14. Map Key
2.15. Persistent Field Defaults
3. XML Schema
4. Conclusion
6. Persistence
1. persistence.xml
2. Non-EE Use
7. EntityManagerFactory
1. Obtaining an EntityManagerFactory
2. Obtaining EntityManagers
3. Persistence Context
3.1. Transaction Persistence Context
3.2. Extended Persistence Context
4. Retrieving Properties Information
5. Closing the EntityManagerFactory
6. PersistenceUnitUtil
8. EntityManager
1. Transaction Association
2. Entity Lifecycle Management
3. Lifecycle Examples
4. Entity Identity Management
5. Cache Management
6. Query Factory
7. Entity Locking
8. Retrieving Properties Information
9. Closing
9. Transaction
1. Transaction Types
2. The EntityTransaction Interface
10. JPA Query
1. JPQL API
1.1. Query Basics
1.2. Relation Traversal
1.3. Embeddable Traversal
1.4. Fetch Joins
1.5. JPQL Functions
1.6. Polymorphic Queries
1.7. Query Parameters
1.8. Query Hints
1.8.1. Locking Hints
1.8.2. Lock Timeout Hint
1.8.3. Query Timeout Hint
1.8.4. Result Set Size Hint
1.8.5. Isolation Level Hint
1.8.6. Other Fetchplan Hints
1.8.7. Database-Specific Hints
1.8.8. Named Query Hints
1.8.9. Handling of Multiple Similar Query Hints
1.9. Ordering
1.10. Aggregates
1.11. Named Queries
1.12. Delete By Query
1.13. Update By Query
2. JPQL Language Reference
2.1. JPQL Statement Types
2.1.1. JPQL Select Statement
2.1.2. JPQL Update and Delete Statements
2.2. JPQL Abstract Schema Types and Query Domains
2.2.1. JPQL Entity Naming
2.2.2. JPQL Schema Example
2.3. JPQL FROM Clause and Navigational Declarations
2.3.1. JPQL FROM Identifiers
2.3.2. JPQL Identification Variables
2.3.3. JPQL Range Declarations
2.3.4. JPQL Path Expressions
2.3.5. JPQL Joins
2.3.5.1. JPQL Inner Joins (Relationship Joins)
2.3.5.2. JPQL Outer Joins
2.3.5.3. JPQL Fetch Joins
2.3.6. JPQL Collection Member Declarations
2.3.7. JPQL FROM Clause and SQL
2.3.8. JPQL Polymorphism
2.4. JPQL WHERE Clause
2.5. JPQL Conditional Expressions
2.5.1. JPQL Literals
2.5.2. JPQL Identification Variables
2.5.3. JPQL Path Expressions
2.5.4. JPQL Input Parameters
2.5.4.1. JPQL Positional Parameters
2.5.4.2. JPQL Named Parameters
2.5.5. JPQL Conditional Expression Composition
2.5.6. JPQL Operators and Operator Precedence
2.5.7. JPQL Comparison Expressions
2.5.8. JPQL Between Expressions
2.5.9. JPQL In Expressions
2.5.10. JPQL Like Expressions
2.5.11. JPQL Null Comparison Expressions
2.5.12. JPQL Empty Collection Comparison Expressions
2.5.13. JPQL Collection Member Expressions
2.5.14. JPQL Exists Expressions
2.5.15. JPQL All or Any Expressions
2.5.16. JPQL Subqueries
2.6. JPQL Scalar Expressions
2.6.1. Arithmetic Expressions
2.6.2. String, Arithmetic, and Datetime Functional Expressions
2.6.2.1. JPQL String Functions
2.6.2.2. JPQL Arithmetic Functions
2.6.2.3. JPQL Datetime Functions
2.6.3. Case Expressions
2.6.4. Entity Type Expressions
2.7. JPQL GROUP BY, HAVING
2.8. JPQL SELECT Clause
2.8.1. JPQL Result Type of the SELECT Clause
2.8.2. JPQL Constructor Expressions
2.8.3. JPQL Null Values in the Query Result
2.8.4. JPQL Embeddables in the Query Result
2.8.5. JPQL Aggregate Functions
2.8.5.1. JPQL Aggregate Examples
2.8.5.2. JPQL Numeric Expressions in the SELECT Clause
2.9. JPQL ORDER BY Clause
2.10. JPQL Bulk Update and Delete
2.11. JPQL Null Values
2.12. JPQL Equality and Comparison Semantics
2.13. JPQL BNF
11. JPA Criteria
1. Constructing a CriteriaQuery
2. Executing a CriteriaQuery
3. Extension to Criteria API
4. Generation of Canonical MetaModel classes
12. SQL Queries
1. Creating SQL Queries
2. Retrieving Persistent Objects with SQL
13. Mapping Metadata
1. Table
2. Unique Constraints
3. Column
4. Identity Mapping
5. Generators
5.1. Sequence Generator
5.2. Table Generator
5.3. Example
6. Inheritance
6.1. Single Table
6.1.1. Advantages
6.1.2. Disadvantages
6.2. Joined
6.2.1. Advantages
6.2.2. Disadvantages
6.3. Table Per Class
6.3.1. Advantages
6.3.2. Disadvantages
6.4. Putting it All Together
7. Discriminator
8. Field Mapping
8.1. Basic Mapping
8.1.1. LOBs
8.1.2. Enumerated
8.1.3. Temporal Types
8.1.4. The Updated Mappings
8.2. Secondary Tables
8.3. Embedded Mapping
8.4. Direct Relations
8.5. Join Table
8.6. Bidirectional Mapping
8.7. Map Mapping
9. The Complete Mappings
14. Conclusion

Chapter 1.  Introduction

The Java Persistence API (JPA) is a specification for the persistence of Java objects to any relational datastore. This document provides an overview of JPA 2.2. Unless otherwise noted, the information presented applies to all JPA implementations.

Note

For coverage of OpenJPA's many extensions to the JPA specification, see the Reference Guide.

1.  Intended Audience

This document is intended for developers who want to learn about JPA in order to use it in their applications. It assumes that you have a strong knowledge of object-oriented concepts and Java, including annotations and generics. It also assumes some experience with relational databases and the Structured Query Language (SQL).

2.  Lightweight Persistence

Persistent data is information that can outlive the program that creates it. The majority of complex programs use persistent data: GUI applications need to store user preferences across program invocations, web applications track user movements and orders over long periods of time, etc.

Lightweight persistence is the storage and retrieval of persistent data with little or no work from you, the developer. For example, Java serialization is a form of lightweight persistence because it can be used to persist Java objects directly to a file with very little effort. Serialization's capabilities as a lightweight persistence mechanism pale in comparison to those provided by JPA, however. The next chapter compares JPA to serialization and other available persistence mechanisms.

Chapter 2.  Why JPA?

Java developers who need to store and retrieve persistent data already have several options available to them: serialization, JDBC, JDO, proprietary object-relational mapping tools, object databases, and EJB 2 entity beans. Why introduce yet another persistence framework? The answer to this question is that with the exception of JDO, each of the aforementioned persistence solutions has severe limitations. JPA attempts to overcome these limitations, as illustrated by the table below.

Table 2.1.  Persistence Mechanisms

Supports: Serialization JDBC ORM ODB EJB 2 JDO JPA
Java Objects Yes No Yes Yes Yes Yes Yes
Advanced OO Concepts Yes No Yes Yes No Yes Yes
Transactional Integrity No Yes Yes Yes Yes Yes Yes
Concurrency No Yes Yes Yes Yes Yes Yes
Large Data Sets No Yes Yes Yes Yes Yes Yes
Existing Schema No Yes Yes No Yes Yes Yes
Relational and Non-Relational Stores No No No No Yes Yes No
Queries No Yes Yes Yes Yes Yes Yes
Strict Standards / Portability Yes No No No Yes Yes Yes
Simplicity Yes Yes Yes Yes No Yes Yes

  • Serialization is Java's built-in mechanism for transforming an object graph into a series of bytes, which can then be sent over the network or stored in a file. Serialization is very easy to use, but it is also very limited. It must store and retrieve the entire object graph at once, making it unsuitable for dealing with large amounts of data. It cannot undo changes that are made to objects if an error occurs while updating information, making it unsuitable for applications that require strict data integrity. Multiple threads or programs cannot read and write the same serialized data concurrently without conflicting with each other. It provides no query capabilities. All these factors make serialization useless for all but the most trivial persistence needs.

  • Many developers use the Java Database Connectivity (JDBC) APIs to manipulate persistent data in relational databases. JDBC overcomes most of the shortcomings of serialization: it can handle large amounts of data, has mechanisms to ensure data integrity, supports concurrent access to information, and has a sophisticated query language in SQL. Unfortunately, JDBC does not duplicate serialization's ease of use. The relational paradigm used by JDBC was not designed for storing objects, and therefore forces you to either abandon object-oriented programming for the portions of your code that deal with persistent data, or to find a way of mapping object-oriented concepts like inheritance to relational databases yourself.

  • There are many proprietary software products that can perform the mapping between objects and relational database tables for you. These object-relational mapping (ORM) frameworks allow you to focus on the object model and not concern yourself with the mismatch between the object-oriented and relational paradigms. Unfortunately, each of these product has its own set of APIs. Your code becomes tied to the proprietary interfaces of a single vendor. If the vendor raises prices, fails to fix show-stopping bugs, or falls behind in features, you cannot switch to another product without rewriting all of your persistence code. This is referred to as vendor lock-in.

  • Rather than map objects to relational databases, some software companies have developed a form of database designed specifically to store objects. These object databases (ODBs) are often much easier to use than object-relational mapping software. The Object Database Management Group (ODMG) was formed to create a standard API for accessing object databases; few object database vendors, however, comply with the ODMG's recommendations. Thus, vendor lock-in plagues object databases as well. Many companies are also hesitant to switch from tried-and-true relational systems to the relatively unknown object database technology. Fewer data-analysis tools are available for object database systems, and there are vast quantities of data already stored in older relational databases. For all of these reasons and more, object databases have not caught on as well as their creators hoped.

  • The Enterprise Edition of the Java platform introduced entity Enterprise Java Beans (EJBs). EJB 2.x entities are components that represent persistent information in a datastore. Like object-relational mapping solutions, EJB 2.x entities provide an object-oriented view of persistent data. Unlike object-relational software, however, EJB 2.x entities are not limited to relational databases; the persistent information they represent may come from an Enterprise Information System (EIS) or other storage device. Also, EJB 2.x entities use a strict standard, making them portable across vendors. Unfortunately, the EJB 2.x standard is somewhat limited in the object-oriented concepts it can represent. Advanced features like inheritance, polymorphism, and complex relations are absent. Additionally, EBJ 2.x entities are difficult to code, and they require heavyweight and often expensive application servers to run.

  • The JDO specification uses an API that is strikingly similar to JPA. JDO, however, supports non-relational databases, a feature that some argue dilutes the specification.

JPA combines the best features from each of the persistence mechanisms listed above. Creating entities under JPA is as simple as creating serializable classes. JPA supports the large data sets, data consistency, concurrent use, and query capabilities of JDBC. Like object-relational software and object databases, JPA allows the use of advanced object-oriented concepts such as inheritance. JPA avoids vendor lock-in by relying on a strict specification like JDO and EJB 2.x entities. JPA focuses on relational databases. And like JDO, JPA is extremely easy to use.

Note

OpenJPA typically stores data in relational databases, but can be customized for use with non-relational datastores as well.

JPA is not ideal for every application. For many applications, though, it provides an exciting alternative to other persistence mechanisms.

Chapter 3.  Java Persistence API Architecture

Table of Contents

1. JPA Exceptions

The diagram below illustrates the relationships between the primary components of the JPA architecture.

Note

A number of the depicted interfaces are only required outside of an EJB3-compliant application server. In an application server, EntityManager instances are typically injected, rendering the EntityManagerFactory unnecessary. Also, transactions within an application server are handled using standard application server transaction controls. Thus, the EntityTransaction also goes unused.

  • Persistence: The javax.persistence.Persistence class contains static helper methods to obtain EntityManagerFactory instances in a vendor-neutral fashion.

  • EntityManagerFactory: The javax.persistence.EntityManagerFactory class is a factory for EntityManager s.

  • EntityManager : The javax.persistence.EntityManager is the primary JPA interface used by applications. Each EntityManager manages a set of persistent objects, and has APIs to insert new objects and delete existing ones. When used outside the container, there is a one-to-one relationship between an EntityManager and an EntityTransaction. EntityManagers also act as factories for Query instances.

  • Entity : Entities are persistent objects that represent datastore records.

  • EntityTransaction: Each EntityManager has a one-to-one relation with a single javax.persistence.EntityTransaction. EntityTransactions allow operations on persistent data to be grouped into units of work that either completely succeed or completely fail, leaving the datastore in its original state. These all-or-nothing operations are important for maintaining data integrity.

  • Query : The javax.persistence.Query interface is implemented by each JPA vendor to find persistent objects that meet certain criteria. JPA standardizes support for queries using both the Java Persistence Query Language (JPQL) and the Structured Query Language (SQL). You obtain Query instances from an EntityManager.

The example below illustrates how the JPA interfaces interact to execute a JPQL query and update persistent objects. The example assumes execution outside a container.

Example 3.1.  Interaction of Interfaces Outside Container

  // get an EntityManagerFactory using the Persistence class
  // It is not recommended to obtain a factory often, as construction of a
  // factory is a costly operation. Typically you will like to cache
  // a factory and then refer it for repeated use
  EntityManagerFactory factory = Persistence.createEntityManagerFactory(null);

  // get an EntityManager from the factory
  EntityManager em = factory.createEntityManager();

  // Begin a transaction
  em.getTransaction().begin();

  // query for all employees who work in our research division
  // and put in over 40 hours a week average
  Query query = em.createQuery("SELECT e " +
                               "  FROM Employee e " +
                               " WHERE e.division.name = 'Research' " +
                               "   AND e.avgHours > 40");
  List results = query.getResultList();

  // give all those hard-working employees a raise
  for (Object res : results) {
    Employee emp = (Employee) res;
    emp.setSalary(emp.getSalary() * 1.1);
  }

  // commit will detect all updated entities and save them in database
  em.getTransaction().commit();

  // free the resources
  em.close();

Within a container, the EntityManager will be injected and transactions will be handled declaratively. Thus, the in-container version of the example consists entirely of business logic:

Example 3.2.  Interaction of Interfaces Inside Container

// query for all employees who work in our research division
// and put in over 40 hours a week average - note that the EntityManager em
// is injected using a @Resource annotation
Query query = em.createQuery("select e from Employee e where "
    + "e.division.name = 'Research' and e.avgHours > 40");
List results = query.getResultList();

// give all those hard-working employees a raise
for (Object res : results) {
    emp = (Employee) res;
    emp.setSalary(emp.getSalary() * 1.1);
}

The remainder of this document explores the JPA interfaces in detail. We present them in roughly the order that you will use them as you develop your application.

1.  JPA Exceptions

The diagram above depicts the JPA exception architecture. All exceptions are unchecked. JPA uses standard exceptions where appropriate, most notably IllegalArgumentExceptions and IllegalStateExceptions. The specification also provides a few JPA-specific exceptions in the javax.persistence package. These exceptions should be self-explanatory. See the Javadoc for additional details on JPA exceptions.

Note

All exceptions thrown by OpenJPA implement org.apache.openjpa.util.ExceptionInfo to provide you with additional error information.

Chapter 4.  Entity

JPA recognizes two types of persistent classes: entity classes and embeddable classes. Each persistent instance of an entity class - each entity - represents a unique datastore record. You can use the EntityManager to find an entity by its persistent identity (covered later in this chapter), or use a Query to find entities matching certain criteria.

An instance of an embeddable class, on the other hand, is only stored as part of a separate entity. Embeddable instances have no persistent identity, and are never returned directly from the EntityManager or from a Query unless the query uses a projection on owning class to the embedded instance. For example, if Address is embedded in Company, then a query "SELECT a FROM Address a" will never return the embedded Address of Company; but a projection query such as "SELECT c.address FROM Company c" will.

Despite these differences, there are few distinctions between entity classes and embeddable classes. In fact, writing either type of persistent class is a lot like writing any other class. There are no special parent classes to extend from, field types to use, or methods to write. This is one important way in which JPA makes persistence transparent to you, the developer.

Note

JPA supports both fields and JavaBean properties as persistent state. For simplicity, however, we will refer to all persistent state as persistent fields, unless we want to note a unique aspect of persistent properties.

Example 4.1.  Persistent Class

package org.mag;

/**
 * Example persistent class.  Notice that it looks exactly like any other
 * class.  JPA makes writing persistent classes completely transparent.
 */
public class Magazine {

    private String isbn;
    private String title;
    private Set articles = new HashSet();
    private Article coverArticle;
    private int copiesSold;
    private double price;
    private Company publisher;
    private int version;

    protected Magazine() {
    }

    public Magazine(String title, String isbn) {
        this.title = title;
        this.isbn = isbn;
    }

    public void publish(Company publisher, double price) {
        this.publisher = publisher;
        publisher.addMagazine(this);
        this.price = price;
    }

    public void sell() {
        copiesSold++;
        publisher.addRevenue(price);
    }

    public void addArticle(Article article) {
        articles.add(article);
    }

    // rest of methods omitted
}

1.  Restrictions on Persistent Classes

There are very few restrictions placed on persistent classes. Still, it never hurts to familiarize yourself with exactly what JPA does and does not support.

1.1.  Default or No-Arg Constructor

The JPA specification requires that all persistent classes have a no-arg constructor. This constructor may be public or protected. Because the compiler automatically creates a default no-arg constructor when no other constructor is defined, only classes that define constructors must also include a no-arg constructor.

Note

OpenJPA's enhancer will automatically add a protected no-arg constructor to your class when required. Therefore, this restriction does not apply when using the enhancer. See Section 2, “ Enhancement ” of the Reference Guide for details.

1.2.  Final

Entity classes may not be final. No method of an entity class can be final.

Note

OpenJPA supports final classes and final methods.

1.3.  Identity Fields

All entity classes must declare one or more fields which together form the persistent identity of an instance. These are called identity or primary key fields. In our Magazine class, isbn and title are identity fields, because no two magazine records in the datastore can have the same isbn and title values. Section 2.3, “ Id ” will show you how to denote your identity fields in JPA metadata. Section 2, “ Entity Identity ” below examines persistent identity.

Note

OpenJPA fully supports identity fields, but does not require them. See Section 4, “ Object Identity ” of the Reference Guide for details.

1.4.  Version Field

The version field in our Magazine class may seem out of place. JPA uses a version field in your entities to detect concurrent modifications to the same datastore record. When the JPA runtime detects an attempt to concurrently modify the same record, it throws an exception to the transaction attempting to commit last. This prevents overwriting the previous commit with stale data.

A version field is not required, but without one concurrent threads or processes might succeed in making conflicting changes to the same record at the same time. This is unacceptable to most applications. Section 2.6, “ Version ” shows you how to designate a version field in JPA metadata.

The version field must be an integral type ( int, Long, etc) or a java.sql.Timestamp. You should consider version fields immutable. Changing the field value has undefined results.

Note

OpenJPA fully supports version fields, but does not require them within the actual entity for concurrency detection. OpenJPA can maintain surrogate version values or use state comparisons to detect concurrent modifications. See Section 7, “ Additional JPA Mappings ” in the Reference Guide.

1.5.  Inheritance

JPA fully supports inheritance in persistent classes. It allows persistent classes to inherit from non-persistent classes, persistent classes to inherit from other persistent classes, and non-persistent classes to inherit from persistent classes. It is even possible to form inheritance hierarchies in which persistence skips generations. There are, however, a few important limitations:

  • Persistent classes cannot inherit from certain natively-implemented system classes such as java.net.Socket and java.lang.Thread.

  • If a persistent class inherits from a non-persistent class, the fields of the non-persistent superclass cannot be persisted.

  • All classes in an inheritance tree must use the same identity type. We cover entity identity in Section 2, “ Entity Identity ”.

1.6.  Persistent Fields

JPA manages the state of all persistent fields. Before you access persistent state, the JPA runtime makes sure that it has been loaded from the datastore. When you set a field, the runtime records that it has changed so that the new value will be persisted. This allows you to treat the field in exactly the same way you treat any other field - another aspect of JPA's transparency.

JPA does not support static or final fields. It does, however, include built-in support for most common field types. These types can be roughly divided into three categories: immutable types, mutable types, and relations.

Immutable types, once created, cannot be changed. The only way to alter a persistent field of an immutable type is to assign a new value to the field. JPA supports the following immutable types:

  • All primitives (int, float, byte, etc)

  • All primitive wrappers (java.lang.Integer, java.lang.Float, java.lang.Byte, etc)

  • java.lang.String

  • java.math.BigInteger

  • java.math.BigDecimal

  • java.time.LocalDate

  • java.time.LocalDateTime

  • java.time.LocalTime

  • java.time.OffsetDateTime

  • java.time.OffsetTime

JPA also supports byte[], Byte[], char[], and Character[] as immutable types. That is, you can persist fields of these types, but you should not manipulate individual array indexes without resetting the array into the persistent field.

Persistent fields of mutable types can be altered without assigning the field a new value. Mutable types can be modified directly through their own methods. The JPA specification requires that implementations support the following mutable field types:

  • java.util.Date

  • java.util.Calendar

  • java.sql.Date

  • java.sql.Timestamp

  • java.sql.Time

  • Enums

  • Entity types (relations between entities)

  • Embeddable types

  • java.util.Collections of entities

  • java.util.Sets of entities

  • java.util.Lists of entities

  • java.util.Maps in which each entry maps the value of one of a related entity's fields to that entity.

Collection and map types may be parameterized.

Most JPA implementations also have support for persisting serializable values as binary data in the datastore. Chapter 5, Metadata has more information on persisting serializable types.

Note

OpenJPA also supports arrays, java.lang.Number, java.util.Locale, all JDK 1.2 Set, List, and Map types, and many other mutable and immutable field types. OpenJPA also allows you to plug in support for custom types.

1.7.  Conclusions

This section detailed all of the restrictions JPA places on persistent classes. While it may seem like we presented a lot of information, you will seldom find yourself hindered by these restrictions in practice. Additionally, there are often ways of using JPA's other features to circumvent any limitations you run into.

2.  Entity Identity

Java recognizes two forms of object identity: numeric identity and qualitative identity. If two references are numerically identical, then they refer to the same JVM instance in memory. You can test for this using the == operator. Qualitative identity, on the other hand, relies on some user-defined criteria to determine whether two objects are "equal". You test for qualitative identity using the equals method. By default, this method simply relies on numeric identity.

JPA introduces another form of object identity, called entity identity or persistent identity. Entity identity tests whether two persistent objects represent the same state in the datastore.

The entity identity of each persistent instance is encapsulated in its identity field(s). If two entities of the same type have the same identity field values, then the two entities represent the same state in the datastore. Each entity's identity field values must be unique among all other entities of the same type.

Identity fields must be primitives, primitive wrappers, Strings, Dates, Timestamps, or embeddable types.

Note

OpenJPA supports entities as identity fields, as the Reference Guide discusses in Section 4.2, “ Entities as Identity Fields ”. For legacy schemas with binary primary key columns, OpenJPA also supports using identity fields of type byte[]. When you use a byte[] identity field, you must create an identity class. Identity classes are covered below.

Warning

Changing the fields of an embeddable instance while it is assigned to an identity field has undefined results. Always treat embeddable identity instances as immutable objects in your applications.

If you are dealing with a single persistence context (see Section 3, “ Persistence Context ”), then you do not have to compare identity fields to test whether two entity references represent the same state in the datastore. There is a much easier way: the == operator. JPA requires that each persistence context maintain only one JVM object to represent each unique datastore record. Thus, entity identity is equivalent to numeric identity within a persistence context. This is referred to as the uniqueness requirement.

The uniqueness requirement is extremely important - without it, it would be impossible to maintain data integrity. Think of what could happen if two different objects in the same transaction were allowed to represent the same persistent data. If you made different modifications to each of these objects, which set of changes should be written to the datastore? How would your application logic handle seeing two different "versions" of the same data? Thanks to the uniqueness requirement, these questions do not have to be answered.

2.1.  Identity Class

If your entity has only one identity field, you can use the value of that field as the entity's identity object in all EntityManager APIs. Otherwise, you must supply an identity class to use for identity objects. Your identity class must meet the following criteria:

  • The class must be public.

  • The class must be serializable.

  • The class must have a public no-args constructor.

  • The names of the non-static fields or properties of the class must be the same as the names of the identity fields or properties of the corresponding entity class, and the types must be identical.

  • The equals and hashCode methods of the class must use the values of all fields or properties corresponding to identity fields or properties in the entity class.

  • If the class is an inner class, it must be static.

  • All entity classes related by inheritance must use the same identity class, or else each entity class must have its own identity class whose inheritance hierarchy mirrors the inheritance hierarchy of the owning entity classes (see Section 2.1.1, “ Identity Hierarchies ”).

Note

Though you may still create identity classes by hand, OpenJPA provides the appidtool to automatically generate proper identity classes based on your identity fields. See Section 4.3, “ Application Identity Tool ” of the Reference Guide.

Example 4.2.  Identity Class

This example illustrates a proper identity class for an entity with multiple identity fields.

/**
 * Persistent class using application identity.
 */
public class Magazine {

    private String isbn;    // identity field
    private String title;   // identity field

    // rest of fields and methods omitted


    /**
     * Application identity class for Magazine.
     */
    public static class MagazineId {

        // each identity field in the Magazine class must have a
        // corresponding field in the identity class
        public String isbn;
        public String title;

        /**
         * Equality must be implemented in terms of identity field
         * equality, and must use instanceof rather than comparing
         * classes directly (some JPA implementations may subclass the
         * identity class).
         */
        public boolean equals(Object other) {
            if (other == this)
                return true;
            if (!(other instanceof MagazineId))
                return false;

            MagazineId mi = (MagazineId) other;
            return (isbn == mi.isbn
                || (isbn != null && isbn.equals(mi.isbn)))
                && (title == mi.title
                || (title != null && title.equals(mi.title)));
        }

        /**
         * Hashcode must also depend on identity values.
         */
        public int hashCode() {
            return ((isbn == null) ? 0 : isbn.hashCode())
                ^ ((title == null) ? 0 : title.hashCode());
        }

        public String toString() {
            return isbn + ":" + title;
        }
    }
}

2.1.1.  Identity Hierarchies

An alternative to having a single identity class for an entire inheritance hierarchy is to have one identity class per level in the inheritance hierarchy. The requirements for using a hierarchy of identity classes are as follows:

  • The inheritance hierarchy of identity classes must exactly mirror the hierarchy of the persistent classes that they identify. In the example pictured above, abstract class Person is extended by abstract class Employee, which is extended by non-abstract class FullTimeEmployee, which is extended by non-abstract class Manager. The corresponding identity classes, then, are an abstract PersonId class, extended by an abstract EmployeeId class, extended by a non-abstract FullTimeEmployeeId class, extended by a non-abstract ManagerId class.

  • Subclasses in the identity hierarchy may define additional identity fields until the hierarchy becomes non-abstract. In the aforementioned example, Person defines an identity field ssn, Employee defines additional identity field userName , and FullTimeEmployee adds a final identity field, empId. However, Manager may not define any additional identity fields, since it is a subclass of a non-abstract class. The hierarchy of identity classes, of course, must match the identity field definitions of the persistent class hierarchy.

  • It is not necessary for each abstract class to declare identity fields. In the previous example, the abstract Person and Employee classes could declare no identity fields, and the first concrete subclass FullTimeEmployee could define one or more identity fields.

  • All subclasses of a concrete identity class must be equals and hashCode-compatible with the concrete superclass. This means that in our example, a ManagerId instance and a FullTimeEmployeeId instance with the same identity field values should have the same hash code, and should compare equal to each other using the equals method of either one. In practice, this requirement reduces to the following coding practices:

    1. Use instanceof instead of comparing Class objects in the equals methods of your identity classes.

    2. An identity class that extends another non-abstract identity class should not override equals or hashCode.

3.  Lifecycle Callbacks

It is often necessary to perform various actions at different stages of a persistent object's lifecycle. JPA includes a variety of callbacks methods for monitoring changes in the lifecycle of your persistent objects. These callbacks can be defined on the persistent classes themselves and on non-persistent listener classes.

3.1.  Callback Methods

Every persistence event has a corresponding callback method marker. These markers are shared between persistent classes and their listeners. You can use these markers to designate a method for callback either by annotating that method or by listing the method in the XML mapping file for a given class. The lifecycle events and their corresponding method markers are:

  • PrePersist: Methods marked with this annotation will be invoked before an object is persisted. This could be used for assigning primary key values to persistent objects. This is equivalent to the XML element tag pre-persist.

  • PostPersist: Methods marked with this annotation will be invoked after an object has transitioned to the persistent state. You might want to use such methods to update a screen after a new row is added. This is equivalent to the XML element tag post-persist.

  • PostLoad: Methods marked with this annotation will be invoked after all eagerly fetched fields of your class have been loaded from the datastore. No other persistent fields can be accessed in this method. This is equivalent to the XML element tag post-load.

    PostLoad is often used to initialize non-persistent fields whose values depend on the values of persistent fields, such as a complex data structure.

  • PreUpdate: Methods marked with this annotation will be invoked just the persistent values in your objects are flushed to the datastore. This is equivalent to the XML element tag pre-update.

    PreUpdate is the complement to PostLoad . While methods marked with PostLoad are most often used to initialize non-persistent values from persistent data, methods annotated with PreUpdate is normally used to set persistent fields with information cached in non-persistent data.

  • PostUpdate: Methods marked with this annotation will be invoked after changes to a given instance have been stored to the datastore. This is useful for clearing stale data cached at the application layer. This is equivalent to the XML element tag post-update.

  • PreRemove: Methods marked with this annotation will be invoked before an object transactions to the deleted state. Access to persistent fields is valid within this method. You might use this method to cascade the deletion to related objects based on complex criteria, or to perform other cleanup. This is equivalent to the XML element tag pre-remove.

  • PostRemove: Methods marked with this annotation will be invoked after an object has been marked as to be deleted. This is equivalent to the XML element tag post-remove.

3.2.  Using Callback Methods

When declaring callback methods on a persistent class, any method may be used which takes no arguments and is not shared with any property access fields. Multiple events can be assigned to a single method as well.

Below is an example of how to declare callback methods on persistent classes:

/**
 * Example persistent class declaring our entity listener.
 */
@Entity
public class Magazine {

    @Transient
    private byte[][] data;

    @ManyToMany
    private List<Photo> photos;

    @PostLoad
    public void convertPhotos() {
        data = new byte[photos.size()][];
        for (int i = 0; i < photos.size(); i++)
            data[i] = photos.get(i).toByteArray();
    }

    @PreDelete
    public void logMagazineDeletion() {
        getLog().debug("deleting magazine containing" + photos.size()
            + " photos.");
    }
}

In an XML mapping file, we can define the same methods without annotations:

<entity class="Magazine">
    <pre-remove>logMagazineDeletion</pre-remove>
    <post-load>convertPhotos</post-load>
</entity>

Note

We fully explore persistence metadata annotations and XML in Chapter 5, Metadata .

3.3.  Using Entity Listeners

Mixing lifecycle event code into your persistent classes is not always ideal. It is often more elegant to handle cross-cutting lifecycle events in a non-persistent listener class. JPA allows for this, requiring only that listener classes have a public no-arg constructor. Like persistent classes, your listener classes can consume any number of callbacks. The callback methods must take in a single java.lang.Object argument which represents the persistent object that triggered the event.

Entities can enumerate listeners using the EntityListeners annotation. This annotation takes an array of listener classes as its value.

Below is an example of how to declare an entity and its corresponding listener classes.

/**
 * Example persistent class declaring our entity listener.
 */
@Entity
@EntityListeners({ MagazineLogger.class, ... })
public class Magazine {

    // ... //
}


/**
 * Example entity listener.
 */
public class MagazineLogger {

    @PostPersist
    public void logAddition(Object pc) {
        getLog().debug("Added new magazine:" + ((Magazine) pc).getTitle());
    }


    @PreRemove
    public void logDeletion(Object pc) {
        getLog().debug("Removing from circulation:" +
            ((Magazine) pc).getTitle());
    }
}

In XML, we define both the listeners and their callback methods as so:

<entity class="Magazine">
    <entity-listeners>
        <entity-listener class="MagazineLogger">
            <post-persist>logAddition</post-persist>
            <pre-remove>logDeletion</pre-remove>
        </entity-listener>
    </entity-listeners>
</entity>

3.4.  Entity Listeners Hierarchy

Entity listener methods are invoked in a specific order when a given event is fired. So-called default listeners are invoked first: these are listeners which have been defined in a package annotation or in the root element of XML mapping files. Next, entity listeners are invoked in the order of the inheritance hierarchy, with superclass listeners being invoked before subclass listeners. Finally, if an entity has multiple listeners for the same event, the listeners are invoked in declaration order.

You can exclude default listeners and listeners defined in superclasses from the invocation chain through the use of two class-level annotations:

  • ExcludeDefaultListeners: This annotation indicates that no default listeners will be invoked for this class, or any of its subclasses. The XML equivalent is the empty exclude-default-listeners element.

  • ExcludeSuperclassListeners: This annotation will cause OpenJPA to skip invoking any listeners declared in superclasses. The XML equivalent is the empty exclude-superclass-listeners element.

4.  Conclusions

This chapter covered everything you need to know to write persistent class definitions in JPA. JPA cannot use your persistent classes, however, until you complete one additional step: you must define the persistence metadata. The next chapter explores metadata in detail.

Chapter 5.  Metadata

JPA requires that you accompany each persistent class with persistence metadata. This metadata serves three primary purposes:

  1. To identify persistent classes.

  2. To override default JPA behavior.

  3. To provide the JPA implementation with information that it cannot glean from simply reflecting on the persistent class.

Persistence metadata is specified using either the Java annotations defined in the javax.persistence package, XML mapping files, or a mixture of both. In the latter case, XML declarations override conflicting annotations. If you choose to use XML metadata, the XML files must be available at development and runtime, and must be discoverable via either of two strategies:

  1. In a resource named orm.xml placed in a META-INF directory within a directory in your classpath or within a jar archive containing your persistent classes.

  2. Declared in your persistence.xml configuration file. In this case, each XML metadata file must be listed in a mapping-file element whose content is either a path to the given file or a resource location available to the class' class loader.

We describe the standard metadata annotations and XML equivalents throughout this chapter. The full schema for XML mapping files is available in Section 3, “ XML Schema ”. JPA also standardizes relational mapping metadata and named query metadata, which we discuss in Chapter 13, Mapping Metadata and Section 1.11, “ Named Queries ” respectively.

Note

OpenJPA defines many useful annotations beyond the standard set. See Section 3, “ Additional JPA Metadata ” and Section 4, “ Metadata Extensions ” in the Reference Guide for details. There are currently no XML equivalents for these extension annotations.

Note

Persistence metadata may be used to validate the contents of your entities prior to communicating with the database. This differs from mapping meta data which is primarily used for schema generation. For example if you indicate that a relationship is not optional (e.g. @Basic(optional=false)) OpenJPA will validate that the variable in your entity is not null before inserting a row in the database.

Through the course of this chapter, we will create the persistent object model above.

1.  Class Metadata

The following metadata annotations and XML elements apply to persistent class declarations.

1.1.  Entity

The Entity annotation denotes an entity class. All entity classes must have this annotation. The Entity annotation takes one optional property:

  • String name: Name used to refer to the entity in queries. Must not be a reserved literal in JPQL. Defaults to the unqualified name of the entity class.

The equivalent XML element is entity. It has the following attributes:

  • class: The entity class. This attribute is required.

  • name: Named used to refer to the class in queries. See the name property above.

  • access: The access type to use for the class. Must either be FIELD or PROPERTY. For details on access types, see Section 2, “ Field and Property Metadata ”.

Note

OpenJPA uses a process called enhancement to modify the bytecode of entities for transparent lazy loading and immediate dirty tracking. See Section 2, “ Enhancement ” in the Reference Guide for details on enhancement.

1.2.  Id Class

As we discussed in Section 2.1, “ Identity Class ”, entities with multiple identity fields must use an identity class to encapsulate their persistent identity. The IdClass annotation specifies this class. It accepts a single java.lang.Class value.

The equivalent XML element is id-class, which has a single attribute:

  • class: Set this required attribute to the name of the identity class.

1.3.  Mapped Superclass

A mapped superclass is a non-entity class that can define persistent state and mapping information for entity subclasses. Mapped superclasses are usually abstract. Unlike true entities, you cannot query a mapped superclass, pass a mapped superclass instance to any EntityManager or Query methods, or declare a persistent relation with a mapped superclass target. You denote a mapped superclass with the MappedSuperclass marker annotation.

The equivalent XML element is mapped-superclass. It expects the following attributes:

Note

OpenJPA allows you to query on mapped superclasses. A query on a mapped superclass will return all matching subclass instances. OpenJPA also allows you to declare relations to mapped superclass types; however, you cannot query across these relations.

1.4.  Embeddable

The Embeddable annotation designates an embeddable persistent class. Embeddable instances are stored as part of the record of their owning instance. All embeddable classes must have this annotation.

A persistent class can either be an entity or an embeddable class, but not both.

The equivalent XML element is embeddable. It understands the following attributes:

Note

OpenJPA allows a persistent class to be both an entity and an embeddable class. Instances of the class will act as entities when persisted explicitly or assigned to non-embedded fields of entities. Instances will act as embedded values when assigned to embedded fields of entities.

To signal that a class is both an entity and an embeddable class in OpenJPA, simply add both the @Entity and the @Embeddable annotations to the class.

1.5.  EntityListeners

An entity may list its lifecycle event listeners in the EntityListeners annotation. This value of this annotation is an array of the listener Class es for the entity. The equivalent XML element is entity-listeners. For more details on entity listeners, see Section 3, “ Lifecycle Callbacks ”.

1.6.  Example

Here are the class declarations for our persistent object model, annotated with the appropriate persistence metadata. Note that Magazine declares an identity class, and that Document and Address are a mapped superclass and an embeddable class, respectively. LifetimeSubscription and TrialSubscription override the default entity name to supply a shorter alias for use in queries.

Example 5.1.  Class Metadata

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
public class Magazine {
    ...

    public static class MagazineId {
        ...
    }
}

@Entity
public class Article {
    ...
}


package org.mag.pub;

@Entity
public class Company {
    ...
}

@Entity
public class Author {
    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {
    ...
}

@Entity
public class Contract
    extends Document {
    ...
}

@Entity
public class Subscription {
    ...

    @Entity
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {
    ...
}

The equivalent declarations in XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        ...
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <id-class class="org.mag.Magazine$MagazineId"/>
        ...
    </entity>
    <entity class="org.mag.Article">
        ...
    </entity>
    <entity class="org.mag.pub.Company">
        ...
    </entity>
    <entity class="org.mag.pub.Author">
        ...
    </entity>
    <entity class="org.mag.subscribe.Contract">
        ...
    </entity>
    <entity class="org.mag.subscribe.LineItem">
        ...
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        ...
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        ...
    </entity>
    <embeddable class="org.mag.pub.Address">
        ...
    </embeddable>
</entity-mappings>

2.  Field and Property Metadata

The persistence implementation must be able to retrieve and set the persistent state of your entities, mapped superclasses, and embeddable types. JPA offers two modes of persistent state access: field access, and property access. The access type of a persistent attribute can be either set explicitly on a class or attribute level, inherited, or determined by the provider.

Under field access, the implementation injects state directly into your persistent fields, and retrieves changed state from your fields as well. To declare field access on an entire entity with XML metadata, set the access attribute of your entity XML element to FIELD. To use field access for an entire entity using annotation metadata, simply place your metadata and mapping annotations on your field declarations:

@ManyToOne
private Company publisher;

Property access, on the other hand, retrieves and loads state through JavaBean "getter" and "setter" methods. For a property p of type T, you must define the following getter method:

T getP();

For boolean properties, this is also acceptable:

boolean isP();

You must also define the following setter method:

void setP(T value);

To implicitly use property access for an entire class by default, set your entity element's access attribute to PROPERTY, or place your metadata and mapping annotations on the getter method:

@ManyToOne
private Company getPublisher() { ... }

private void setPublisher(Company publisher) { ... }

2.1.  Explicit Access

The access type of a class or individual persistent attributes can be specified explicitly using the @Access annotation or access attribute on the XML elements used to define persistent attributes. When explicitly defining access, specify the explicit access type for the class and then apply the @Access annotation or access XML attribute to individual fields or properties. If explicit FIELD or PROPERTY is specified at the class level, all eligible non-transient fields or properties will be persistent. If using class level FIELD access, non-persistent fields must be transient or annotated with @Transient. If using class level PROPERTY access, non-persistent properties must be annotated @Transient or excluded using the transient XML attribute. Refer to the JPA specification for specific rules regarding the use of explicit access with embeddables and within an inheritance hierarchy.

This entity definitions shows how multiple access types may be specified on an entity:

@Entity
@Access(AccessType.FIELD)
public class PaymentContract {

    @Id
    private String id;

    @Temporal(TemporalType.DATE)
    private String contractDate;

    @Transient
    private String terms;

    @Version
    private int version;

    @Lob
    @Access(AccessType.PROPERTY)
    public String getContractTerms() {
        return terms;
    }

    public void setContractTerms(String terms) {
        // Format string before persisting
        this.terms = formatTerms(terms);
    }
    ...
}

The equivalent declarations in XML:

<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd"
    version="2.2">
    <entity class="org.xyz.PaymentContract" access="FIELD">
        <attributes>
            <id name="id"/>
             <basic name="contractTerms" access="PROPERTY">
                <lob/>
            </basic>
            <basic name="contractDate">
                <temporal>DATE</temporal>
            </basic>
            <version name="version"/>
            <transient name="terms"/>
       </attributes>
    </entity>
</entity-mappings>

Warning

When using property access, only the getter and setter method for a property should ever access the underlying persistent field directly. Other methods, including internal business methods in the persistent class, should go through the getter and setter methods when manipulating persistent state.

Also, take care when adding business logic to your getter and setter methods. Consider that they are invoked by the persistence implementation to load and retrieve all persistent state; other side effects might not be desirable.

The remainder of this document uses the term "persistent field" to refer to either a persistent field or a persistent property.

2.2.  Transient

The Transient annotation specifies that a field is non-persistent. Use it to exclude fields from management that would otherwise be persistent. Transient is a marker annotation only; it has no properties.

The equivalent XML element is transient. It has a single attribute:

  • name: The transient field or property name. This attribute is required.

2.3.  Id

Annotate your simple identity fields with Id. This annotation has no properties. We explore entity identity and identity fields in Section 1.3, “ Identity Fields ”.

The equivalent XML element is id. It has one required attribute:

  • name: The name of the identity field or property.

2.4.  Generated Value

The previous section showed you how to declare your identity fields with the Id annotation. It is often convenient to allow the persistence implementation to assign a unique value to your identity fields automatically. JPA includes the GeneratedValue annotation for this purpose. It has the following properties:

  • GenerationType strategy: Enum value specifying how to auto-generate the field value. The GenerationType enum has the following values:

    • GeneratorType.AUTO: The default. Assign the field a generated value, leaving the details to the JPA vendor.

    • GenerationType.IDENTITY: The database will assign an identity value on insert.

    • GenerationType.SEQUENCE: Use a datastore sequence to generate a field value.

    • GenerationType.TABLE: Use a sequence table to generate a field value.

  • String generator: The name of a generator defined in mapping metadata. We show you how to define named generators in Section 5, “ Generators ”. If the GenerationType is set but this property is unset, the JPA implementation uses appropriate defaults for the selected generation type.

The equivalent XML element is generated-value, which includes the following attributes:

  • strategy: One of TABLE, SEQUENCE, IDENTITY, or AUTO, defaulting to AUTO.

  • generator: Equivalent to the generator property listed above.

Note

OpenJPA allows you to use the GeneratedValue annotation on any field, not just identity fields. Before using the IDENTITY generation strategy, however, read Section 4.4, “ Autoassign / Identity Strategy Caveats ” in the Reference Guide.

OpenJPA also offers additional generator strategies for non-numeric fields, which you can access by setting strategy to AUTO (the default), and setting the generator string to:

  • uuid-string: OpenJPA will generate a 128-bit type 1 UUID unique within the network, represented as a 16-character string. For more information on UUIDs, see the IETF UUID draft specification at: http://www.ics.uci.edu/~ejw/authoring/uuid-guid/

  • uuid-hex: Same as uuid-string, but represents the type 1 UUID as a 32-character hexadecimal string.

  • uuid-type4-string: OpenJPA will generate a 128-bit type 4 pseudo-random UUID, represented as a 16-character string. For more information on UUIDs, see the IETF UUID draft specification at: http://www.ics.uci.edu/~ejw/authoring/uuid-guid/

  • uuid-type4-hex: Same as uuid-type4-string , but represents the type 4 UUID as a 32-character hexadecimal string.

These string constants are defined in org.apache.openjpa.persistence.Generator.

If the entities are mapped to the same table name but with different schema name within one PersistenceUnit intentionally, and the strategy of GeneratedType.AUTO is used to generate the ID for each entity, a schema name for each entity must be explicitly declared either through the annotation or the mapping.xml file. Otherwise, the mapping tool only creates the tables for those entities with the schema names under each schema. In addition, there will be only one OPENJPA_SEQUENCE_TABLE created for all the entities within the PersistenceUnit if the entities are not identified with the schema name. Read Section 6, “ Generators ” and Section 11, “ Default Schema ” in the Reference Guide.

2.5.  Embedded Id

If your entity has multiple identity values, you may declare multiple @Id fields, or you may declare a single @EmbeddedId field. The type of a field annotated with EmbeddedId must be an embeddable entity class. The fields of this embeddable class are considered the identity values of the owning entity. We explore entity identity and identity fields in Section 1.3, “ Identity Fields ”.

The EmbeddedId annotation has no properties.

The equivalent XML element is embedded-id. It has one required attribute:

  • name: The name of the identity field or property.

2.6.  Version

Use the Version annotation to designate a version field. Section 1.4, “ Version Field ” explained the importance of version fields to JPA. This is a marker annotation; it has no properties.

The equivalent XML element is version, which has a single attribute:

  • name: The name of the version field or property. This attribute is required.

2.7.  Basic

Basic signifies a standard value persisted as-is to the datastore. You can use the Basic annotation on persistent fields of the following types: primitives, primitive wrappers, java.lang.String, byte[], Byte[], char[], Character[], java.math.BigDecimal, java.math.BigInteger, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Timestamp, java.sql.Time, Enums, and Serializable types.

Since JPA 2.2 the following java.time Types are also supported: java.time.LocalDate, java.time.LocalDateTime, java.time.LocalTime, java.time.LocalOffsetTime and java.time.OffsetDateTime.

Basic declares these properties:

  • FetchType fetch: Whether to load the field eagerly (FetchType.EAGER) or lazily ( FetchType.LAZY). Defaults to FetchType.EAGER.

  • boolean optional: Whether the datastore allows null values. Defaults to true.

The equivalent XML element is basic. It has the following attributes:

  • name: The name of the field or property. This attribute is required.

  • fetch: One of EAGER or LAZY .

  • optional: Boolean indicating whether the field value may be null.

2.7.1.  Fetch Type

Many metadata annotations in JPA have a fetch property. This property can take on one of two values: FetchType.EAGER or FetchType.LAZY. FetchType.EAGER means that the field is loaded by the JPA implementation before it returns the persistent object to you. Whenever you retrieve an entity from a query or from the EntityManager, you are guaranteed that all of its eager fields are populated with datastore data.

FetchType.LAZY is a hint to the JPA runtime that you want to defer loading of the field until you access it. This is called lazy loading. Lazy loading is completely transparent; when you attempt to read the field for the first time, the JPA runtime will load the value from the datastore and populate the field automatically. Lazy loading is only a hint and not a directive because some JPA implementations cannot lazy-load certain field types.

With a mix of eager and lazily-loaded fields, you can ensure that commonly-used fields load efficiently, and that other state loads transparently when accessed. As you will see in Section 3, “ Persistence Context ”, you can also use eager fetching to ensure that entities have all needed data loaded before they become detached at the end of a persistence context.

Note

OpenJPA can lazy-load any field type. OpenJPA also allows you to dynamically change which fields are eagerly or lazily loaded at runtime. See Section 7, “ Fetch Groups ” in the Reference Guide for details.

The Reference Guide details OpenJPA's eager fetching behavior in Section 8, “ Eager Fetching ”.

2.8.  Embedded

Use the Embedded marker annotation on embeddable field types. Embedded fields are mapped as part of the datastore record of the declaring entity. In our sample model, Author and Company each embed their Address, rather than forming a relation to an Address as a separate entity.

The equivalent XML element is embedded, which expects a single attribute:

  • name: The name of the field or property. This attribute is required.

2.9.  Many To One

When an entity A references a single entity B, and other As might also reference the same B, we say there is a many to one relation from A to B. In our sample model, for example, each magazine has a reference to its publisher. Multiple magazines might have the same publisher. We say, then, that the Magazine.publisher field is a many to one relation from magazines to publishers.

JPA indicates many to one relations between entities with the ManyToOne annotation. This annotation has the following properties:

  • Class targetEntity: The class of the related entity type.

  • CascadeType[] cascade: Array of enum values defining cascade behavior for this field. We explore cascades below. Defaults to an empty array.

  • FetchType fetch: Whether to load the field eagerly (FetchType.EAGER) or lazily (FetchType.LAZY). Defaults to FetchType.EAGER. See Section 2.7.1, “ Fetch Type ” above for details on fetch types.

  • boolean optional: Whether the related object must exist. If false, this field cannot be null. Defaults to true.

The equivalent XML element is many-to-one. It accepts the following attributes:

  • name: The name of the field or property. This attribute is required.

  • target-entity: The class of the related type.

  • fetch: One of EAGER or LAZY.

  • optional: Boolean indicating whether the field value may be null.

2.9.1.  Cascade Type

We introduce the JPA EntityManager in Chapter 8, EntityManager . The EntityManager has APIs to persist new entities, remove (delete) existing entities, refresh entity state from the datastore, and merge detached entity state back into the persistence context. We explore all of these APIs in detail later in the overview.

When the EntityManager is performing the above operations, you can instruct it to automatically cascade the operation to the entities held in a persistent field with the cascade property of your metadata annotation. This process is recursive. The cascade property accepts an array of CascadeType enum values.

  • CascadeType.PERSIST: When persisting an entity, also persist the entities held in this field. We suggest liberal application of this cascade rule, because if the EntityManager finds a field that references a new entity during flush, and the field does not use CascadeType.PERSIST, it is an error.

  • CascadeType.REMOVE: When deleting an entity, also delete the entities held in this field.

  • CascadeType.REFRESH: When refreshing an entity, also refresh the entities held in this field.

  • CascadeType.MERGE: When merging entity state, also merge the entities held in this field.

Note

OpenJPA offers enhancements to JPA's CascadeType.REMOVE functionality, including additional annotations to control how and when dependent fields will be removed. See Section 4.2.1, “ Dependent ” for more details.

CascadeType defines one additional value, CascadeType.ALL, that acts as a shortcut for all of the values above. The following annotations are equivalent:

@ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE,
    CascadeType.REFRESH,CascadeType.MERGE})
private Company publisher;
@ManyToOne(cascade=CascadeType.ALL)
private Company publisher;

In XML, these enumeration constants are available as child elements of the cascade element. The cascade element is itself a child of many-to-one. The following examples are equivalent:

<many-to-one name="publisher">
    <cascade>
        <cascade-persist/>
        <cascade-merge/>
        <cascade-remove/>
        <cascade-refresh/>
    </cascade>
</many-to-one>
<many-to-one name="publisher">
    <cascade>
        <cascade-all/>
    </cascade>
</many-to-one>

2.10.  One To Many

When an entity A references multiple B entities, and no two As reference the same B, we say there is a one to many relation from A to B.

One to many relations are the exact inverse of the many to one relations we detailed in the preceding section. In that section, we said that the Magazine.publisher field is a many to one relation from magazines to publishers. Now, we see that the Company.mags field is the inverse - a one to many relation from publishers to magazines. Each company may publish multiple magazines, but each magazine can have only one publisher.

JPA indicates one to many relations between entities with the OneToMany annotation. This annotation has the following properties:

  • Class targetEntity: The class of the related entity type. This information is usually taken from the parameterized collection or map element type. You must supply it explicitly, however, if your field isn't a parameterized type.

  • String mappedBy: Names the many to one field in the related entity that maps this bidirectional relation. We explain bidirectional relations below. Leaving this property unset signals that this is a standard unidirectional relation.

  • CascadeType[] cascade: Array of enum values defining cascade behavior for the collection elements. We explore cascades above in Section 2.9.1, “ Cascade Type ”. Defaults to an empty array.

  • FetchType fetch: Whether to load the field eagerly (FetchType.EAGER) or lazily (FetchType.LAZY). Defaults to FetchType.LAZY. See Section 2.7.1, “ Fetch Type ” above for details on fetch types.

The equivalent XML element is one-to-many, which includes the following attributes:

  • name: The name of the field or property. This attribute is required.

  • target-entity: The class of the related type.

  • fetch: One of EAGER or LAZY.

  • mapped-by: The name of the field or property that owns the relation. See Section 2, “ Field and Property Metadata ”.

You may also nest the cascade element within a one-to-many element.

2.10.1.  Bidirectional Relations

When two fields are logical inverses of each other, they form a bidirectional relation. Our model contains two bidirectional relations: Magazine.publisher and Company.mags form one bidirectional relation, and Article.authors and Author.articles form the other. In both cases, there is a clear link between the two fields that form the relationship. A magazine refers to its publisher while the publisher refers to all its published magazines. An article refers to its authors while each author refers to her written articles.

When the two fields of a bidirectional relation share the same datastore mapping, JPA formalizes the connection with the mappedBy property. Marking Company.mags as mappedBy Magazine.publisher means two things:

  1. Company.mags uses the datastore mapping for Magazine.publisher, but inverses it. In fact, it is illegal to specify any additional mapping information when you use the mappedBy property. All mapping information is read from the referenced field. We explore mapping in depth in Chapter 13, Mapping Metadata .

  2. Magazine.publisher is the "owner" of the relation. The field that specifies the mapping data is always the owner. This means that changes to the Magazine.publisher field are reflected in the datastore, while changes to the Company.mags field alone are not. Changes to Company.mags may still affect the JPA implementation's cache, however. Thus, it is very important that you keep your object model consistent by properly maintaining both sides of your bidirectional relations at all times.

You should always take advantage of the mappedBy property rather than mapping each field of a bidirectional relation independently. Failing to do so may result in the JPA implementation trying to update the database with conflicting data. Be careful to only mark one side of the relation as mappedBy, however. One side has to actually do the mapping!

Note

You can configure OpenJPA to automatically synchronize both sides of a bidirectional relation, or to perform various actions when it detects inconsistent relations. See Section 5, “ Managed Inverses ” in the Reference Guide for details.

2.11.  One To One

When an entity A references a single entity B, and no other As can reference the same B, we say there is a one to one relation between A and B. In our sample model, Magazine has a one to one relation to Article through the Magazine.coverArticle field. No two magazines can have the same cover article.

JPA indicates one to one relations between entities with the OneToOne annotation. This annotation has the following properties:

  • Class targetEntity: The class of the related entity type. This information is usually taken from the field type.

  • String mappedBy: Names the field in the related entity that maps this bidirectional relation. We explain bidirectional relations in Section 2.10.1, “ Bidirectional Relations ” above. Leaving this property unset signals that this is a standard unidirectional relation.

  • CascadeType[] cascade: Array of enum values defining cascade behavior for this field. We explore cascades in Section 2.9.1, “ Cascade Type ” above. Defaults to an empty array.

  • FetchType fetch: Whether to load the field eagerly (FetchType.EAGER) or lazily (FetchType.LAZY). Defaults to FetchType.EAGER. See Section 2.7.1, “ Fetch Type ” above for details on fetch types.

  • boolean optional: Whether the related object must exist. If false, this field cannot be null. Defaults to true.

The equivalent XML element is one-to-one which understands the following attributes:

  • name: The name of the field or property. This attribute is required.

  • target-entity: The class of the related type.

  • fetch: One of EAGER or LAZY.

  • mapped-by: The field that owns the relation. See Section 2, “ Field and Property Metadata ”.

You may also nest the cascade element within a one-to-one element.

2.12.  Many To Many

When an entity A references multiple B entities, and other As might reference some of the same Bs, we say there is a many to many relation between A and B. In our sample model, for example, each article has a reference to all the authors that contributed to the article. Other articles might have some of the same authors. We say, then, that Article and Author have a many to many relation through the Article.authors field.

JPA indicates many to many relations between entities with the ManyToMany annotation. This annotation has the following properties:

  • Class targetEntity: The class of the related entity type. This information is usually taken from the parameterized collection or map element type. You must supply it explicitly, however, if your field isn't a parameterized type.

  • String mappedBy: Names the many to many field in the related entity that maps this bidirectional relation. We explain bidirectional relations in Section 2.10.1, “ Bidirectional Relations ” above. Leaving this property unset signals that this is a standard unidirectional relation.

  • CascadeType[] cascade: Array of enum values defining cascade behavior for the collection elements. We explore cascades above in Section 2.9.1, “ Cascade Type ”. Defaults to an empty array.

  • FetchType fetch: Whether to load the field eagerly (FetchType.EAGER) or lazily (FetchType.LAZY). Defaults to FetchType.LAZY. See Section 2.7.1, “ Fetch Type ” above for details on fetch types.

The equivalent XML element is many-to-many. It accepts the following attributes:

  • name: The name of the field or property. This attribute is required.

  • target-entity: The class of the related type.

  • fetch: One of EAGER or LAZY.

  • mapped-by: The field that owns the relation. See Section 2, “ Field and Property Metadata ”.

You may also nest the cascade element within a many-to-many element.

2.13.  Order By

Datastores such as relational databases do not preserve the order of records. Your persistent List fields might be ordered one way the first time you retrieve an object from the datastore, and a completely different way the next. To ensure consistent ordering of collection fields, you must use the OrderBy annotation. The OrderBy annotation's value is a string defining the order of the collection elements. An empty value means to sort on the identity value(s) of the elements in ascending order. Any other value must be of the form:

<field name>[ ASC|DESC][, ...]

Each <field name> is the name of a persistent field in the collection's element type. You can optionally follow each field by the keyword ASC for ascending order, or DESC for descending order. If the direction is omitted, it defaults to ascending.

The equivalent XML element is order-by which can be listed as a sub-element of the one-to-many or many-to-many elements. The text within this element is parsed as the order by string.

2.14.  Map Key

JPA supports persistent Map fields through either a OneToMany or ManyToMany association. The related entities form the map values. JPA derives the map keys by extracting a field from each entity value. The MapKey annotation designates the field that is used as the key. It has the following properties:

  • String name: The name of a field in the related entity class to use as the map key. If no name is given, defaults to the identity field of the related entity class.

The equivalent XML element is map-key which can be listed as a sub-element of the one-to-many or many-to-many elements. The map-key element has the following attributes:

  • name: The name of the field in the related entity class to use as the map key.

2.15.  Persistent Field Defaults

In the absence of any of the annotations above, JPA defines the following default behavior for declared fields:

  1. Fields declared static, transient, or final default to non-persistent.

  2. Fields of any primitive type, primitive wrapper type, java.lang.String, byte[], Byte[], char[], Character[], java.math.BigDecimal, java.math.BigInteger, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Timestamp, or any Serializable type default to persistent, as if annotated with @Basic.

  3. Fields of an embeddable type default to persistent, as if annotated with @Embedded.

  4. All other fields default to non-persistent.

Note that according to these defaults, all relations between entities must be annotated explicitly. Without an annotation, a relation field will default to serialized storage if the related entity type is serializable, or will default to being non-persistent if not.

3.  XML Schema

The latest revision of the version 2.0 orm schema is presented below. Version 2.0 of the schema must be used if JPA 2.0 elements are specified as XML metadata. Many of the elements in the schema relate to object/relational mapping rather than metadata; these elements are discussed in Chapter 13, Mapping Metadata . Version 1.0 of the orm schema can be found at http://java.sun.com/xml/ns/persistence/orm_1_0.xsd.

<?xml version="1.0" encoding="UTF-8"?>
    <!-- Java Persistence API object/relational mapping file schema -->
<xsd:schema targetNamespace="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified" attributeFormDefault="unqualified"
    version="2.0">

    <xsd:annotation>
        <xsd:documentation>
            @(#)orm_2_0.xsd 2.0 October 1 2009
        </xsd:documentation>
    </xsd:annotation>

  <xsd:annotation>
    <xsd:documentation>

          DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.

          Copyright 2005-2009 Sun Microsystems, Inc. All rights reserved.

          The contents of this file are subject to the terms of either the
          GNU General Public License Version 2 only ("GPL") or the Common
          Development and Distribution License("CDDL") (collectively, the
          "License").  You may not use this file except in compliance with
          the License. You can obtain a copy of the License at
          https://glassfish.dev.java.net/public/CDDL+GPL.html or
          glassfish/bootstrap/legal/LICENSE.txt.  See the License for the
          specific language governing permissions and limitations under the
          License.

          When distributing the software, include this License Header
          Notice in each file and include the License file at
          glassfish/bootstrap/legal/LICENSE.txt.  Sun designates this
          particular file as subject to the "Classpath" exception as
          provided by Sun in the GPL Version 2 section of the License file
          that accompanied this code.  If applicable, add the following
          below the License Header, with the fields enclosed by brackets []
          replaced by your own identifying information:
          "Portions Copyrighted [year] [name of copyright owner]"

          Contributor(s):

          If you wish your version of this file to be governed by only the
          CDDL or only the GPL Version 2, indicate your decision by adding
          "[Contributor] elects to include this software in this
          distribution under the [CDDL or GPL Version 2] license."  If you
          don't indicate a single choice of license, a recipient has the
          option to distribute your version of this file under either the
          CDDL, the GPL Version 2 or to extend the choice of license to its
          licensees as provided above.  However, if you add GPL Version 2
          code and therefore, elected the GPL Version 2 license, then the
          option applies only if the new code is made subject to such
          option by the copyright holder.

        </xsd:documentation>
    </xsd:annotation>

    <xsd:annotation>
        <xsd:documentation>
        <![CDATA[
            This is the XML Schema for the persistence object/relational
            mapping file.
            The file may be named "META-INF/orm.xml" in the persistence
            archive or it may be named some other name which would be
            used to locate the file as resource on the classpath.
            Object/relational mapping files must indicate the object/relational
            mapping file schema by using the persistence namespace:
            http://java.sun.com/xml/ns/persistence
            and indicate the version of the schema by
            using the version element as shown below:
            <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
            http://java.sun.com/xml/ns/persistence/orm/orm_2_0.xsd"
            version="2.0">
            ...
            </entity-mappings>
        ]]>
        </xsd:documentation>
    </xsd:annotation>

    <xsd:complexType name="emptyType" />
    <xsd:simpleType name="versionType">
        <xsd:restriction base="xsd:token">
            <xsd:pattern value="[0-9]+(\.[0-9]+)*" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:element name="entity-mappings">
        <xsd:complexType>
            <xsd:annotation>
                <xsd:documentation>
                    The entity-mappings element is the root element of a mapping
                    file. It contains the following four types of elements:
                    1. The persistence-unit-metadata element contains metadata
                    for the entire persistence unit. It is undefined if this element
                    occurs in multiple mapping files within the same
                    persistence unit.
                    2. The package, schema, catalog and access elements apply to all of
                    the entity, mapped-superclass and embeddable
                    elements defined in
                    the same file in which they occur.
                    3. The sequence-generator, table-generator, named-query,
                    named-native-query and sql-result-set-mapping
                    elements are global
                    to the persistence unit. It is undefined to have more than one
                    sequence-generator or table-generator of the same
                    name in the same
                    or different mapping files in a persistence unit. It is also
                    undefined to have more than one named-query,
                    named-native-query, or
                    result-set-mapping of the same name in the same or different mapping
                    files in a persistence unit.
                    4. The entity, mapped-superclass and embeddable elements each define
                    the mapping information for a managed persistent
                    class. The mapping
                    information contained in these elements may be complete or it may
                    be partial.
                </xsd:documentation>
            </xsd:annotation>
            <xsd:sequence>
                <xsd:element name="description" type="xsd:string"
                    minOccurs="0" />
                <xsd:element name="persistence-unit-metadata"
                    type="orm:persistence-unit-metadata" minOccurs="0" />
                <xsd:element name="package" type="xsd:string"
                    minOccurs="0" />
                <xsd:element name="schema" type="xsd:string"
                    minOccurs="0" />
                <xsd:element name="catalog" type="xsd:string"
                    minOccurs="0" />
                <xsd:element name="access" type="orm:access-type"
                    minOccurs="0" />
                <xsd:element name="sequence-generator" type="orm:sequence-generator"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="table-generator" type="orm:table-generator"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="named-query" type="orm:named-query"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="named-native-query" type="orm:named-native-query"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="sql-result-set-mapping"
                    type="orm:sql-result-set-mapping" minOccurs="0"
                    maxOccurs="unbounded" />
                <xsd:element name="mapped-superclass" type="orm:mapped-superclass"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="entity" type="orm:entity"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="embeddable" type="orm:embeddable"
                    minOccurs="0" maxOccurs="unbounded" />
            </xsd:sequence>
            <xsd:attribute name="version" type="orm:versionType"
                fixed="2.0" use="required" />
        </xsd:complexType>
    </xsd:element>
    <!-- **************************************************** -->
    <xsd:complexType name="persistence-unit-metadata">
        <xsd:annotation>
            <xsd:documentation>
                Metadata that applies to the persistence unit and not just to
                the mapping file in which it is contained.
                If the xml-mapping-metadata-complete element is specified,
                the complete set of mapping metadata for the persistence unit
                is contained in the XML mapping files for the persistence unit.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="xml-mapping-metadata-complete"
                type="orm:emptyType" minOccurs="0" />
            <xsd:element name="persistence-unit-defaults"
                type="orm:persistence-unit-defaults" minOccurs="0" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="persistence-unit-defaults">
        <xsd:annotation>
            <xsd:documentation>
                These defaults are applied to the persistence unit as a whole
                unless they are overridden by local annotation or XML
                element settings.
                schema - Used as the schema for all tables, secondary tables, join
                tables, collection tables, sequence generators, and table
                generators that apply to the persistence unit
                catalog - Used as the catalog for all tables, secondary tables, join
                tables, collection tables, sequence generators, and
                table generators that apply to the persistence unit
                delimited-identifiers - Used to treat database identifiers as
                delimited identifiers.
                access - Used as the access type for all managed classes in
                the persistence unit
                cascade-persist - Adds cascade-persist to the set of cascade options
                in all entity relationships of the persistence unit
                entity-listeners - List of default entity listeners to be invoked
                on each entity in the persistence unit.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="schema" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="catalog" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="delimited-identifiers" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="access" type="orm:access-type"
                minOccurs="0" />
            <xsd:element name="cascade-persist" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="entity-listeners" type="orm:entity-listeners"
                minOccurs="0" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="entity">
        <xsd:annotation>
            <xsd:documentation>
                Defines the settings and mappings for an entity. Is allowed to be
                sparsely populated and used in conjunction with the annotations.
                Alternatively, the metadata-complete attribute can be
                used to
                indicate that no annotations on the entity class (and its fields
                or properties) are to be processed. If this is the case then
                the defaulting rules for the entity and its subelements will
                be recursively applied.
                @Target(TYPE) @Retention(RUNTIME)
                public @interface Entity {
                String name() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="table" type="orm:table"
                minOccurs="0" />
            <xsd:element name="secondary-table" type="orm:secondary-table"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="primary-key-join-column"
                type="orm:primary-key-join-column" minOccurs="0"
                maxOccurs="unbounded" />
            <xsd:element name="id-class" type="orm:id-class"
                minOccurs="0" />
            <xsd:element name="inheritance" type="orm:inheritance"
                minOccurs="0" />
            <xsd:element name="discriminator-value" type="orm:discriminator-value"
                minOccurs="0" />
            <xsd:element name="discriminator-column" type="orm:discriminator-column"
                minOccurs="0" />
            <xsd:element name="sequence-generator" type="orm:sequence-generator"
                minOccurs="0" />
            <xsd:element name="table-generator" type="orm:table-generator"
                minOccurs="0" />
            <xsd:element name="named-query" type="orm:named-query"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="named-native-query" type="orm:named-native-query"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="sql-result-set-mapping" type="orm:sql-result-set-mapping"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="exclude-default-listeners"
                type="orm:emptyType" minOccurs="0" />
            <xsd:element name="exclude-superclass-listeners"
                type="orm:emptyType" minOccurs="0" />
            <xsd:element name="entity-listeners" type="orm:entity-listeners"
                minOccurs="0" />
            <xsd:element name="pre-persist" type="orm:pre-persist"
                minOccurs="0" />
            <xsd:element name="post-persist" type="orm:post-persist"
                minOccurs="0" />
            <xsd:element name="pre-remove" type="orm:pre-remove"
                minOccurs="0" />
            <xsd:element name="post-remove" type="orm:post-remove"
                minOccurs="0" />
            <xsd:element name="pre-update" type="orm:pre-update"
                minOccurs="0" />
            <xsd:element name="post-update" type="orm:post-update"
                minOccurs="0" />
            <xsd:element name="post-load" type="orm:post-load"
                minOccurs="0" />
            <xsd:element name="attribute-override" type="orm:attribute-override"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="association-override" type="orm:association-override"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="attributes" type="orm:attributes"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="class" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="cacheable" type="xsd:boolean" />
        <xsd:attribute name="metadata-complete" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="access-type">
        <xsd:annotation>
            <xsd:documentation>
                This element determines how the persistence provider accesses the
                state of an entity or embedded object.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="PROPERTY" />
            <xsd:enumeration value="FIELD" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="association-override">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
                public @interface AssociationOverride {
                String name();
                JoinColumn[] joinColumns() default{};
                JoinTable joinTable() default @JoinTable;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string" minOccurs="0" />
            <xsd:choice>
                <xsd:element name="join-column" type="orm:join-column"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="join-table" type="orm:join-table"
                    minOccurs="0" />
            </xsd:choice>
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="attribute-override">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
                public @interface AttributeOverride {
                String name();
                Column column();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="column" type="orm:column" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="attributes">
        <xsd:annotation>
            <xsd:documentation>
                This element contains the entity field or property mappings.
                It may be sparsely populated to include only a subset of the
                fields or properties. If metadata-complete for the entity is true
                then the remainder of the attributes will be defaulted according
                to the default rules.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:choice>
                <xsd:element name="id" type="orm:id"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="embedded-id" type="orm:embedded-id"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:element name="basic" type="orm:basic"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="version" type="orm:version"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="many-to-one" type="orm:many-to-one"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="one-to-many" type="orm:one-to-many"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="one-to-one" type="orm:one-to-one"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="many-to-many" type="orm:many-to-many"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="element-collection" type="orm:element-collection"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="embedded" type="orm:embedded"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="transient" type="orm:transient"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="basic">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Basic {
                FetchType fetch() default EAGER;
                boolean optional() default true;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="column" type="orm:column"
                minOccurs="0" />
            <xsd:choice>
                <xsd:element name="lob" type="orm:lob"
                    minOccurs="0" />
                <xsd:element name="temporal" type="orm:temporal"
                    minOccurs="0" />
                <xsd:element name="enumerated" type="orm:enumerated"
                    minOccurs="0" />
            </xsd:choice>
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="optional" type="xsd:boolean" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="cascade-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH,
                DETACH};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="cascade-all" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="cascade-persist" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="cascade-merge" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="cascade-remove" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="cascade-refresh" type="orm:emptyType"
                minOccurs="0" />
            <xsd:element name="cascade-detach" type="orm:emptyType"
                minOccurs="0" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="collection-table">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface CollectionTable {
                String name() default "";
                String catalog() default "";
                String schema() default "";
                JoinColumn[] joinColumns() default {};
                UniqueConstraint[] uniqueConstraints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="join-column" type="orm:join-column"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="unique-constraint" type="orm:unique-constraint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Column {
                String name() default "";
                boolean unique() default false;
                boolean nullable() default true;
                boolean insertable() default true;
                boolean updatable() default true;
                String columnDefinition() default "";
                String table() default "";
                int length() default 255;
                int precision() default 0; // decimal precision
                int scale() default 0; // decimal scale
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="unique" type="xsd:boolean" />
        <xsd:attribute name="nullable" type="xsd:boolean" />
        <xsd:attribute name="insertable" type="xsd:boolean" />
        <xsd:attribute name="updatable" type="xsd:boolean" />
        <xsd:attribute name="column-definition" type="xsd:string" />
        <xsd:attribute name="table" type="xsd:string" />
        <xsd:attribute name="length" type="xsd:int" />
        <xsd:attribute name="precision" type="xsd:int" />
        <xsd:attribute name="scale" type="xsd:int" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="column-result">
        <xsd:annotation>
            <xsd:documentation>
                @Target({}) @Retention(RUNTIME)
                public @interface ColumnResult {
                String name();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="discriminator-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface DiscriminatorColumn {
                String name() default "DTYPE";
                DiscriminatorType discriminatorType() default STRING;
                String columnDefinition() default "";
                int length() default 31;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="discriminator-type" type="orm:discriminator-type" />
        <xsd:attribute name="column-definition" type="xsd:string" />
        <xsd:attribute name="length" type="xsd:int" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="discriminator-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum DiscriminatorType { STRING, CHAR, INTEGER };
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="STRING" />
            <xsd:enumeration value="CHAR" />
            <xsd:enumeration value="INTEGER" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="discriminator-value">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface DiscriminatorValue {
                String value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:string" />
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="element-collection">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface ElementCollection {
                Class targetClass() default void.class;
                FetchType fetch() default LAZY;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:choice>
                <xsd:element name="order-by" type="orm:order-by"
                    minOccurs="0" />
                <xsd:element name="order-column" type="orm:order-column"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:choice>
                <xsd:element name="map-key" type="orm:map-key"
                    minOccurs="0" />
                <xsd:sequence>
                    <xsd:element name="map-key-class" type="orm:map-key-class"
                        minOccurs="0" />
                    <xsd:choice>
                        <xsd:element name="map-key-temporal"
                            type="orm:temporal" minOccurs="0" />
                        <xsd:element name="map-key-enumerated"
                            type="orm:enumerated" minOccurs="0" />
                        <xsd:element name="map-key-attribute-override"
                            type="orm:attribute-override" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:choice>
                        <xsd:element name="map-key-column"
                            type="orm:map-key-column" minOccurs="0" />
                        <xsd:element name="map-key-join-column"
                            type="orm:map-key-join-column" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                </xsd:sequence>
            </xsd:choice>
            <xsd:choice>
                <xsd:sequence>
                    <xsd:element name="column" type="orm:column"
                        minOccurs="0" />
                    <xsd:choice>
                        <xsd:element name="temporal" type="orm:temporal"
                            minOccurs="0" />
                        <xsd:element name="enumerated" type="orm:enumerated"
                            minOccurs="0" />
                        <xsd:element name="lob" type="orm:lob"
                            minOccurs="0" />
                    </xsd:choice>
                </xsd:sequence>
                <xsd:sequence>
                    <xsd:element name="attribute-override"
                        type="orm:attribute-override" minOccurs="0"
                        maxOccurs="unbounded" />
                    <xsd:element name="association-override"
                        type="orm:association-override" minOccurs="0"
                        maxOccurs="unbounded" />
                </xsd:sequence>
            </xsd:choice>
            <xsd:element name="collection-table" type="orm:collection-table"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="target-class" type="xsd:string" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="embeddable">
        <xsd:annotation>
            <xsd:documentation>
                Defines the settings and mappings for embeddable objects. Is
                allowed to be sparsely populated and used in conjunction with
                the annotations. Alternatively, the metadata-complete attribute
                can be used to indicate that no annotations are to be processed
                in the class. If this is the case then the defaulting rules will
                be recursively applied.
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface Embeddable {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="attributes" type="orm:embeddable-attributes"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="class" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="metadata-complete" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="embeddable-attributes">
        <xsd:sequence>
            <xsd:element name="basic" type="orm:basic"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="many-to-one" type="orm:many-to-one"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="one-to-many" type="orm:one-to-many"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="one-to-one" type="orm:one-to-one"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="many-to-many" type="orm:many-to-many"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="element-collection" type="orm:element-collection"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="embedded" type="orm:embedded"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="transient" type="orm:transient"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="embedded">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Embedded {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="attribute-override" type="orm:attribute-override"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="association-override" type="orm:association-override"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="embedded-id">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface EmbeddedId {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="attribute-override" type="orm:attribute-override"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="entity-listener">
        <xsd:annotation>
            <xsd:documentation>
                Defines an entity listener to be invoked at lifecycle events
                for the entities that list this listener.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="pre-persist" type="orm:pre-persist"
                minOccurs="0" />
            <xsd:element name="post-persist" type="orm:post-persist"
                minOccurs="0" />
            <xsd:element name="pre-remove" type="orm:pre-remove"
                minOccurs="0" />
            <xsd:element name="post-remove" type="orm:post-remove"
                minOccurs="0" />
            <xsd:element name="pre-update" type="orm:pre-update"
                minOccurs="0" />
            <xsd:element name="post-update" type="orm:post-update"
                minOccurs="0" />
            <xsd:element name="post-load" type="orm:post-load"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="class" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="entity-listeners">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface EntityListeners {
                Class[] value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="entity-listener" type="orm:entity-listener"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="entity-result">
        <xsd:annotation>
            <xsd:documentation>
                @Target({}) @Retention(RUNTIME)
                public @interface EntityResult {
                Class entityClass();
                FieldResult[] fields() default {};
                String discriminatorColumn() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="field-result" type="orm:field-result"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="entity-class" type="xsd:string"
            use="required" />
        <xsd:attribute name="discriminator-column" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="enum-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum EnumType {
                ORDINAL,
                STRING
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="ORDINAL" />
            <xsd:enumeration value="STRING" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="enumerated">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Enumerated {
                EnumType value() default ORDINAL;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="orm:enum-type" />
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="fetch-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum FetchType { LAZY, EAGER };
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="LAZY" />
            <xsd:enumeration value="EAGER" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="field-result">
        <xsd:annotation>
            <xsd:documentation>
                @Target({}) @Retention(RUNTIME)
                public @interface FieldResult {
                String name();
                String column();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="column" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="generated-value">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface GeneratedValue {
                GenerationType strategy() default AUTO;
                String generator() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="strategy" type="orm:generation-type" />
        <xsd:attribute name="generator" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="generation-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO };
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="TABLE" />
            <xsd:enumeration value="SEQUENCE" />
            <xsd:enumeration value="IDENTITY" />
            <xsd:enumeration value="AUTO" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="id">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Id {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="column" type="orm:column"
                minOccurs="0" />
            <xsd:element name="generated-value" type="orm:generated-value"
                minOccurs="0" />
            <xsd:element name="temporal" type="orm:temporal"
                minOccurs="0" />
            <xsd:element name="table-generator" type="orm:table-generator"
                minOccurs="0" />
            <xsd:element name="sequence-generator" type="orm:sequence-generator"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="id-class">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface IdClass {
                Class value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="class" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="inheritance">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface Inheritance {
                InheritanceType strategy() default SINGLE_TABLE;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="strategy" type="orm:inheritance-type" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="inheritance-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum InheritanceType
                { SINGLE_TABLE, JOINED, TABLE_PER_CLASS};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="SINGLE_TABLE" />
            <xsd:enumeration value="JOINED" />
            <xsd:enumeration value="TABLE_PER_CLASS" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="join-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface JoinColumn {
                String name() default "";
                String referencedColumnName() default "";
                boolean unique() default false;
                boolean nullable() default true;
                boolean insertable() default true;
                boolean updatable() default true;
                String columnDefinition() default "";
                String table() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="referenced-column-name" type="xsd:string" />
        <xsd:attribute name="unique" type="xsd:boolean" />
        <xsd:attribute name="nullable" type="xsd:boolean" />
        <xsd:attribute name="insertable" type="xsd:boolean" />
        <xsd:attribute name="updatable" type="xsd:boolean" />
        <xsd:attribute name="column-definition" type="xsd:string" />
        <xsd:attribute name="table" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="join-table">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface JoinTable {
                String name() default "";
                String catalog() default "";
                String schema() default "";
                JoinColumn[] joinColumns() default {};
                JoinColumn[] inverseJoinColumns() default {};
                UniqueConstraint[] uniqueConstraints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="join-column" type="orm:join-column"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="inverse-join-column" type="orm:join-column"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="unique-constraint" type="orm:unique-constraint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="lob">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Lob {}
            </xsd:documentation>
        </xsd:annotation>
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="lock-mode-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum LockModeType { READ, WRITE, OPTIMISTIC,
                OPTIMISTIC_FORCE_INCREMENT, PESSIMISTIC_READ,
                PESSIMISTIC_WRITE,
                PESSIMISTIC_FORCE_INCREMENT, NONE};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="READ" />
            <xsd:enumeration value="WRITE" />
            <xsd:enumeration value="OPTIMISTIC" />
            <xsd:enumeration value="OPTIMISTIC_FORCE_INCREMENT" />
            <xsd:enumeration value="PESSIMISTIC_READ" />
            <xsd:enumeration value="PESSIMISTIC_WRITE" />
            <xsd:enumeration value="PESSIMISTIC_FORCE_INCREMENT" />
            <xsd:enumeration value="NONE" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="many-to-many">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface ManyToMany {
                Class targetEntity() default void.class;
                CascadeType[] cascade() default {};
                FetchType fetch() default LAZY;
                String mappedBy() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:choice>
                <xsd:element name="order-by" type="orm:order-by"
                    minOccurs="0" />
                <xsd:element name="order-column" type="orm:order-column"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:choice>
                <xsd:element name="map-key" type="orm:map-key"
                    minOccurs="0" />
                <xsd:sequence>
                    <xsd:element name="map-key-class" type="orm:map-key-class"
                        minOccurs="0" />
                    <xsd:choice>
                        <xsd:element name="map-key-temporal"
                            type="orm:temporal" minOccurs="0" />
                        <xsd:element name="map-key-enumerated"
                            type="orm:enumerated" minOccurs="0" />
                        <xsd:element name="map-key-attribute-override"
                            type="orm:attribute-override" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:choice>
                        <xsd:element name="map-key-column"
                            type="orm:map-key-column" minOccurs="0" />
                        <xsd:element name="map-key-join-column"
                            type="orm:map-key-join-column" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                </xsd:sequence>
            </xsd:choice>
            <xsd:element name="join-table" type="orm:join-table"
                minOccurs="0" />
            <xsd:element name="cascade" type="orm:cascade-type"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="target-entity" type="xsd:string" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="mapped-by" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="many-to-one">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface ManyToOne {
                Class targetEntity() default void.class;
                CascadeType[] cascade() default {};
                FetchType fetch() default EAGER;
                boolean optional() default true;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:choice>
                <xsd:element name="join-column" type="orm:join-column"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="join-table" type="orm:join-table"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:element name="cascade" type="orm:cascade-type"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="target-entity" type="xsd:string" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="optional" type="xsd:boolean" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="maps-id" type="xsd:string" />
        <xsd:attribute name="id" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="map-key">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface MapKey {
                String name() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="map-key-class">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface MapKeyClass {
                Class value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="class" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="map-key-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface MapKeyColumn {
                String name() default "";
                boolean unique() default false;
                boolean nullable() default false;
                boolean insertable() default true;
                boolean updatable() default true;
                String columnDefinition() default "";
                String table() default "";
                int length() default 255;
                int precision() default 0; // decimal precision
                int scale() default 0; // decimal scale
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="unique" type="xsd:boolean" />
        <xsd:attribute name="nullable" type="xsd:boolean" />
        <xsd:attribute name="insertable" type="xsd:boolean" />
        <xsd:attribute name="updatable" type="xsd:boolean" />
        <xsd:attribute name="column-definition" type="xsd:string" />
        <xsd:attribute name="table" type="xsd:string" />
        <xsd:attribute name="length" type="xsd:int" />
        <xsd:attribute name="precision" type="xsd:int" />
        <xsd:attribute name="scale" type="xsd:int" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="map-key-join-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface MapKeyJoinColumn {
                String name() default "";
                String referencedColumnName() default "";
                boolean unique() default false;
                boolean nullable() default false;
                boolean insertable() default true;
                boolean updatable() default true;
                String columnDefinition() default "";
                String table() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="referenced-column-name" type="xsd:string" />
        <xsd:attribute name="unique" type="xsd:boolean" />
        <xsd:attribute name="nullable" type="xsd:boolean" />
        <xsd:attribute name="insertable" type="xsd:boolean" />
        <xsd:attribute name="updatable" type="xsd:boolean" />
        <xsd:attribute name="column-definition" type="xsd:string" />
        <xsd:attribute name="table" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="mapped-superclass">
        <xsd:annotation>
            <xsd:documentation>
                Defines the settings and mappings for a mapped superclass. Is
                allowed to be sparsely populated and used in conjunction with
                the annotations. Alternatively, the metadata-complete attribute
                can be used to indicate that no annotations are to be processed
                If this is the case then the defaulting rules will be recursively
                applied.
                @Target(TYPE) @Retention(RUNTIME)
                public @interface MappedSuperclass{}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="id-class" type="orm:id-class"
                minOccurs="0" />
            <xsd:element name="exclude-default-listeners"
                type="orm:emptyType" minOccurs="0" />
            <xsd:element name="exclude-superclass-listeners"
                type="orm:emptyType" minOccurs="0" />
            <xsd:element name="entity-listeners" type="orm:entity-listeners"
                minOccurs="0" />
            <xsd:element name="pre-persist" type="orm:pre-persist"
                minOccurs="0" />
            <xsd:element name="post-persist" type="orm:post-persist"
                minOccurs="0" />
            <xsd:element name="pre-remove" type="orm:pre-remove"
                minOccurs="0" />
            <xsd:element name="post-remove" type="orm:post-remove"
                minOccurs="0" />
            <xsd:element name="pre-update" type="orm:pre-update"
                minOccurs="0" />
            <xsd:element name="post-update" type="orm:post-update"
                minOccurs="0" />
            <xsd:element name="post-load" type="orm:post-load"
                minOccurs="0" />
            <xsd:element name="attributes" type="orm:attributes"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="class" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="metadata-complete" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="named-native-query">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface NamedNativeQuery {
                String name();
                String query();
                QueryHint[] hints() default {};
                Class resultClass() default void.class;
                String resultSetMapping() default ""; //named SqlResultSetMapping
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="query" type="xsd:string" />
            <xsd:element name="hint" type="orm:query-hint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="result-class" type="xsd:string" />
        <xsd:attribute name="result-set-mapping" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="named-query">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface NamedQuery {
                String name();
                String query();
                LockModeType lockMode() default NONE;
                QueryHint[] hints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="query" type="xsd:string" />
            <xsd:element name="lock-mode" type="orm:lock-mode-type"
                minOccurs="0" />
            <xsd:element name="hint" type="orm:query-hint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="one-to-many">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface OneToMany {
                Class targetEntity() default void.class;
                CascadeType[] cascade() default {};
                FetchType fetch() default LAZY;
                String mappedBy() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:choice>
                <xsd:element name="order-by" type="orm:order-by"
                    minOccurs="0" />
                <xsd:element name="order-column" type="orm:order-column"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:choice>
                <xsd:element name="map-key" type="orm:map-key"
                    minOccurs="0" />
                <xsd:sequence>
                    <xsd:element name="map-key-class" type="orm:map-key-class"
                        minOccurs="0" />
                    <xsd:choice>
                        <xsd:element name="map-key-temporal"
                            type="orm:temporal" minOccurs="0" />
                        <xsd:element name="map-key-enumerated"
                            type="orm:enumerated" minOccurs="0" />
                        <xsd:element name="map-key-attribute-override"
                            type="orm:attribute-override" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:choice>
                        <xsd:element name="map-key-column"
                            type="orm:map-key-column" minOccurs="0" />
                        <xsd:element name="map-key-join-column"
                            type="orm:map-key-join-column" minOccurs="0"
                            maxOccurs="unbounded" />
                    </xsd:choice>
                </xsd:sequence>
            </xsd:choice>
            <xsd:choice>
                <xsd:element name="join-table" type="orm:join-table"
                    minOccurs="0" />
                <xsd:element name="join-column" type="orm:join-column"
                    minOccurs="0" maxOccurs="unbounded" />
            </xsd:choice>
            <xsd:element name="cascade" type="orm:cascade-type"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="target-entity" type="xsd:string" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="mapped-by" type="xsd:string" />
        <xsd:attribute name="orphan-removal" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="one-to-one">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface OneToOne {
                Class targetEntity() default void.class;
                CascadeType[] cascade() default {};
                FetchType fetch() default EAGER;
                boolean optional() default true;
                String mappedBy() default "";
                boolean orphanRemoval() default false;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:choice>
                <xsd:element name="primary-key-join-column"
                    type="orm:primary-key-join-column" minOccurs="0"
                    maxOccurs="unbounded" />
                <xsd:element name="join-column" type="orm:join-column"
                    minOccurs="0" maxOccurs="unbounded" />
                <xsd:element name="join-table" type="orm:join-table"
                    minOccurs="0" />
            </xsd:choice>
            <xsd:element name="cascade" type="orm:cascade-type"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="target-entity" type="xsd:string" />
        <xsd:attribute name="fetch" type="orm:fetch-type" />
        <xsd:attribute name="optional" type="xsd:boolean" />
        <xsd:attribute name="access" type="orm:access-type" />
        <xsd:attribute name="mapped-by" type="xsd:string" />
        <xsd:attribute name="orphan-removal" type="xsd:boolean" />
        <xsd:attribute name="maps-id" type="xsd:string" />
        <xsd:attribute name="id" type="xsd:boolean" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="order-by">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface OrderBy {
                String value() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:string" />
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="order-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface OrderColumn {
                String name() default "";
                boolean nullable() default true;
                boolean insertable() default true;
                boolean updatable() default true;
                String columnDefinition() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="nullable" type="xsd:boolean" />
        <xsd:attribute name="insertable" type="xsd:boolean" />
        <xsd:attribute name="updatable" type="xsd:boolean" />
        <xsd:attribute name="column-definition" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="post-load">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PostLoad {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="post-persist">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PostPersist {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="post-remove">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PostRemove {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="post-update">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PostUpdate {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="pre-persist">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PrePersist {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="pre-remove">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PreRemove {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="pre-update">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD}) @Retention(RUNTIME)
                public @interface PreUpdate {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="method-name" type="xsd:string"
            use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="primary-key-join-column">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
                public @interface PrimaryKeyJoinColumn {
                String name() default "";
                String referencedColumnName() default "";
                String columnDefinition() default "";
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="referenced-column-name" type="xsd:string" />
        <xsd:attribute name="column-definition" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="query-hint">
        <xsd:annotation>
            <xsd:documentation>
                @Target({}) @Retention(RUNTIME)
                public @interface QueryHint {
                String name();
                String value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="value" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="secondary-table">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface SecondaryTable {
                String name();
                String catalog() default "";
                String schema() default "";
                PrimaryKeyJoinColumn[] pkJoinColumns() default {};
                UniqueConstraint[] uniqueConstraints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="primary-key-join-column"
                type="orm:primary-key-join-column" minOccurs="0"
                maxOccurs="unbounded" />
            <xsd:element name="unique-constraint" type="orm:unique-constraint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="sequence-generator">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
                public @interface SequenceGenerator {
                String name();
                String sequenceName() default "";
                String catalog() default "";
                String schema() default "";
                int initialValue() default 1;
                int allocationSize() default 50;
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="sequence-name" type="xsd:string" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
        <xsd:attribute name="initial-value" type="xsd:int" />
        <xsd:attribute name="allocation-size" type="xsd:int" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="sql-result-set-mapping">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface SqlResultSetMapping {
                String name();
                EntityResult[] entities() default {};
                ColumnResult[] columns() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="entity-result" type="orm:entity-result"
                minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="column-result" type="orm:column-result"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="table">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE}) @Retention(RUNTIME)
                public @interface Table {
                String name() default "";
                String catalog() default "";
                String schema() default "";
                UniqueConstraint[] uniqueConstraints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="unique-constraint" type="orm:unique-constraint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="table-generator">
        <xsd:annotation>
            <xsd:documentation>
                @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
                public @interface TableGenerator {
                String name();
                String table() default "";
                String catalog() default "";
                String schema() default "";
                String pkColumnName() default "";
                String valueColumnName() default "";
                String pkColumnValue() default "";
                int initialValue() default 0;
                int allocationSize() default 50;
                UniqueConstraint[] uniqueConstraints() default {};
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="description" type="xsd:string"
                minOccurs="0" />
            <xsd:element name="unique-constraint" type="orm:unique-constraint"
                minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="table" type="xsd:string" />
        <xsd:attribute name="catalog" type="xsd:string" />
        <xsd:attribute name="schema" type="xsd:string" />
        <xsd:attribute name="pk-column-name" type="xsd:string" />
        <xsd:attribute name="value-column-name" type="xsd:string" />
        <xsd:attribute name="pk-column-value" type="xsd:string" />
        <xsd:attribute name="initial-value" type="xsd:int" />
        <xsd:attribute name="allocation-size" type="xsd:int" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:simpleType name="temporal">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Temporal {
                TemporalType value();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="orm:temporal-type" />
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="temporal-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum TemporalType {
                DATE, // java.sql.Date
                TIME, // java.sql.Time
                TIMESTAMP // java.sql.Timestamp
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="DATE" />
            <xsd:enumeration value="TIME" />
            <xsd:enumeration value="TIMESTAMP" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:complexType name="transient">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Transient {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:attribute name="name" type="xsd:string" use="required" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="unique-constraint">
        <xsd:annotation>
            <xsd:documentation>
                @Target({}) @Retention(RUNTIME)
                public @interface UniqueConstraint {
                String name() default "";
                String[] columnNames();
                }
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="column-name" type="xsd:string"
                maxOccurs="unbounded" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" />
    </xsd:complexType>
    <!-- **************************************************** -->
    <xsd:complexType name="version">
        <xsd:annotation>
            <xsd:documentation>
                @Target({METHOD, FIELD}) @Retention(RUNTIME)
                public @interface Version {}
            </xsd:documentation>
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="column" type="orm:column"
                minOccurs="0" />
            <xsd:element name="temporal" type="orm:temporal"
                minOccurs="0" />
        </xsd:sequence>
        <xsd:attribute name="name" type="xsd:string" use="required" />
        <xsd:attribute name="access" type="orm:access-type" />
    </xsd:complexType>
</xsd:schema>

4.  Conclusion

That exhausts persistence metadata annotations. We present the class definitions for our sample model below:

Example 5.2.  Complete Metadata

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
public class Magazine {

    @Id private String isbn;
    @Id private String title;
    @Version private int version;

    private double price;   // defaults to @Basic
    private int copiesSold; // defaults to @Basic

    @OneToOne(fetch=FetchType.LAZY,
        cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    private Article coverArticle;

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @OrderBy
    private Collection<Article> articles;

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
    private Company publisher;

    @Transient private byte[] data;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
public class Article {

    @Id private long id;
    @Version private int version;

    private String title;   // defaults to @Basic
    private byte[] content; // defaults to @Basic

    @ManyToMany(cascade=CascadeType.PERSIST)
    @OrderBy("lastName, firstName")
    private Collection<Author> authors;

    ...
}


package org.mag.pub;

@Entity
public class Company {

    @Id private long id;
    @Version private int version;

    private String name;     // defaults to @Basic
    private double revenue;  // defaults to @Basic
    private Address address; // defaults to @Embedded

    @OneToMany(mappedBy="publisher", cascade=CascadeType.PERSIST)
    private Collection<Magazine> mags;

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    private Collection<Subscription> subscriptions;

    ...
}

@Entity
public class Author {

    @Id private long id;
    @Version private int version;

    private String firstName; // defaults to @Basic
    private double lastName;  // defaults to @Basic
    private Address address;  // defaults to @Embedded

    @ManyToMany(mappedBy="authors", cascade=CascadeType.PERSIST)
    private Collection<Article> arts;

    ...
}

@Embeddable
public class Address {

    private String street; // defaults to @Basic
    private String city;   // defaults to @Basic
    private String state;  // defaults to @Basic
    private String zip;    // defaults to @Basic

    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id private long id;
    @Version private int version;

    ...
}

@Entity
public class Contract
    extends Document {

    private String terms; // defaults to @Basic

    ...
}

@Entity
public class Subscription {

    @Id private long id;
    @Version private int version;

    private Date startDate; // defaults to @Basic
    private double payment; // defaults to @Basic

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @MapKey(name="num")
    private Map<Long,LineItem> lineItems;

    ...

    @Entity
    public static class LineItem
        extends Contract {

        private String comments; // defaults to @Basic
        private double price;    // defaults to @Basic
        private long num;        // defaults to @Basic

        @ManyToOne
        private Magazine magazine;

        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {

    @Basic(fetch=FetchType.LAZY)
    private boolean getEliteClub() { ... }
    public void setEliteClub(boolean elite) { ... }

    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {

    public Date getEndDate() { ... }
    public void setEndDate(Date end) { ... }

    ...
}

The same metadata declarations in XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <!-- declares a default access type for all entities -->
    <access-type>FIELD</access-type>
    <mapped-superclass class="org.mag.subscribe.Document">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            <version name="version"/>
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <id-class="org.mag.Magazine$MagazineId"/>
        <attributes>
            <id name="isbn"/>
            <id name="title"/>
            <basic name="name"/>
            <basic name="price"/>
            <basic name="copiesSold"/>
            <version name="version"/>
            <many-to-one name="publisher" fetch="LAZY">
                <cascade>
                    <cascade-persist/>
                </cascade>
            </many-to-one>
            <one-to-many name="articles">
                <order-by/>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
            <one-to-one name="coverArticle" fetch="LAZY">
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-one>
            <transient name="data"/>
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <attributes>
            <id name="id"/>
            <basic name="title"/>
            <basic name="content"/>
            <version name="version"/>
            <many-to-many name="articles">
                <order-by>lastName, firstName</order-by>
            </many-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <attributes>
            <id name="id"/>
            <basic name="name"/>
            <basic name="revenue"/>
            <version name="version"/>
            <one-to-many name="mags" mapped-by="publisher">
                <cascade>
                    <cascade-persist/>
                </cascade>
            </one-to-many>
            <one-to-many name="subscriptions">
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <attributes>
            <id name="id"/>
            <basic name="firstName"/>
            <basic name="lastName"/>
            <version name="version"/>
            <many-to-many name="arts" mapped-by="authors">
                <cascade>
                    <cascade-persist/>
                </cascade>
            </many-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <attributes>
            <basic name="terms"/>
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <attributes>
            <id name="id"/>
            <basic name="payment"/>
            <basic name="startDate"/>
            <version name="version"/>
            <one-to-many name="items">
                <map-key name="num">
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <attributes>
            <basic name="comments"/>
            <basic name="price"/>
            <basic name="num"/>
            <many-to-one name="magazine"/>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime"
        access="PROPERTY">
        <attributes>
            <basic name="eliteClub" fetch="LAZY"/>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        <attributes>
            <basic name="endDate"/>
        </attributes>
    </entity>
    <embeddable class="org.mag.pub.Address">
        <attributes>
            <basic name="street"/>
            <basic name="city"/>
            <basic name="state"/>
            <basic name="zip"/>
        </attributes>
    </embeddable>
</entity-mappings>

Chapter 13, Mapping Metadata will show you how to map your persistent classes to the datastore using additional annotations and XML markup. First, however, we turn to the JPA runtime APIs.

Chapter 6.  Persistence

Note

OpenJPA also includes the OpenJPAPersistence helper class to provide additional utility methods.

Within a container, you will typically use injection to access an EntityManagerFactory. Applications operating outside of a container, however, can use the Persistence class to obtain EntityManagerFactory objects in a vendor-neutral fashion.

public static EntityManagerFactory createEntityManagerFactory(String name);
public static EntityManagerFactory createEntityManagerFactory(String name, Map props);
public static PersistenceUtil getPersistenceUtil();

Each createEntityManagerFactory method searches the system for an EntityManagerFactory definition with the given name. Use null for an unnamed factory. The optional map contains vendor-specific property settings used to further configure the factory.

persistence.xml files define EntityManagerFactories. The createEntityManagerFactory methods search for persistence.xml files within the META-INF directory of any CLASSPATH element. For example, if your CLASSPATH contains the conf directory, you could place an EntityManagerFactory definition in conf/META-INF/persistence.xml.

The getPersistenceUtil method returns a PersistenceUtil interface that can be used to determine whether an entity or attribute of an entity is loaded.

PersistenceUtil pUtil = Persistence.getPersistenceUtil();

if (!pUtil.isLoaded(myEntity)) {
    loadEntity(myEntity);
}

1.  persistence.xml

With the introduction of JPA 2.0, there are two versions of the persistence.xml. The most current revision of the 2.0 persistence schema is presented below. Version 1.0 of the persistence schema can be found at http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd.

<?xml version="1.0" encoding="UTF-8"?>
    <!-- persistence.xml schema -->
<xsd:schema targetNamespace="http://java.sun.com/xml/ns/persistence"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:persistence="http://java.sun.com/xml/ns/persistence"
    elementFormDefault="qualified" attributeFormDefault="unqualified"
    version="2.0">

    <xsd:annotation>
        <xsd:documentation>
            @(#)persistence_2_0.xsd 1.0 October 1 2009
        </xsd:documentation>
    </xsd:annotation>

    <xsd:annotation>
        <xsd:documentation>

          DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.

          Copyright 2005-2009 Sun Microsystems, Inc. All rights reserved.

          The contents of this file are subject to the terms of either the
          GNU General Public License Version 2 only ("GPL") or the Common
          Development and Distribution License("CDDL") (collectively, the
          "License").  You may not use this file except in compliance with
          the License. You can obtain a copy of the License at
          https://glassfish.dev.java.net/public/CDDL+GPL.html or
          glassfish/bootstrap/legal/LICENSE.txt.  See the License for the
          specific language governing permissions and limitations under the
          License.

          When distributing the software, include this License Header
          Notice in each file and include the License file at
          glassfish/bootstrap/legal/LICENSE.txt.  Sun designates this
          particular file as subject to the "Classpath" exception as
          provided by Sun in the GPL Version 2 section of the License file
          that accompanied this code.  If applicable, add the following
          below the License Header, with the fields enclosed by brackets []
          replaced by your own identifying information:
          "Portions Copyrighted [year] [name of copyright owner]"

          Contributor(s):

          If you wish your version of this file to be governed by only the
          CDDL or only the GPL Version 2, indicate your decision by adding
          "[Contributor] elects to include this software in this
          distribution under the [CDDL or GPL Version 2] license."  If you
          don't indicate a single choice of license, a recipient has the
          option to distribute your version of this file under either the
          CDDL, the GPL Version 2 or to extend the choice of license to its
          licensees as provided above.  However, if you add GPL Version 2
          code and therefore, elected the GPL Version 2 license, then the
          option applies only if the new code is made subject to such
          option by the copyright holder.

        </xsd:documentation>
    </xsd:annotation>

    <xsd:annotation>
        <xsd:documentation>
        <![CDATA[
            This is the XML Schema for the persistence configuration file.
            The file must be named "META-INF/persistence.xml" in the
            persistence archive.
            Persistence configuration files must indicate
            the persistence schema by using the persistence namespace:
            http://java.sun.com/xml/ns/persistence
            and indicate the version of the schema by
            using the version element as shown below:
            <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>
        ]]>
        </xsd:documentation>
    </xsd:annotation>

    <xsd:simpleType name="versionType">
        <xsd:restriction base="xsd:token">
            <xsd:pattern value="[0-9]+(\.[0-9]+)*" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:element name="persistence">
        <xsd:complexType>
            <xsd:sequence>
                <!-- **************************************************** -->
                <xsd:element name="persistence-unit"
                    minOccurs="1" maxOccurs="unbounded">
                    <xsd:complexType>
                        <xsd:annotation>
                            <xsd:documentation>
                                Configuration of a persistence unit.
                            </xsd:documentation>
                        </xsd:annotation>
                        <xsd:sequence>
                            <!-- **************************************************** -->
                            <xsd:element name="description"
                                type="xsd:string" minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        Description of this persistence unit.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="provider"
                                type="xsd:string" minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        Provider class that supplies EntityManagers for this
                                        persistence unit.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="jta-data-source"
                                type="xsd:string" minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        The container-specific name of the JTA datasource to use.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="non-jta-data-source"
                                type="xsd:string" minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        The container-specific name of a non-JTA datasource to use.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="mapping-file"
                                type="xsd:string" minOccurs="0"
                                maxOccurs="unbounded">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        File containing mapping information. Loaded as a resource
                                        by the persistence provider.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="jar-file"
                                type="xsd:string" minOccurs="0"
                                maxOccurs="unbounded">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        Jar file that is to be scanned for managed classes.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="class" type="xsd:string"
                                minOccurs="0" maxOccurs="unbounded">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        Managed class to be included in the persistence unit and
                                        to scan for annotations. It should be annotated
                                        with either @Entity, @Embeddable or @MappedSuperclass.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="exclude-unlisted-classes"
                                type="xsd:boolean" default="true"
                                minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        When set to true then only listed classes and jars will
                                        be scanned for persistent classes, otherwise the
                                        enclosing jar or directory will also be scanned.
                                        Not applicable to Java SE persistence units.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="shared-cache-mode"
                                type="persistence:persistence-unit-caching-type"
                                minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        Defines whether caching is enabled for the
                                        persistence unit if caching is supported by the
                                        persistence provider. When set to ALL, all entities
                                        will be cached. When set to NONE, no entities will
                                        be cached. When set to ENABLE_SELECTIVE, only entities
                                        specified as cacheable will be cached. When set to
                                        DISABLE_SELECTIVE, entities specified as not cacheable
                                        will not be cached. When not specified or when set to
                                        UNSPECIFIED, provider defaults may apply.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="validation-mode"
                                type="persistence:persistence-unit-validation-mode-type"
                                minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        The validation mode to be used for the persistence unit.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:element>
                            <!-- **************************************************** -->
                            <xsd:element name="properties"
                                minOccurs="0">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        A list of standard and vendor-specific properties
                                        and hints.
                                    </xsd:documentation>
                                </xsd:annotation>
                                <xsd:complexType>
                                    <xsd:sequence>
                                        <xsd:element name="property"
                                            minOccurs="0" maxOccurs="unbounded">
                                            <xsd:annotation>
                                                <xsd:documentation>
                                                    A name-value pair.
                                                </xsd:documentation>
                                            </xsd:annotation>
                                            <xsd:complexType>
                                                <xsd:attribute
                                                    name="name" type="xsd:string"
                                                    use="required" />
                                                <xsd:attribute
                                                    name="value" type="xsd:string"
                                                    use="required" />
                                            </xsd:complexType>
                                        </xsd:element>
                                    </xsd:sequence>
                                </xsd:complexType>
                            </xsd:element>
                        </xsd:sequence>
                        <!-- **************************************************** -->
                        <xsd:attribute name="name" type="xsd:string"
                            use="required">
                            <xsd:annotation>
                                <xsd:documentation>
                                    Name used in code to reference this persistence unit.
                                </xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                        <!-- **************************************************** -->
                        <xsd:attribute name="transaction-type"
                            type="persistence:persistence-unit-transaction-type">
                            <xsd:annotation>
                                <xsd:documentation>
                                    Type of transactions used by EntityManagers from this
                                    persistence unit.
                                </xsd:documentation>
                            </xsd:annotation>
                        </xsd:attribute>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
            <xsd:attribute name="version" type="persistence:versionType"
                fixed="2.0" use="required" />
        </xsd:complexType>
    </xsd:element>
    <!-- **************************************************** -->
    <xsd:simpleType name="persistence-unit-transaction-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum PersistenceUnitTransactionType {JTA, RESOURCE_LOCAL};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="JTA" />
            <xsd:enumeration value="RESOURCE_LOCAL" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="persistence-unit-caching-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum SharedCacheMode { ALL, NONE, ENABLE_SELECTIVE,
                DISABLE_SELECTIVE, UNSPECIFIED};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="ALL" />
            <xsd:enumeration value="NONE" />
            <xsd:enumeration value="ENABLE_SELECTIVE" />
            <xsd:enumeration value="DISABLE_SELECTIVE" />
            <xsd:enumeration value="UNSPECIFIED" />
        </xsd:restriction>
    </xsd:simpleType>
    <!-- **************************************************** -->
    <xsd:simpleType name="persistence-unit-validation-mode-type">
        <xsd:annotation>
            <xsd:documentation>
                public enum ValidationMode { AUTO, CALLBACK, NONE};
            </xsd:documentation>
        </xsd:annotation>
        <xsd:restriction base="xsd:token">
            <xsd:enumeration value="AUTO" />
            <xsd:enumeration value="CALLBACK" />
            <xsd:enumeration value="NONE" />
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

The root element of a persistence.xml file is persistence, which then contains one or more persistence-unit definitions. The root element should include the version attribute with the appropriate version, 1.0 for a version 1.0 file and 2.0 for a version 2.0 file. Each persistence unit describes the configuration for the entity managers created by the persistence unit's entity manager factory. The persistence unit can specify these elements and attributes.

  • name: This is the name you pass to the Persistence.createEntityManagerFactory methods described above. The name attribute is required.

  • transaction-type: Whether to use managed (JTA) or local (RESOURCE_LOCAL) transaction management.

  • provider: If you are using a third-party JPA vendor, this element names its implementation of the PersistenceProvider bootstrapping interface.

    Note

    Set the provider to org.apache.openjpa.persistence.PersistenceProviderImpl to use OpenJPA.

  • jta-data-source: The JNDI name of a JDBC DataSource that is automatically enlisted in JTA transactions. This may be an XA DataSource.

  • non-jta-data-source: The JNDI name of a JDBC DataSource that is not enlisted in JTA transactions.

  • mapping-file*: The resource names of XML mapping files for entities and embeddable classes. You can also specify mapping information in an orm.xml file in your META-INF directory. If present, the orm.xml mapping file will be read automatically.

  • jar-file*: The names of jar files containing entities and embeddable classes. The implementation will scan the jar for annotated classes.

  • class*: The class names of entities and embeddable classes.

  • properties: This element contains nested property elements used to specify vendor-specific settings. Each property has a name attribute and a value attribute.

    Note

    The Reference Guide's Chapter 2, Configuration describes OpenJPA's configuration properties.

Here is a typical persistence.xml file for a non-EE environment:

Example 6.1.  persistence.xml

<?xml version="1.0"?>
<persistence version="1.0">
  <persistence-unit name="openjpa">
    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
    <class>tutorial.Animal</class>
    <class>tutorial.Dog</class>
    <class>tutorial.Rabbit</class>
    <class>tutorial.Snake</class>
    <properties>
      <property name="openjpa.ConnectionURL" value="jdbc:hsqldb:tutorial_database"/>
      <property name="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver"/>
      <property name="openjpa.ConnectionUserName" value="sa"/>
      <property name="openjpa.ConnectionPassword" value=""/>
      <property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
    </properties>
  </persistence-unit>
</persistence>

2.  Non-EE Use

The example below demonstrates the Persistence class in action. You will typically execute code like this on application startup, then cache the resulting factory for future use. This bootstrapping code is only necessary in non-EE environments; in an EE environment EntityManagerFactories are typically injected.

Example 6.2.  Obtaining an EntityManagerFactory

// if your persistence.xml file does not contain all settings already, you
// can add vendor settings to a map
Properties props = new Properties();
...

// create the factory defined by the "openjpa" entity-manager entry
EntityManagerFactory emf = Persistence.createEntityManagerFactory("openjpa", props);

Chapter 7.  EntityManagerFactory

The EntityManagerFactory creates EntityManager instances for application use.

Note

OpenJPA extends the standard EntityManagerFactory interface with the OpenJPAEntityManagerFactory to provide additional functionality.

1.  Obtaining an EntityManagerFactory

Within a container, you will typically use injection to access an EntityManagerFactory. There are, however, alternative mechanisms for EntityManagerFactory construction.

Some vendors may supply public constructors for their EntityManagerFactory implementations, but we recommend using the Java Connector Architecture (JCA) in a managed environment, or the Persistence class' createEntityManagerFactory methods in an unmanaged environment, as described in Chapter 6, Persistence . These strategies allow vendors to pool factories, cutting down on resource utilization.

JPA allows you to create and configure an EntityManagerFactory, then store it in a Java Naming and Directory Interface (JNDI) tree for later retrieval and use.

2.  Obtaining EntityManagers

public EntityManager createEntityManager();
public EntityManager createEntityManager(Map map);

The two createEntityManager methods above create a new EntityManager each time they are invoked. The optional Map is used to supply vendor-specific settings. If you have configured your implementation for JTA transactions and a JTA transaction is active, the returned EntityManager will be synchronized with that transaction.

Note

OpenJPA recognizes the following string keys in the map supplied to createEntityManager:

The last option uses reflection to configure any property of OpenJPA's EntityManager implementation with the value supplied in your map. The first options correspond exactly to the same-named OpenJPA configuration keys described in Chapter 2, Configuration of the Reference Guide.

3.  Persistence Context

A persistence context is a set of entities such that for any persistent identity there is a unique entity instance. Within a persistence context, entities are managed. The EntityManager controls their lifecycle, and they can access datastore resources.

When a persistence context ends, previously-managed entities become detached. A detached entity is no longer under the control of the EntityManager, and no longer has access to datastore resources. We discuss detachment in detail in Section 2, “ Entity Lifecycle Management ”. For now, it is sufficient to know that detachment has two obvious consequences:

  1. The detached entity cannot load any additional persistent state.

  2. The EntityManager will not return the detached entity from find, nor will queries include the detached entity in their results. Instead, find method invocations and query executions that would normally incorporate the detached entity will create a new managed entity with the same identity.

Note

OpenJPA offers several features related to detaching entities. See Section 1, “ Detach and Attach ” in the Reference Guide. Section 1.3, “ Defining the Detached Object Graph ” in particular describes how to use the DetachState setting to boost the performance of merging detached entities.

Injected EntityManagers have a transaction persistence context, while EntityManagers obtained through the EntityManagerFactory have an extended persistence context. We describe these persistence context types below.

3.1.  Transaction Persistence Context

Under the transaction persistence context model, an EntityManager begins a new persistence context with each transaction, and ends the context when the transaction commits or rolls back. Within the transaction, entities you retrieve through the EntityManager or via Queries are managed entities. They can access datastore resources to lazy-load additional persistent state as needed, and only one entity may exist for any persistent identity.

When the transaction completes, all entities lose their association with the EntityManager and become detached. Traversing a persistent field that wasn't already loaded now has undefined results. And using the EntityManager or a Query to retrieve additional objects may now create new instances with the same persistent identities as detached instances.

If you use an EntityManager with a transaction persistence context model outside of an active transaction, each method invocation creates a new persistence context, performs the method action, and ends the persistence context. For example, consider using the EntityManager.find method outside of a transaction. The EntityManager will create a temporary persistence context, perform the find operation, end the persistence context, and return the detached result object to you. A second call with the same id will return a second detached object.

When the next transaction begins, the EntityManager will begin a new persistence context, and will again start returning managed entities. As you'll see in Chapter 8, EntityManager , you can also merge the previously-detached entities back into the new persistence context.

Example 7.1.  Behavior of Transaction Persistence Context

The following code illustrates the behavior of entities under an EntityManager using a transaction persistence context.

EntityManager em; // injected
...

// outside a transaction:

// each operation occurs in a separate persistence context, and returns
// a new detached instance
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
...

// transaction begins:

// within a transaction, a subsequent lookup doesn't return any of the
// detached objects.  however, two lookups within the same transaction
// return the same instance, because the persistence context spans the
// transaction
Magazine mag3 = em.find(Magazine.class, magId);
assertTrue(mag3 != mag1 && mag3 != mag2);
Magazine mag4 = em.find(Magazine.class, magId);
assertTrue(mag4 == mag3);
...

// transaction commits:

// once again, each operation returns a new instance
Magazine mag5 = em.find(Magazine.class, magId);
assertTrue(mag5 != mag3);

3.2.  Extended Persistence Context

An EntityManager using an extended persistence context maintains the same persistence context for its entire lifecycle. Whether inside a transaction or not, all entities returned from the EntityManager are managed, and the EntityManager never creates two entity instances to represent the same persistent identity. Entities only become detached when you finally close the EntityManager (or when they are serialized).

Example 7.2.  Behavior of Extended Persistence Context

The following code illustrates the behavior of entities under an EntityManager using an extended persistence context.

EntityManagerFactory emf = ...
EntityManager em = emf.createEntityManager();

// persistence context active for entire life of EM, so only one entity
// for a given persistent identity
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 == mag1);

em.getTransaction().begin();

// same persistence context active within the transaction
Magazine mag3 = em.find(Magazine.class, magId);
assertTrue(mag3 == mag1);
Magazine mag4 = em.find(Magazine.class, magId);
assertTrue(mag4 == mag1);

em.getTransaction.commit();

// when the transaction commits, instance still managed
Magazine mag5 = em.find(Magazine.class, magId);
assertTrue(mag5 == mag1);

// instance finally becomes detached when EM closes
em.close();

4.  Retrieving Properties Information

There are two sets of properties that may be specified: those that are specific to OpenJPA and those that have been defined by the JPA specification. In some cases, two properties may be equivalent, but have different keys. For example, openjpa.LockTimeout and javax.persistence.lock.timeout are two different keys for the same property.

There are two methods that can be used to retrieve information related to properties:

public Map<String,Object> getProperties();
public Set<String> getSupportedProperties();

  • getProperties - Provides a list of current properties. If a property has more than one key, the key that will be returned is the one that was used when the property was set. If the property was not explicitly set, the key defined by the JPA specification will be returned with the default value.

  • getSupportedProperties - Returns a set of supported property keys. This includes keys defined by the JPA specification as well as keys specific to OpenJPA. If a property has more than one key, all possible keys will be returned.

Note

The getSupportedProperties method is an OpenJPA extension to the JPA specification.

5.  Closing the EntityManagerFactory

public boolean isOpen();
public void close();

EntityManagerFactory instances are heavyweight objects. Each factory might maintain a metadata cache, object state cache, EntityManager pool, connection pool, and more. If your application no longer needs an EntityManagerFactory, you should close it to free these resources. When an EntityManagerFactory closes, all EntityManagers from that factory, and by extension all entities managed by those EntityManagers, become invalid. Attempting to close an EntityManagerFactory while one or more of its EntityManagers has an active transaction may result in an IllegalStateException.

Closing an EntityManagerFactory should not be taken lightly. It is much better to keep a factory open for a long period of time than to repeatedly create and close new factories. Thus, most applications will never close the factory, or only close it when the application is exiting. Only applications that require multiple factories with different configurations have an obvious reason to create and close multiple EntityManagerFactory instances. Once a factory is closed, all methods except isOpen throw an IllegalStateException.

6.  PersistenceUnitUtil

public PersistenceUnitUtil getPersistenceUnitUtil();

The EntityManagerFactory method getPersistenceUnitUtil provides access to a PersistenceUnitUtil utility. PersistenceUnitUtil can be used to obtain the identity of a managed object and determine the load state of the entity or one of its attributes. If the object is not managed by one of the entity managers created from the entity manager factory from which the utility was obtained, the getIdentifier method will return null and the isLoaded methods will return false.

EntityManagerFactory emf = Persistence.createEntityManagerFactory();
PersistenceUnitUtil puUtil = emf.getPersistenceUnitUtil();

if (puUtil.getIdentifier(deptEntity) == null) {
    throw new Exception("Identity is not valid.");
}
if (!puUtil.isLoaded(deptEntity, "employees")) {
    throw new Exception("Employees not loaded.");
}

Chapter 8.  EntityManager

The diagram above presents an overview of the EntityManager interface. For a complete treatment of the EntityManager API, see the Javadoc documentation. Methods whose parameter signatures consist of an ellipsis (...) are overloaded to take multiple parameter types.

Note

OpenJPA extends the standard EntityManager interface with the org.apache.openjpa.persistence.OpenJPAEntityManager interface to provide additional functionality.

The EntityManager is the primary interface used by application developers to interact with the JPA runtime. The methods of the EntityManager can be divided into the following functional categories:

  • Transaction association.

  • Entity lifecycle management.

  • Entity identity management.

  • Cache management.

  • Query factory.

  • Entity locking.

  • Closing.

1.  Transaction Association

public EntityTransaction getTransaction();

Every EntityManager has a one-to-one relation with an EntityTransaction instance. In fact, many vendors use a single class to implement both the EntityManager and EntityTransaction interfaces. If your application requires multiple concurrent transactions, you will use multiple EntityManagers.

You can retrieve the EntityTransaction associated with an EntityManager through the getTransaction method. Note that most JPA implementations can integrate with an application server's managed transactions. If you take advantage of this feature, you will control transactions by declarative demarcation or through the Java Transaction API (JTA) rather than through the EntityTransaction.

2.  Entity Lifecycle Management

EntityManagers perform several actions that affect the lifecycle state of entity instances.

public void persist(Object entity);

Transitions new instances to managed. On the next flush or commit, the newly persisted instances will be inserted into the datastore.

For a given entity A, the persist method behaves as follows:

  • If A is a new entity, it becomes managed.

  • If A is an existing managed entity, it is ignored. However, the persist operation cascades as defined below.

  • If A is a removed entity, it becomes managed.

  • If A is a detached entity, an IllegalArgumentException is thrown.

  • The persist operation recurses on all relation fields of A whose cascades include CascadeType.PERSIST.

This action can only be used in the context of an active transaction.

public void remove(Object entity);

Transitions managed instances to removed. The instances will be deleted from the datastore on the next flush or commit. Accessing a removed entity has undefined results.

For a given entity A, the remove method behaves as follows:

  • If A is a new entity, it is ignored. However, the remove operation cascades as defined below.

  • If A is an existing managed entity, it becomes removed.

  • If A is a removed entity, it is ignored.

  • If A is a detached entity, an IllegalArgumentException is thrown.

  • The remove operation recurses on all relation fields of A whose cascades include CascadeType.REMOVE.

This action can only be used in the context of an active transaction.

public void refresh(Object entity);

Use the refresh action to make sure the persistent state of an instance is synchronized with the values in the datastore. refresh is intended for long-running optimistic transactions in which there is a danger of seeing stale data.

For a given entity A, the refresh method behaves as follows:

  • If A is a new entity, it is ignored. However, the refresh operation cascades as defined below.

  • If A is an existing managed entity, its state is refreshed from the datastore.

  • If A is a removed entity, it is ignored.

  • If A is a detached entity, an IllegalArgumentException is thrown.

  • The refresh operation recurses on all relation fields of A whose cascades include CascadeType.REFRESH.

public Object merge(Object entity);

A common use case for an application running in a servlet or application server is to "detach" objects from all server resources, modify them, and then "attach" them again. For example, a servlet might store persistent data in a user session between a modification based on a series of web forms. Between each form request, the web container might decide to serialize the session, requiring that the stored persistent state be disassociated from any other resources. Similarly, a client/server application might transfer persistent objects to a client via serialization, allow the client to modify their state, and then have the client return the modified data in order to be saved. This is sometimes referred to as the data transfer object or value object pattern, and it allows fine-grained manipulation of data objects without incurring the overhead of multiple remote method invocations.

JPA provides support for this pattern by automatically detaching entities when they are serialized or when a persistence context ends (see Section 3, “ Persistence Context ” for an exploration of persistence contexts). The JPA merge API re-attaches detached entities. This allows you to detach a persistent instance, modify the detached instance offline, and merge the instance back into an EntityManager (either the same one that detached the instance, or a new one). The changes will then be applied to the existing instance from the datastore.

A detached entity maintains its persistent identity, but cannot load additional state from the datastore. Accessing any persistent field or property that was not loaded at the time of detachment has undefined results. Also, be sure not to alter the version or identity fields of detached instances if you plan on merging them later.

The merge method returns a managed copy of the given detached entity. Changes made to the persistent state of the detached entity are applied to this managed instance. Because merging involves changing persistent state, you can only merge within a transaction.

If you attempt to merge an instance whose representation has changed in the datastore since detachment, the merge operation will throw an exception, or the transaction in which you perform the merge will fail on commit, just as if a normal optimistic conflict were detected.

Note

OpenJPA offers enhancements to JPA detachment functionality, including additional options to control which fields are detached. See Section 1, “ Detach and Attach ” in the Reference Guide for details.

For a given entity A, the merge method behaves as follows:

  • If A is a detached entity, its state is copied into existing managed instance A' of the same entity identity, or a new managed copy of A is created.

  • If A is a new entity, a new managed entity A' is created and the state of A is copied into A'.

  • If A is an existing managed entity, it is ignored. However, the merge operation still cascades as defined below.

  • If A is a removed entity, an IllegalArgumentException is thrown.

  • The merge operation recurses on all relation fields of A whose cascades include CascadeType.MERGE.

The following diagram illustrates the lifecycle of an entity with respect to the APIs presented in this section.

3.  Lifecycle Examples

The examples below demonstrate how to use the lifecycle methods presented in the previous section. The examples are appropriate for out-of-container use. Within a container, EntityManagers are usually injected, and transactions are usually managed. You would therefore omit the createEntityManager and close calls, as well as all transaction demarcation code.

Example 8.1.  Persisting Objects

// create some objects
Magazine mag = new Magazine("1B78-YU9L", "JavaWorld");

Company pub = new Company("Weston House");
pub.setRevenue(1750000D);
mag.setPublisher(pub);
pub.addMagazine(mag);

Article art = new Article("JPA Rules!", "Transparent Object Persistence");
art.addAuthor(new Author("Fred", "Hoyle"));
mag.addArticle(art);

// persist
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(mag);
em.persist(pub);
em.persist(art);
em.getTransaction().commit();

// or we could continue using the EntityManager...
em.close();

Example 8.2.  Updating Objects

Magazine.MagazineId mi = new Magazine.MagazineId();
mi.isbn = "1B78-YU9L";
mi.title = "JavaWorld";

// updates should always be made within transactions; note that
// there is no code explicitly linking the magazine or company
// with the transaction; JPA automatically tracks all changes
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Magazine mag = em.find(Magazine.class, mi);
mag.setPrice(5.99);
Company pub = mag.getPublisher();
pub.setRevenue(1750000D);
em.getTransaction().commit();

// or we could continue using the EntityManager...
em.close();

Example 8.3.  Removing Objects

// assume we have an object id for the company whose subscriptions
// we want to delete
Object oid = ...;

// deletes should always be made within transactions
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Company pub = (Company) em.find(Company.class, oid);
for (Subscription sub : pub.getSubscriptions())
    em.remove(sub);
pub.getSubscriptions().clear();
em.getTransaction().commit();

// or we could continue using the EntityManager...
em.close();

Example 8.4.  Detaching and Merging

This example demonstrates a common client/server scenario. The client requests objects and makes changes to them, while the server handles the object lookups and transactions.

// CLIENT:
// requests an object with a given oid
Record detached = (Record) getFromServer(oid);

...

// SERVER:
// send object to client; object detaches on EM close
Object oid = processClientRequest();
EntityManager em = emf.createEntityManager();
Record record = em.find(Record.class, oid);
em.close();
sendToClient(record);

...

// CLIENT:
// makes some modifications and sends back to server
detached.setSomeField("bar");
sendToServer(detached);

...

// SERVER:
// merges the instance and commit the changes
Record modified = (Record) processClientRequest();
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Record merged = (Record) em.merge(modified);
merged.setLastModified(System.currentTimeMillis());
merged.setModifier(getClientIdentityCode());
em.getTransaction().commit();
em.close();

4.  Entity Identity Management

Each EntityManager is responsible for managing the persistent identities of the managed objects in the persistence context. The following methods allow you to interact with the management of persistent identities. The behavior of these methods is deeply affected by the persistence context type of the EntityManager; see Section 3, “ Persistence Context ” for an explanation of persistence contexts.

public <T> T find(Class<T> cls, Object oid);

This method returns the persistent instance of the given type with the given persistent identity. If the instance is already present in the current persistence context, the cached version will be returned. Otherwise, a new instance will be constructed and loaded with state from the datastore. If no entity with the given type and identity exists in the datastore, this method returns null.

public <T> T getReference(Class<T> cls, Object oid);

This method is similar to find, but does not necessarily go to the database when the entity is not found in cache. The implementation may construct a hollow entity and return it to you instead. Hollow entities do not have any state loaded. The state only gets loaded when you attempt to access a persistent field. At that time, the implementation may throw an EntityNotFoundException if it discovers that the entity does not exist in the datastore. The implementation may also throw an EntityNotFoundException from the getReference method itself. Unlike find, getReference does not return null.

public boolean contains(Object entity);

Returns true if the given entity is part of the current persistence context, and false otherwise. Removed entities are not considered part of the current persistence context.

5.  Cache Management

public void flush();

The flush method writes any changes that have been made in the current transaction to the datastore. If the EntityManager does not already have a connection to the datastore, it obtains one for the flush and retains it for the duration of the transaction. Any exceptions during flush cause the transaction to be marked for rollback. See Chapter 9, Transaction .

Flushing requires an active transaction. If there isn't a transaction in progress, the flush method throws a TransactionRequiredException.

public FlushModeType getFlushMode();
public void setFlushMode(FlushModeType flushMode);

The EntityManager's FlushMode property controls whether to flush transactional changes before executing queries. This allows the query results to take into account changes you have made during the current transaction. Available javax.persistence.FlushModeType constants are:

  • COMMIT: Only flush when committing, or when told to do so through the flush method. Query results may not take into account changes made in the current transaction.

  • AUTO: The implementation is permitted to flush before queries to ensure that the results reflect the most recent object state.

You can also set the flush mode on individual Query instances.

Note

OpenJPA only flushes before a query if the query might be affected by data changed in the current transaction. Additionally, OpenJPA allows fine-grained control over flushing behavior. See the Reference Guide's Section 8, “ Configuring the Use of JDBC Connections ”.

public void clear();

Clearing the EntityManager effectively ends the persistence context. All entities managed by the EntityManager become detached.

6.  Query Factory

public Query createQuery(String query);

Query objects are used to find entities matching certain criteria. The createQuery method creates a query using the given Java Persistence Query Language (JPQL) string. See Chapter 10, JPA Query for details.

public Query createNamedQuery(String name);

This method retrieves a query defined in metadata by name. The returned Query instance is initialized with the information declared in metadata. For more information on named queries, read Section 1.11, “ Named Queries ”.

public Query createNativeQuery(String sql);
public Query createNativeQuery(String sql, Class resultCls);
public Query createNativeQuery(String sql, String resultMapping);

Native queries are queries in the datastore's native language. For relational databases, this is the Structured Query Language (SQL). Chapter 12, SQL Queries elaborates on JPA's native query support.

7.  Entity Locking

In the area of concurrency control, the JPA specification supports optimistic and pessimistic locking.

public void lock(Object entity, LockModeType mode);

This method locks the given entity using the named mode. The javax.persistence.LockModeType enum defines eight modes:

  • OPTIMISTIC: Optimistic locking.

  • OPTIMISTIC_FORCE_INCREMENT: Optimistic locking. When a transaction is committed, the entity's version column will be incremented even if the entity's state did not change in the transaction.

  • PESSIMISTIC_READ: Pessimistic locking. Other transactions may concurrently read the entity, but cannot concurrently update it.

  • PESSIMISTIC_WRITE: Pessimistic locking. Other transactions cannot concurrently read or write the entity.

  • PESSIMISTIC_FORCE_INCREMENT: Pessimistic locking. Other transactions cannot concurrently read or write the entity. When a transaction is committed, the entity's version column will be incremented even if the entity's state did not change in the transaction.

  • READ: A synonym for OPTIMISTIC.

  • WRITE: A synonym for OPTIMISTIC_FORCE_INCREMENT.

  • NONE: No locking is performed.

Entities can also be locked at the time when entity state gets loaded from the datastore. This is achieved by supplying a lock mode to the respective versions of find and refresh methods. If an entity state is to be loaded by a query, a lock mode can be passed to the Query.setLockMode and TypedQuery.setLockMode methods.

public LockModeType getLockMode(Object entity);

Returns the lock mode currently held by the given entity.

Note

  • OpenJPA differentiates between PESSIMISTIC_READ and PESSIMISTIC_WRITE lock modes only with DB2 databases. While running with other databases, there is no distinction between these two modes because PESSIMISTIC_READ lock mode is upgraded to PESSIMISTIC_WRITE.

  • OpenJPA has additional APIs for controlling entity locking. See Section 3, “ Object Locking ” in the Reference Guide for details.

8.  Retrieving Properties Information

There are two sets of properties that may be specified: those that are specific to OpenJPA and those that have been defined by the JPA specification. In some cases, two properties may be equivalent, but have different keys. For example, openjpa.LockTimeout and javax.persistence.lock.timeout are two different keys for the same property.

There are two methods that can be used to retrieve information related to properties:

public Map<String,Object> getProperties();
public Set<String> getSupportedProperties();

  • getProperties - Provides a list of current properties. If a property has more than one key, the key that will be returned is the one that was used when the property was set. If the property was not explicitly set, the key defined by the JPA specification will be returned with the default value.

  • getSupportedProperties - Returns a set of supported property keys. This includes keys defined by the JPA specification as well as keys specific to OpenJPA. If a property has more than one key, all possible keys will be returned.

Note

The getSupportedProperties method is an OpenJPA extension to the JPA specification.

9.  Closing

public boolean isOpen();
public void close();

When an EntityManager is no longer needed, you should call its close method. Closing an EntityManager releases any resources it is using. The persistence context ends, and the entities managed by the EntityManager become detached. Any Query instances the EntityManager created become invalid. Calling any method other than isOpen on a closed EntityManager results in an IllegalStateException. You cannot close a EntityManager that is in the middle of a transaction.

If you are in a managed environment using injected entity managers, you should not close them.

Chapter 9.  Transaction

Transactions are critical to maintaining data integrity. They are used to group operations into units of work that act in an all-or-nothing fashion. Transactions have the following qualities:

  • Atomicity. Atomicity refers to the all-or-nothing property of transactions. Either every data update in the transaction completes successfully, or they all fail, leaving the datastore in its original state. A transaction cannot be only partially successful.

  • Consistency. Each transaction takes the datastore from one consistent state to another consistent state.

  • Isolation. Transactions are isolated from each other. When you are reading persistent data in one transaction, you cannot "see" the changes that are being made to that data in other transactions. Similarly, the updates you make in one transaction cannot conflict with updates made in concurrent transactions. The form of conflict resolution employed depends on whether you are using pessimistic or optimistic transactions. Both types are described later in this chapter.

  • Durability. The effects of successful transactions are durable; the updates made to persistent data last for the lifetime of the datastore.

Together, these qualities are called the ACID properties of transactions. To understand why these properties are so important to maintaining data integrity, consider the following example:

Suppose you create an application to manage bank accounts. The application includes a method to transfer funds from one user to another, and it looks something like this:

public void transferFunds(User from, User to, double amnt) {
    from.decrementAccount(amnt);
    to.incrementAccount(amnt);
}

Now suppose that user Alice wants to transfer 100 dollars to user Bob. No problem; you simply invoke your transferFunds method, supplying Alice in the from parameter, Bob in the to parameter, and 100.00 as the amnt . The first line of the method is executed, and 100 dollars is subtracted from Alice's account. But then, something goes wrong. An unexpected exception occurs, or the hardware fails, and your method never completes.

You are left with a situation in which the 100 dollars has simply disappeared. Thanks to the first line of your method, it is no longer in Alice's account, and yet it was never transferred to Bob's account either. The datastore is in an inconsistent state.

The importance of transactions should now be clear. If the two lines of the transferFunds method had been placed together in a transaction, it would be impossible for only the first line to succeed. Either the funds would be transferred properly or they would not be transferred at all, and an exception would be thrown. Money could never vanish into thin air, and the data store could never get into an inconsistent state.

1.  Transaction Types

There are two major types of transactions: pessimistic transactions and optimistic transactions. Each type has both advantages and disadvantages.

Pessimistic transactions generally lock the datastore records they act on, preventing other concurrent transactions from using the same data. This avoids conflicts between transactions, but consumes database resources. Additionally, locking records can result in deadlock, a situation in which two transactions are both waiting for the other to release its locks before completing. The results of a deadlock are datastore-dependent; usually one transaction is forcefully rolled back after some specified timeout interval, and an exception is thrown.

This document will often use the term datastore transaction in place of pessimistic transaction. This is to acknowledge that some datastores do not support pessimistic semantics, and that the exact meaning of a non-optimistic JPA transaction is dependent on the datastore. Most of the time, a datastore transaction is equivalent to a pessimistic transaction.

Optimistic transactions consume less resources than pessimistic/datastore transactions, but only at the expense of reliability. Because optimistic transactions do not lock datastore records, two transactions might change the same persistent information at the same time, and the conflict will not be detected until the second transaction attempts to flush or commit. At this time, the second transaction will realize that another transaction has concurrently modified the same records (usually through a timestamp or versioning system), and will throw an appropriate exception. Note that optimistic transactions still maintain data integrity; they are simply more likely to fail in heavily concurrent situations.

Despite their drawbacks, optimistic transactions are the best choice for most applications. They offer better performance, better scalability, and lower risk of hanging due to deadlock.

Note

OpenJPA uses optimistic semantics by default, but supports both optimistic and datastore transactions. OpenJPA also offers advanced locking and versioning APIs for fine-grained control over database resource allocation and object versioning. See Section 3, “ Object Locking ” of the Reference Guide for details on locking. Section 2.6, “ Version ” of this document covers standard object versioning, while Section 7, “ Additional JPA Mappings ” of the Reference Guide discusses additional versioning strategies available in OpenJPA.

2.  The EntityTransaction Interface

JPA integrates with your container's managed transactions, allowing you to use the container's declarative transaction demarcation and its Java Transaction API (JTA) implementation for transaction management. Outside of a container, though, you must demarcate transactions manually through JPA. The EntityTransaction interface controls unmanaged transactions in JPA.

public void begin();
public void commit();
public void rollback();

The begin, commit, and rollback methods demarcate transaction boundaries. The methods should be self-explanatory: begin starts a transaction, commit attempts to commit the transaction's changes to the datastore, and rollback aborts the transaction, in which case the datastore is "rolled back" to its previous state. JPA implementations will automatically roll back transactions if any exception is thrown during the commit process.

Unless you are using an extended persistence context, committing or rolling back also ends the persistence context. All managed entities will be detached from the EntityManager.

public boolean isActive();

Finally, the isActive method returns true if the transaction is in progress (begin has been called more recently than commit or rollback), and false otherwise.

Example 9.1.  Grouping Operations with Transactions

public void transferFunds(EntityManager em, User from, User to, double amnt) {
    // note: it would be better practice to move the transaction demarcation
    // code out of this method, but for the purposes of example...
    Transaction trans = em.getTransaction();
    trans.begin();
    try
    {
        from.decrementAccount(amnt);
        to.incrementAccount(amnt);
        trans.commit();
    }
    catch (RuntimeException re)
    {
        if (trans.isActive())
            trans.rollback();   // or could attempt to fix error and retry
        throw re;
    }
}

Chapter 10.  JPA Query

Table of Contents

1. JPQL API
1.1. Query Basics
1.2. Relation Traversal
1.3. Embeddable Traversal
1.4. Fetch Joins
1.5. JPQL Functions
1.6. Polymorphic Queries
1.7. Query Parameters
1.8. Query Hints
1.8.1. Locking Hints
1.8.2. Lock Timeout Hint
1.8.3. Query Timeout Hint
1.8.4. Result Set Size Hint
1.8.5. Isolation Level Hint
1.8.6. Other Fetchplan Hints
1.8.7. Database-Specific Hints
1.8.8. Named Query Hints
1.8.9. Handling of Multiple Similar Query Hints
1.9. Ordering
1.10. Aggregates
1.11. Named Queries
1.12. Delete By Query
1.13. Update By Query
2. JPQL Language Reference
2.1. JPQL Statement Types
2.1.1. JPQL Select Statement
2.1.2. JPQL Update and Delete Statements
2.2. JPQL Abstract Schema Types and Query Domains
2.2.1. JPQL Entity Naming
2.2.2. JPQL Schema Example
2.3. JPQL FROM Clause and Navigational Declarations
2.3.1. JPQL FROM Identifiers
2.3.2. JPQL Identification Variables
2.3.3. JPQL Range Declarations
2.3.4. JPQL Path Expressions
2.3.5. JPQL Joins
2.3.5.1. JPQL Inner Joins (Relationship Joins)
2.3.5.2. JPQL Outer Joins
2.3.5.3. JPQL Fetch Joins
2.3.6. JPQL Collection Member Declarations
2.3.7. JPQL FROM Clause and SQL
2.3.8. JPQL Polymorphism
2.4. JPQL WHERE Clause
2.5. JPQL Conditional Expressions
2.5.1. JPQL Literals
2.5.2. JPQL Identification Variables
2.5.3. JPQL Path Expressions
2.5.4. JPQL Input Parameters
2.5.4.1. JPQL Positional Parameters
2.5.4.2. JPQL Named Parameters
2.5.5. JPQL Conditional Expression Composition
2.5.6. JPQL Operators and Operator Precedence
2.5.7. JPQL Comparison Expressions
2.5.8. JPQL Between Expressions
2.5.9. JPQL In Expressions
2.5.10. JPQL Like Expressions
2.5.11. JPQL Null Comparison Expressions
2.5.12. JPQL Empty Collection Comparison Expressions
2.5.13. JPQL Collection Member Expressions
2.5.14. JPQL Exists Expressions
2.5.15. JPQL All or Any Expressions
2.5.16. JPQL Subqueries
2.6. JPQL Scalar Expressions
2.6.1. Arithmetic Expressions
2.6.2. String, Arithmetic, and Datetime Functional Expressions
2.6.2.1. JPQL String Functions
2.6.2.2. JPQL Arithmetic Functions
2.6.2.3. JPQL Datetime Functions
2.6.3. Case Expressions
2.6.4. Entity Type Expressions
2.7. JPQL GROUP BY, HAVING
2.8. JPQL SELECT Clause
2.8.1. JPQL Result Type of the SELECT Clause
2.8.2. JPQL Constructor Expressions
2.8.3. JPQL Null Values in the Query Result
2.8.4. JPQL Embeddables in the Query Result
2.8.5. JPQL Aggregate Functions
2.8.5.1. JPQL Aggregate Examples
2.8.5.2. JPQL Numeric Expressions in the SELECT Clause
2.9. JPQL ORDER BY Clause
2.10. JPQL Bulk Update and Delete
2.11. JPQL Null Values
2.12. JPQL Equality and Comparison Semantics
2.13. JPQL BNF

The javax.persistence.Query interface is the mechanism for issuing queries in JPA. The primary query language used is the Java Persistence Query Language, or JPQL. JPQL is syntactically very similar to SQL, but is object-oriented rather than table-oriented.

The API for executing JPQL queries will be discussed in Section 1, “ JPQL API ”, and a full language reference will be covered in Section 2, “ JPQL Language Reference ”.

1.  JPQL API

1.1.  Query Basics

SELECT x FROM Magazine x

The preceding is a simple JPQL query for all Magazine entities.

public Query createQuery(String jpql);

The EntityManager.createQuery method creates a Query instance from a given JPQL string.

public List getResultList();

Invoking Query.getResultList executes the query and returns a List containing the matching objects. The following example executes our Magazine query above:

EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x");
List<Magazine> results = (List<Magazine>) q.getResultList();

A JPQL query has an internal namespace declared in the from clause of the query. Arbitrary identifiers are assigned to entities so that they can be referenced elsewhere in the query. In the query example above, the identifier x is assigned to the entity Magazine .

Note

The as keyword can optionally be used when declaring identifiers in the from clause. SELECT x FROM Magazine x and SELECT x FROM Magazine AS x are synonymous.

Following the select clause of the query is the object or objects that the query returns. In the case of the query above, the query's result list will contain instances of the Magazine class.

Note

When selecting entities, you can optionally use the keyword object . The clauses select x and SELECT OBJECT(x) are synonymous.

The optional where clause places criteria on matching results. For example:

SELECT x FROM Magazine x WHERE x.title = 'JDJ'

Keywords in JPQL expressions are case-insensitive, but entity, identifier, and member names are not. For example, the expression above could also be expressed as:

select x from Magazine x where x.title = 'JDJ'

But it could not be expressed as:

SELECT x FROM Magazine x WHERE x.TITLE = 'JDJ'

As with the select clause, alias names in the where clause are resolved to the entity declared in the from clause. The query above could be described in English as "for all Magazine instances x, return a list of every x such that x's title field is equal to 'JDJ'".

JPQL uses SQL-like syntax for query criteria. The and and or logical operators chain multiple criteria together:

SELECT x FROM Magazine x WHERE x.title = 'JDJ' OR x.title = 'JavaPro'

The = operator tests for equality. <> tests for inequality. JPQL also supports the following arithmetic operators for numeric comparisons: >, >=, <, <=. For example:

SELECT x FROM Magazine x WHERE x.price > 3.00 AND x.price <= 5.00

This query returns all magazines whose price is greater than 3.00 and less than or equal to 5.00.

SELECT x FROM Magazine x WHERE x.price <> 3.00

This query returns all Magazines whose price is not equal to 3.00.

You can group expressions together using parentheses in order to specify how they are evaluated. This is similar to how parentheses are used in Java. For example:

SELECT x FROM Magazine x WHERE (x.price > 3.00 AND x.price <= 5.00) OR x.price < 7.00

This expression would match magazines whose price is less than 7.00. Alternately:

SELECT x FROM Magazine x WHERE x.price > 3.00 AND (x.price <= 5.00 OR x.price < 7.00)

This expression would match magazines whose price is 4.00, 5.00 or 6.00, but not 1.00, 2.00 or 3.00.

JPQL also includes the following conditionals:

  • [NOT] BETWEEN: Shorthand for expressing that a value falls between two other values. The following two statements are synonymous:

    SELECT x FROM Magazine x WHERE x.price >= 3.00 AND x.price <= 5.00
    
    SELECT x FROM Magazine x WHERE x.price BETWEEN 3.00 AND 5.00
    
  • [NOT] LIKE: Performs a string comparison with wildcard support. The special character '_' in the parameter means to match any single character, and the special character '%' means to match any sequence of characters. The following statement matches title fields "JDJ" and "JavaPro", but not "IT Insider":

    SELECT x FROM Magazine x WHERE x.title LIKE 'J%'
    

    The following statement matches the title field "JDJ" but not "JavaPro":

    SELECT x FROM Magazine x WHERE x.title LIKE 'J__'
    
  • [NOT] IN: Specifies that the member must be equal to one element of the provided list. The following two statements are synonymous:

    SELECT x FROM Magazine x WHERE x.title IN ('JDJ', 'JavaPro', 'IT Insider')
    
    SELECT x FROM Magazine x WHERE x.title = 'JDJ' OR x.title = 'JavaPro' OR x.title = 'IT Insider'
    
  • IS [NOT] EMPTY: Specifies that the collection field holds no elements. For example:

    SELECT x FROM Magazine x WHERE x.articles is empty
    

    This statement will return all magazines whose articles member contains no elements.

  • IS [NOT] NULL: Specifies that the field is equal to null. For example:

    SELECT x FROM Magazine x WHERE x.publisher is null
    

    This statement will return all Magazine instances whose "publisher" field is set to null.

  • NOT: Negates the contained expression. For example, the following two statements are synonymous:

    SELECT x FROM Magazine x WHERE NOT(x.price = 10.0)
    
    SELECT x FROM Magazine x WHERE x.price <> 10.0
    

1.2.  Relation Traversal

Relations between objects can be traversed using Java-like syntax. For example, if the Magazine class has a field named "publisher" of type Company, that relation can be queried as follows:

SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House'

This query returns all Magazine instances whose publisher field is set to a Company instance whose name is "Random House".

Single-valued relation traversal implies that the relation is not null. In SQL terms, this is known as an inner join. If you want to also include relations that are null, you can specify:

SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House' or x.publisher is null

You can also traverse collection fields in queries, but you must declare each traversal in the from clause. Consider:

SELECT x FROM Magazine x, IN(x.articles) y WHERE y.authorName = 'John Doe'

This query says that for each Magazine x , traverse the articles relation and check each Article y, and pass the filter if y's authorName field is equal to "John Doe". In short, this query will return all magazines that have any articles written by John Doe.

Note

The IN() syntax can also be expressed with the keywords inner join. The statements SELECT x FROM Magazine x, IN(x.articles) y WHERE y.authorName = 'John Doe' and SELECT x FROM Magazine x inner join x.articles y WHERE y.authorName = 'John Doe' are synonymous.

1.3.  Embeddable Traversal

Similar to relation traversal, nested embeddable objects can be traversed using Java-like syntax. For example, if the Company class has a field named "address" of an embeddable type Address, and the Address has a field named "geocode" of an embeddable type Geocode, the geocode of a company's address can be queried as follows:

SELECT c.address.geocode FROM Company c WHERE c.name = 'Random House'

Note

The geocode returned by the above query will not be part of the state of any managed entity. Modifications to these embeddable instances are not allowed.

Traversal into embeddable's state field is also allowed as shown in the following query:

SELECT c.address.geocode.latitude FROM Company c WHERE c.name = 'Random House'

Embeddable objects may contain single-valued or collection-valued relations. These relations can also be traversed using Java-like syntax. For example, if the Address has a relation field named "phoneLists" of an entity type PhoneNumber, the following query returns the PhoneNumber entities of the Company named 'Random House':

SELECT p FROM Company c, IN(c.address.phoneLists) p WHERE c.name = 'Random House'

1.4.  Fetch Joins

JPQL queries may specify one or more join fetch declarations, which allow the query to specify which fields in the returned instances will be pre-fetched.

SELECT x FROM Magazine x join fetch x.articles WHERE x.title = 'JDJ'

The query above returns Magazine instances and guarantees that the articles field will already be fetched in the returned instances.

Multiple fields may be specified in separate join fetch declarations:

SELECT x FROM Magazine x join fetch x.articles join fetch x.authors WHERE x.title = 'JDJ'

Notice that in the above query, both articles and authors are relation property in Magazine. JPQL syntax does not allow range variable declared for paths on the right-hand side of join fetch. Therefore, if Article entity has a relation property of publishers, it is not possible to specify a query that returns Magazine instances and pre-fetch the articles and the publishers. The following query will result in syntax error:

SELECT x FROM Magazine x join fetch x.articles a join fetch a.publishers p WHERE x.title = 'JDJ'

Note

Specifying the join fetch declaration is functionally equivalent to adding the fields to the Query's FetchConfiguration. See Section 7, “ Fetch Groups ”.

1.5.  JPQL Functions

As well as supporting direct field and relation comparisons, JPQL supports a pre-defined set of functions that you can apply.

  • CONCAT(string1, string2): Concatenates two string fields or literals. For example:

    SELECT x FROM Magazine x WHERE CONCAT(x.title, 's') = 'JDJs'
    
  • SUBSTRING(string, startIndex, [length]): Returns the part of the string argument starting at startIndex (1-based) and optionally ending at length characters past startIndex. If the length argument is not specified, the substring from the startIndex to the end of the string is returned.

    SELECT x FROM Magazine x WHERE SUBSTRING(x.title, 1, 1) = 'J'
    
  • TRIM([LEADING | TRAILING | BOTH] [character FROM] string: Trims the specified character from either the beginning ( LEADING ) end ( TRAILING) or both ( BOTH ) of the string argument. If no trim character is specified, the space character will be trimmed.

    SELECT x FROM Magazine x WHERE TRIM(BOTH 'J' FROM x.title) = 'D'
    
  • LOWER(string): Returns the lower-case of the specified string argument.

    SELECT x FROM Magazine x WHERE LOWER(x.title) = 'jdj'
    
  • UPPER(string): Returns the upper-case of the specified string argument.

    SELECT x FROM Magazine x WHERE UPPER(x.title) = 'JAVAPRO'
    
  • LENGTH(string): Returns the number of characters in the specified string argument.

    SELECT x FROM Magazine x WHERE LENGTH(x.title) = 3
    
  • LOCATE(searchString, candidateString [, startIndex]): Returns the first index of searchString in candidateString. Positions are 1-based. If the string is not found, returns 0.

    SELECT x FROM Magazine x WHERE LOCATE('D', x.title) = 2
    
  • ABS(number): Returns the absolute value of the argument.

    SELECT x FROM Magazine x WHERE ABS(x.price) >= 5.00
    
  • SQRT(number): Returns the square root of the argument.

    SELECT x FROM Magazine x WHERE SQRT(x.price) >= 1.00
    
  • MOD(number, divisor): Returns the modulo of number and divisor.

    SELECT x FROM Magazine x WHERE MOD(x.price, 10) = 0
    
  • INDEX(identification_variable): Returns an integer value corresponding to the position of its argument in an ordered list. The INDEX function can only be applied to identification variables denoting types for which an order column has been specified.

    In the following example, studentWaitlist is a list of students for which an order column has been specified, the query returns the name of the first student on the waiting list of the course named 'Calculus':

    SELECT w.name FROM Course c JOIN c.studentWaitlist w WHERE c.name = ‘Calculus’ AND INDEX(w) = 0
    
  • CURRENT_DATE: Returns the current date.

  • CURRENT_TIME: Returns the current time.

  • CURRENT_TIMESTAMP: Returns the current timestamp.

1.6.  Polymorphic Queries

All JPQL queries are polymorphic, which means the from clause of a query includes not only instances of the specific entity class to which it refers, but all subclasses of that class as well. The instances returned by a query include instances of the subclasses that satisfy the query conditions. For example, the following query may return instances of Magazine , as well as Tabloid and Digest instances, where Tabloid and Digest are Magazine subclasses.

SELECT x FROM Magazine x WHERE x.price < 5

Non-polymorphic queries or queries whose polymorphism is restricted can be specified using entity type expressions (see Section 2.6.4, “ Entity Type Expressions ” ) in the WHERE clause to restrict the domain of the query. For example, the following query returns instances of Digest:

SELECT x FROM Magazine WHERE TYPE(x) = Digest

1.7.  Query Parameters

JPQL provides support for parameterized queries. Either named parameters or positional parameters may be specified in the query string. Parameters allow you to re-use query templates where only the input parameters vary. A single query can declare either named parameters or positional parameters, but is not allowed to declare both named and positional parameters.

public Query setParameter (int pos, Object value);

Specify positional parameters in your JPQL string using an integer prefixed by a question mark. You can then populate the Query object with positional parameter values via calls to the setParameter method above. The method returns the Query instance for optional method chaining.

EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = ?1 and x.price > ?2");
q.setParameter(1, "JDJ").setParameter(2, 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();

This code will substitute JDJ for the ?1 parameter and 5.0 for the ?2 parameter, then execute the query with those values.

public Query setParameter(String name, Object value);

Named parameters are denoted by prefixing an arbitrary name with a colon in your JPQL string. You can then populate the Query object with parameter values using the method above. Like the positional parameter method, this method returns the Query instance for optional method chaining.

EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = :titleParam and x.price > :priceParam");
q.setParameter("titleParam", "JDJ").setParameter("priceParam", 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();

This code substitutes JDJ for the :titleParam parameter and 5.0 for the :priceParam parameter, then executes the query with those values.

All input parameters must be single-valued, except in IN expressions (see Section 2.5.9, “ JPQL In Expressions ”), which support the use of collection-valued input parameters.

1.8.  Query Hints

JPQL provides support for hints which are name/value pairs used to control locking and optimization keywords in SQL. The following example shows how to use the JPA hint API to set the ReadLockMode and ResultCount in the OpenJPA fetch plan. This will result in a database-specific SQL keyword (usually FOR UPDATE) to be emitted into the SQL provided that a pessimistic LockManager is being used. Additionally, if a DB2 database is being used, the OPTIMIZE FOR 2 ROWS clause will also be emitted.

Example 10.1.  Query Hints

...
Query q = em.createQuery("select m from Magazine m where ... ");
q.setHint("openjpa.hint.OptimizeResultCount", new Integer(2));
q.setHint("openjpa.FetchPlan.ReadLockMode","WRITE");
List r = q.getResultList();
...

Hints which can not be processed by a particular database or are unknown to OpenJPA are ignored. Hints known to OpenJPA but supplied with an incompatible value will result in an IllegalArgumentException being thrown.

1.8.1.  Locking Hints

To avoid deadlock and optimistic update exceptions among multiple updaters, use a pessimistic LockManager, specified in the persistence unit definition, and use a hint name of "openjpa.FetchPlan.ReadLockMode" on queries for entities that must be locked for serialization. The value of ReadLockMode can be either "READ" or "WRITE". This results in a database-specific locking keyword (usually FOR UPDATE) to be emitted into the SQL.

Using a ReadLockMode hint with JPA optimistic locking (i.e. specifying LockManager = "version") will result in the entity version field either being reread at end of transaction in the case of a value of "READ" or the version field updated at end of transaction in the case of "WRITE". You must define a version field in the entity mapping when using a version LockManager and using ReadLockMode.

Table 10.1.  Interaction of ReadLockMode hint and LockManager

ReadLockMode LockManager=pessimistic LockManager=version
READ SQL with FOR UPDATE SQL without FOR UPDATE;

reread version field at the end of transaction and check for no change.

WRITE SQL with FOR UPDATE SQL without FOR UPDATE;

force update version field at the end of transaction

not specified SQL without FOR UPDATE SQL without FOR UPDATE

1.8.2.  Lock Timeout Hint

To specify a lock timeout hint in milliseconds to those databases that support it, specify a hint name of "openjpa.LockTimeout" or "javax.persistence.lock.timeout" with an integer value greater than zero, or zero for no timeout which is the default behavior.

1.8.3.  Query Timeout Hint

To specify a query timeout hint in milliseconds to those database drivers that support it, specify a hint name of "javax.persistence.query.timeout" with an integer value greater than zero, or zero for no timeout which is the default behavior.

1.8.4.  Result Set Size Hint

To specify a result set size hint to those databases that support it, specify a hint name of "openjpa.hint.OptimizeResultCount" with an integer value greater than zero. This causes the SQL keyword OPTIMIZE FOR to be generated.

1.8.5.  Isolation Level Hint

To specify an isolation level, specify a hint name of "openjpa.FetchPlan.Isolation". The value will be used to specify isolation level using the SQL WITH <isolation> clause for those databases that support it. This hint only works in conjunction with the ReadLockMode hint.

1.8.6.  Other Fetchplan Hints

Any property of an OpenJPA FetchPlan can be changed using a hint by using a name of the form "openjpa.FetchPlan."<property name>. Valid property names include: MaxFetchDepth, FetchBatchSize, LockTimeOut, EagerFetchMode, SubclassFetchMode and Isolation.

1.8.7.  Database-Specific Hints

The hint names "openjpa.hint.MySQLSelectHint" and "openjpa.hint.OracleSelectHint" can be used to specify a string value of a query hint that will be inserted into SQL for MySQL and Oracle databases. See Section 19.1, “ Using Query Hints with MySQL ” and Section 21.1, “ Using Query Hints with Oracle ” for examples.

1.8.8.  Named Query Hints

Hints can also be included as part of a NamedQuery definition.

Example 10.2.  Named Query using Hints

...
@NamedQuery(name="magsOverPrice",
    query="SELECT x FROM Magazine x WHERE x.price > ?1",
    hints={
        @QueryHint(name="openjpa.hint.OptimizeResultCount", value="2"),
        @QueryHint(name="openjpa.FetchPlan.ReadLockMode", value="WRITE")
    }
)
...

1.8.9.  Handling of Multiple Similar Query Hints

When similar hints in different prefix scopes are specified in a query, the following prefix precedence order is used to select the effective hint:

  • javax.persistence.*
  • openjpa.FetchPlan.*
  • openjpa.jdbc.*
  • openjpa.*

Example 10.3.  Setting Multiple Similar Query Hints

...
Query q = em.createQuery(.....);
q.setHint("openjpa.FetchPlan.LockTimeout", 1000);
q.setHint("javax.persistence.lock.timeout", 2000);
q.setHint("openjpa.LockTimeout", 3000);
// Lock time out of 2000 ms is in effect for query q
...


1.9.  Ordering

JPQL queries may optionally contain an order by clause which specifies one or more fields to order by when returning query results. You may follow the order by field clause with the asc or desc keywords, which indicate that ordering should be ascending or descending, respectively. If the direction is omitted, ordering is ascending by default.

SELECT x FROM Magazine x order by x.title asc, x.price desc

The query above returns Magazine instances sorted by their title in ascending order. In cases where the titles of two or more magazines are the same, those instances will be sorted by price in descending order.

1.10.  Aggregates

JPQL queries can select aggregate data as well as objects. JPQL includes the min, max, avg, and count aggregates. These functions can be used for reporting and summary queries.

The following query will return the average of all the prices of all the magazines:

EntityManager em = ...
Query q = em.createQuery("SELECT AVG(x.price) FROM Magazine x");
Number result = (Number) q.getSingleResult();

The following query will return the highest price of all the magazines titled "JDJ":

EntityManager em = ...
Query q = em.createQuery("SELECT MAX(x.price) FROM Magazine x WHERE x.title = 'JDJ'");
Number result = (Number) q.getSingleResult();

1.11.  Named Queries

Query templates can be statically declared using the NamedQuery and NamedQueries annotations. For example:

@Entity
@NamedQueries({
    @NamedQuery(name="magsOverPrice",
        query="SELECT x FROM Magazine x WHERE x.price > ?1"),
    @NamedQuery(name="magsByTitle",
        query="SELECT x FROM Magazine x WHERE x.title = :titleParam")
})
public class Magazine {
    ...
}

These declarations will define two named queries called magsOverPrice and magsByTitle.

public Query createNamedQuery(String name);

You retrieve named queries with the above EntityManager method. For example:

EntityManager em = ...
Query q = em.createNamedQuery("magsOverPrice");
q.setParameter(1, 5.0f);
List<Magazine> results = (List<Magazine>) q.getResultList();
EntityManager em = ...
Query q = em.createNamedQuery("magsByTitle");
q.setParameter("titleParam", "JDJ");
List<Magazine> results = (List<Magazine>) q.getResultList();

1.12.  Delete By Query

Queries are useful not only for finding objects, but for efficiently deleting them as well. For example, you might delete all records created before a certain date. Rather than bring these objects into memory and delete them individually, JPA allows you to perform a single bulk delete based on JPQL criteria.

Delete by query uses the same JPQL syntax as normal queries, with one exception: begin your query string with the delete keyword instead of the select keyword. To then execute the delete, you call the following Query method:

public int executeUpdate();

This method returns the number of objects deleted. The following example deletes all subscriptions whose expiration date has passed.

Example 10.4.  Delete by Query

Query q = em.createQuery("DELETE FROM Subscription s WHERE s.subscriptionDate < :today");
q.setParameter("today", new Date());
int deleted = q.executeUpdate();

1.13.  Update By Query

Similar to bulk deletes, it is sometimes necessary to perform updates against a large number of queries in a single operation, without having to bring all the instances down to the client. Rather than bring these objects into memory and modifying them individually, JPA allows you to perform a single bulk update based on JPQL criteria.

Update by query uses the same JPQL syntax as normal queries, except that the query string begins with the update keyword instead of select. To execute the update, you call the following Query method:

public int executeUpdate();

This method returns the number of objects updated. The following example updates all subscriptions whose expiration date has passed to have the "paid" field set to true..

Example 10.5.  Update by Query

Query q = em.createQuery("UPDATE Subscription s SET s.paid = :paid WHERE s.subscriptionDate < :today");
q.setParameter("today", new Date());
q.setParameter("paid", true);
int updated = q.executeUpdate();

2.  JPQL Language Reference

2.1. JPQL Statement Types
2.1.1. JPQL Select Statement
2.1.2. JPQL Update and Delete Statements
2.2. JPQL Abstract Schema Types and Query Domains
2.2.1. JPQL Entity Naming
2.2.2. JPQL Schema Example
2.3. JPQL FROM Clause and Navigational Declarations
2.3.1. JPQL FROM Identifiers
2.3.2. JPQL Identification Variables
2.3.3. JPQL Range Declarations
2.3.4. JPQL Path Expressions
2.3.5. JPQL Joins
2.3.5.1. JPQL Inner Joins (Relationship Joins)
2.3.5.2. JPQL Outer Joins
2.3.5.3. JPQL Fetch Joins
2.3.6. JPQL Collection Member Declarations
2.3.7. JPQL FROM Clause and SQL
2.3.8. JPQL Polymorphism
2.4. JPQL WHERE Clause
2.5. JPQL Conditional Expressions
2.5.1. JPQL Literals
2.5.2. JPQL Identification Variables
2.5.3. JPQL Path Expressions
2.5.4. JPQL Input Parameters
2.5.4.1. JPQL Positional Parameters
2.5.4.2. JPQL Named Parameters
2.5.5. JPQL Conditional Expression Composition
2.5.6. JPQL Operators and Operator Precedence
2.5.7. JPQL Comparison Expressions
2.5.8. JPQL Between Expressions
2.5.9. JPQL In Expressions
2.5.10. JPQL Like Expressions
2.5.11. JPQL Null Comparison Expressions
2.5.12. JPQL Empty Collection Comparison Expressions
2.5.13. JPQL Collection Member Expressions
2.5.14. JPQL Exists Expressions
2.5.15. JPQL All or Any Expressions
2.5.16. JPQL Subqueries
2.6. JPQL Scalar Expressions
2.6.1. Arithmetic Expressions
2.6.2. String, Arithmetic, and Datetime Functional Expressions
2.6.2.1. JPQL String Functions
2.6.2.2. JPQL Arithmetic Functions
2.6.2.3. JPQL Datetime Functions
2.6.3. Case Expressions
2.6.4. Entity Type Expressions
2.7. JPQL GROUP BY, HAVING
2.8. JPQL SELECT Clause
2.8.1. JPQL Result Type of the SELECT Clause
2.8.2. JPQL Constructor Expressions
2.8.3. JPQL Null Values in the Query Result
2.8.4. JPQL Embeddables in the Query Result
2.8.5. JPQL Aggregate Functions
2.8.5.1. JPQL Aggregate Examples
2.8.5.2. JPQL Numeric Expressions in the SELECT Clause
2.9. JPQL ORDER BY Clause
2.10. JPQL Bulk Update and Delete
2.11. JPQL Null Values
2.12. JPQL Equality and Comparison Semantics
2.13. JPQL BNF

The Java Persistence Query Language (JPQL) is used to define searches against persistent entities independent of the mechanism used to store those entities. As such, JPQL is "portable", and not constrained to any particular data store. The Java Persistence query language is an extension of the Enterprise JavaBeans query language, EJB QL, adding operations such as bulk deletes and updates, join operations, aggregates, projections, and subqueries. Furthermore, JPQL queries can be declared statically in metadata, or can be dynamically built in code. This chapter provides the full definition of the language.

Note

Much of this section is paraphrased or taken directly from Chapter 4 of the JSR 317 Java Persistence API Specification.

2.1.  JPQL Statement Types

A JPQL statement may be either a SELECT statement, an UPDATE statement, or a DELETE statement. This chapter refers to all such statements as "queries". Where it is important to distinguish among statement types, the specific statement type is referenced. In BNF syntax, a query language statement is defined as:

  • QL_statement ::= select_statement | update_statement | delete_statement

The complete BNF for JPQL is defined in Section 2.13, “ JPQL BNF ”. Any JPQL statement may be constructed dynamically or may be statically defined in a metadata annotation or XML descriptor element. All statement types may have parameters, as discussed in Section 2.5.4, “ JPQL Input Parameters ”.

2.1.1.  JPQL Select Statement

A select statement is a string which consists of the following clauses:

  • a SELECT clause, which determines the type of the objects or values to be selected.

  • a FROM clause, which provides declarations that designate the domain to which the expressions specified in the other clauses of the query apply.

  • an optional WHERE clause, which may be used to restrict the results that are returned by the query.

  • an optional GROUP BY clause, which allows query results to be aggregated in terms of groups.

  • an optional HAVING clause, which allows filtering over aggregated groups.

  • an optional ORDER BY clause, which may be used to order the results that are returned by the query.

In BNF syntax, a select statement is defined as:

  • select_statement ::= select_clause from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

A select statement must always have a SELECT and a FROM clause. The square brackets [] indicate that the other clauses are optional.

2.1.2.  JPQL Update and Delete Statements

Update and delete statements provide bulk operations over sets of entities. In BNF syntax, these operations are defined as:

  • update_statement ::= update_clause [where_clause]

  • delete_statement ::= delete_clause [where_clause]

The update and delete clauses determine the type of the entities to be updated or deleted. The WHERE clause may be used to restrict the scope of the update or delete operation. Update and delete statements are described further in Section 2.10, “ JPQL Bulk Update and Delete ”.

2.2.  JPQL Abstract Schema Types and Query Domains

The Java Persistence query language is a typed language, and every expression has a type. The type of an expression is derived from the structure of the expression, the abstract schema types of the identification variable declarations, the types to which the persistent fields and relationships evaluate, and the types of literals.

The abstract schema type of an entity or embeddable is derived from the entity class and the metadata information provided by Java language annotations or in the XML descriptor.

Informally, the abstract schema type of an entity or embeddable can be characterized as follows:

  • For every persistent field or get accessor method (for a persistent property) of the entity class, there is a field ("state-field") whose abstract schema type corresponds to that of the field or the result type of the accessor method.

  • For every persistent relationship field or get accessor method (for a persistent relationship property) of the entity class, there is a field ("association-field") whose type is the abstract schema type of the related entity (or, if the relationship is a one-to-many or many-to-many, a collection of such).

Abstract schema types are specific to the query language data model. The persistence provider is not required to implement or otherwise materialize an abstract schema type.

The domain of a query consists of the abstract schema types of all entities and embeddables that are defined in the same persistence unit.

The domain of a query may be restricted by the navigability of the relationships of the entity and associated embeddable classes on which it is based. The association-fields of an entity's or embeddable's abstract schema type determine navigability. Using the association fields and their values, a query can select related entities and use their abstract schema types in the query.

2.2.1.  JPQL Entity Naming

Entities are designated in query strings by their entity names. The entity name is defined by the name element of the Entity annotation (or the entity-name XML descriptor element), and defaults to the unqualified name of the entity class. Entity names are scoped within the persistence unit and must be unique within the persistence unit.

2.2.2.  JPQL Schema Example

This example assumes that the application developer provides several entity classes, representing magazines, publishers, authors, and articles. The abstract schema types for these entities are Magazine, Publisher, Author, and Article.

Several Entities with Abstract Persistence Schemas Defined in the Same Persistence Unit. The entity Publisher has a one-to-many relationships with Magazine. There is also a one-to-many relationship between Magazine and Article . The entity Article is related to Author in a one-to-one relationship.

Queries to select magazines can be defined by navigating over the association-fields and state-fields defined by Magazine and Author. A query to find all magazines that have unpublished articles is as follows:

SELECT DISTINCT mag FROM Magazine AS mag JOIN mag.articles AS art WHERE art.published = FALSE

This query navigates over the association-field authors of the abstract schema type Magazine to find articles, and uses the state-field published of Article to select those magazines that have at least one article that is not published. Although predefined reserved identifiers, such as DISTINCT, FROM, AS, JOIN, WHERE, and FALSE appear in upper case in this example, predefined reserved identifiers are case insensitive.

The SELECT clause of this example designates the return type of this query to be of type Magazine.

Because the same persistence unit defines the abstract persistence schemas of the related entities, the developer can also specify a query over articles that utilizes the abstract schema type for products, and hence the state-fields and association-fields of both the abstract schema types Magazine and Author. For example, if the abstract schema type Author has a state-field named firstName, a query over articles can be specified using this state-field. Such a query might be to find all magazines that have articles authored by someone with the first name "John".

SELECT DISTINCT mag FROM Magazine mag JOIN mag.articles art JOIN art.author auth WHERE auth.firstName = 'John'

Because Magazine is related to Author by means of the relationships between Magazine and Article and between Article and Author, navigation using the association-fields authors and product is used to express the query. This query is specified by using the abstract schema name Magazine, which designates the abstract schema type over which the query ranges. The basis for the navigation is provided by the association-fields authors and product of the abstract schema types Magazine and Article respectively.

2.3.  JPQL FROM Clause and Navigational Declarations

The FROM clause of a query defines the domain of the query by declaring identification variables. An identification variable is an identifier declared in the FROM clause of a query. The domain of the query may be constrained by path expressions (See section Section 2.3.4, “ JPQL Path Expressions ”.

Identification variables designate instances of a particular entity abstract schema type. The FROM clause can contain multiple identification variable declarations separated by a comma (,).

  • from_clause ::= FROM identification_variable_declaration {, {identification_variable_declaration | collection_member_declaration}}*

  • identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*

  • range_variable_declaration ::= abstract_schema_name [AS] identification_variable

  • join ::= join_spec join_association_path_expression [AS] identification_variable

  • fetch_join ::= join_spec FETCH join_association_path_expression

  • join_association_path_expression ::= join_collection_valued_path_expression | join_single_valued_association_path_expression

  • join_collection_valued_path_expression::= identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field

  • join_single_valued_path_expression::= identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field

  • join_spec ::= [ LEFT [OUTER] | INNER ] JOIN

  • collection_member_declaration ::= IN (collection_valued_path_expression) [AS] identification_variable

The following subsections discuss the constructs used in the FROM clause.

2.3.1.  JPQL FROM Identifiers

An identifier is a character sequence of unlimited length. The character sequence must begin with a Java identifier start character, and all other characters must be Java identifier part characters. An identifier start character is any character for which the method Character.isJavaIdentifierStart returns true. This includes the underscore (_) character and the dollar sign ($) character. An identifier part character is any character for which the method Character.isJavaIdentifierPart returns true. The question mark (?) character is reserved for use by the Java Persistence query language. The following are reserved identifiers:

  • ABS

  • ALL

  • AND

  • ANY

  • AS

  • ASC

  • AVG

  • BETWEEN

  • BOTH

  • BY

  • CASE

  • CLASS

  • COALESCE

  • CONCAT

  • COUNT

  • CURRENT_DATE

  • CURRENT_TIME

  • CURRENT_TIMESTAMP

  • DELETE

  • DESC

  • DISTINCT

  • ELSE

  • EMPTY

  • END

  • ENTRY

  • ESCAPE

  • EXISTS

  • FALSE

  • FETCH

  • FROM

  • GROUP

  • HAVING

  • IN

  • INDEX

  • INNER

  • IS

  • JOIN

  • KEY

  • LEADING

  • LEFT

  • LENGTH

  • LIKE

  • LOCATE

  • LOWER

  • MAX

  • MEMBER

  • MIN

  • MOD

  • NEW

  • NOT

  • NULL

  • NULLIF

  • OBJECT

  • OF

  • OR

  • ORDER

  • OUTER

  • SELECT

  • SET

  • SIZE

  • SOME

  • SQRT

  • SIBSTRING

  • SUM

  • THEN

  • TRAILING

  • TRIM

  • TRUE

  • TYPE

  • UPDATE

  • UPPER

  • VALUE

  • WHEN

  • WHERE

  • CHARACTER_LENGTH

  • CHAR_LENGTH

  • BIT_LENGTH

  • POSITION

  • UNKNOWN

Reserved identifiers are case insensitive. Reserved identifiers must not be used as identification variables or result variables.

Note

It is recommended that other SQL reserved words also not be used as identification variables in queries because they may be used as reserved identifiers in future releases of the specification.

Note

BIT_LENGTH, CHAR_LENGTH, CHARACTER_LENGTH, POSITION, and UNKNOWN are not currently used: they are reserved for future use.

2.3.2.  JPQL Identification Variables

An identification variable is a valid identifier declared in the FROM clause of a query.

All identification variables must be declared in the FROM clause. Identification variables cannot be declared in other clauses.

An identification variable must not be a reserved identifier or have the same name as any entity in the same persistence unit.

Identification variables are case insensitive.

An identification variable evaluates to a value of the type of the expression used in declaring the variable. For example, consider the previous query:

SELECT DISTINCT mag FROM Magazine mag JOIN mag.articles art JOIN art.author auth WHERE auth.firstName = 'John'

In the FROM clause declaration mag.articles art, the identification variable art evaluates to any Article value directly reachable from Magazine. The association-field articles is a collection of instances of the abstract schema type Article and the identification variable art refers to an element of this collection. The type of auth is the abstract schema type of Author.

An identification variable can range over an entity, embeddable, or basic abstract schema type. An identification variable designates an instance of an entity abstract schema type or an element of a collection of entity abstract schema type instances.

Note that for identification variables referring to an instance of an association or collection represented as a java.util.Map, the identification variable is of the abstract schema type of the map value.

An identification variable always designates a reference to a single value. It is declared in one of three ways: in a range variable declaration, in a join clause, or in a collection member declaration. The identification variable declarations are evaluated from left to right in the FROM clause, and an identification variable declaration can use the result of a preceding identification variable declaration of the query string.

All identification variables used in the SELECT, WHERE, ORDER BY, GROUP BY, or HAVING clause of a SELECT or DELETE statement must be declared in the FROM clause. The identification variables used in the WHERE clause of an UPDATE statement must be declared in the UPDATE clause.

Identification variables are existentially quantified in these clauses. This means that an identification variable represents a member of a collection or an instance of an entity’s abstract schema type. An identification variable never designates a collection in its entirety.

An identification variable is scoped to the query (or subquery) in which it is defined and is also visible to any subqueries within that query scope that do not define an identification variable of the same name.

2.3.3.  JPQL Range Declarations

The syntax for declaring an identification variable as a range variable is similar to that of SQL; optionally, it uses the AS keyword. A range variable designates an entity abstract schema type.

Note

A range variable must not designate an embeddable class abstract schema type.

  • range_variable_declaration ::= entity_name [AS] identification_variable

Range variable declarations allow the developer to designate a "root" for objects which may not be reachable by navigation.

In order to select values by comparing more than one instance of an entity abstract schema type, more than one identification variable ranging over the abstract schema type is needed in the FROM clause.

The following query returns magazines whose price is greater than the price of magazines published by "Adventure" publishers. This example illustrates the use of two different identification variables in the FROM clause, both of the abstract schema type Magazine. The SELECT clause of this query determines that it is the magazines with prices greater than those of "Adventure" publisher's that are returned.

SELECT DISTINCT mag1 FROM Magazine mag1, Magazine mag2
WHERE mag1.price > mag2.price AND mag2.publisher.name = 'Adventure'

2.3.4.  JPQL Path Expressions

An identification variable followed by the navigation operator (.) and a state-field or association-field is a path expression. The type of the path expression is the type computed as the result of navigation; that is, the type of the state-field or association-field to which the expression navigates.

An identification variable qualified by the KEY, VALUE, or ENTRY operator is a path expression. The KEY, VALUE, and ENTRY operators may only be applied to identification variables that correspond to map-valued associations or map-valued element collections. The type of the path expression is the type computed as the result of the operation; that is, the abstract schema type of the field that is the value of the KEY, VALUE, or ENTRY operator (the map key, map value, or map entry respectively).

Note

Note that use of VALUE is optional, as an identification variable referring to an association of type java.util.Map is of the abstract schema type of the map value.

The syntax for qualified identification variables is as follows.

  • qualified_identification_variable :: = KEY(identification_variable) | VALUE(identification_variable) | ENTRY(identification_variable)

A path expression using the KEY or VALUE operator may be further composed. A path expression using the ENTRY operator is terminal. It cannot be further composed and can only appear in the SELECT list of a query.

In the following query, photos is a map from photo label to filename.

SELECT i.name, VALUE(p)
FROM Item i JOIN i.photos p
WHERE KEY(p) LIKE ‘egret’

In the above query the identification variable p designates an abstract schema type corresponding to the map value. The results of VALUE(p) and KEY(p) are the map value and the map key associated with p, respectively. The following query is equivalent:

SELECT i.name, p
FROM Item i JOIN i.photos p
WHERE KEY(p) LIKE ‘egret’

Depending on navigability, a path expression that leads to a association-field or to a field whose type is an embeddable class may be further composed. Path expressions can be composed from other path expressions if the original path expression evaluates to a single-valued type (not a collection) corresponding to a association-field.

In the following example, contactInfo denotes an embeddable class consisting of an address and set of phones. Phone is an entity.

SELECT p.vendor
FROM Employee e JOIN e.contactInfo.phones p
WHERE e.contactInfo.address.zipcode = '95054'

Path expression navigability is composed using "inner join" semantics. That is, if the value of a non-terminal association-field in the path expression is null, the path is considered to have no value, and does not participate in the determination of the result.

The following query is equivalent to the query above:

SELECT p.vendor
FROM Employee e JOIN e.contactInfo c JOIN c.phones p
WHERE e.contactInfo.address.zipcode = '95054'

The syntax for single-valued path expressions and collection valued path expressions is as follows:

  • single_valued_path_expression ::= qualified_identification_variable | state_field_path_expression | single_valued_object_path_expression

  • state_field_path_expression ::= general_identification_variable.{single_valued_object_field.}*state_field

  • single_valued_object_path_expression ::= general_identification_variable.{single_valued_object_field.}*single_valued_object_field

  • collection_valued_path_expression ::= general_identification_variable.{single_valued_object_field.}*collection_valued_field

A single_valued_object_field is designated by the name of an association-field in a one-to-one or many-to-one relationship or a field of embeddable class type. The type of a single_valued_object_field is the abstract schema type of the related entity or embeddable class.

A state_field is designated by the name of an entity or embeddable class state field that corresponds to a basic type.

A collection_valued_field is designated by the name of an association-field in a one-to-many or a many-to-many relationship or by the name of an element collection field. The type of a collection_valued_field is a collection of values of the abstract schema type of the related entity or element type.

An identification variable used in a single_valued_object_path_expression or in a collection_valued_path_expression may be an unqualified identification variable or an identification variable to which the KEY or VALUE function has been applied.

  • general_identification_variable ::= identification_variable | KEY(identification_variable) | VALUE(identification_variable)

It is syntactically illegal to compose a path expression from a path expression that evaluates to a collection. For example, if mag designates Magazine, the path expression mag.articles.author is illegal since navigation to authors results in a collection. This case should produce an error when the query string is verified. To handle such a navigation, an identification variable must be declared in the FROM clause to range over the elements of the articles collection. Another path expression must be used to navigate over each such element in the WHERE clause of the query, as in the following query which returns all authors that have any articles in any magazines:

SELECT DISTINCT art.author FROM Magazine AS mag, IN(mag.articles) art

It is illegal to use a collection_valued_path_expression other than in the FROM clause of a query except in an empty_collection_comparison_expression, in a collection_member_expression, or as an argument to the SIZE operator. See Section 2.5.12, “ JPQL Empty Collection Comparison Expressions ”, Section 2.5.13, “ JPQL Collection Member Expressions ”, and Section 2.6.2.2, “ JPQL Arithmetic Functions ”.

2.3.5.  JPQL Joins

An inner join may be implicitly specified by the use of a cartesian product in the FROM clause and a join condition in the WHERE clause. In the absence of a join condition, this reduces to the cartesian product.

The main use case for this generalized style of join is when a join condition does not involve a foreign key relationship that is mapped to an entity relationship, for example:

SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize

In general, use of this style of inner join (also referred to as theta-join) is less typical than explicitly defined joins over relationships.

The syntax for explicit join operations is as follows:

  • join ::= join_spec join_association_path_expression [AS] identification_variable

  • fetch_join ::= join_spec FETCH join_association_path_expression

  • join_association_path_expression ::= join_collection_valued_path_expression | join_single_valued_path_expression

  • join_collection_valued_path_expression::= identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field

  • join_single_valued_path_expression::= identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field

  • join_spec ::= [ LEFT [OUTER] | INNER ] JOIN

The inner and outer join operation types described in Section 2.3.5.1, “ JPQL Inner Joins (Relationship Joins) ” and Section 2.3.5.2, “ JPQL Outer Joins ” are supported.

2.3.5.1.  JPQL Inner Joins (Relationship Joins)

The syntax for the inner join operation is

  • [ INNER ] JOIN join_association_path_expression [AS] identification_variable

For example, the query below joins over the relationship between publishers and magazines. This type of join typically equates to a join over a foreign key relationship in the database.

SELECT pub FROM Publisher pub JOIN pub.magazines mag WHERE pub.revenue > 1000000

The keyword INNER may optionally be used:

SELECT pub FROM Publisher pub INNER JOIN pub.magazines mag WHERE pub.revenue > 1000000

This is equivalent to the following query using the earlier IN construct. It selects those publishers with revenue of over 1 million for which at least one magazine exists:

SELECT OBJECT(pub) FROM Publisher pub, IN(pub.magazines) mag WHERE pub.revenue > 1000000

The query below joins over Employee, ContactInfo and Phone. ContactInfo is an embeddable class that consists of an address and set of phones. Phone is an entity.

SELECT p.vendor
FROM Employee e JOIN e.contactInfo c JOIN c.phones p
WHERE c.address.zipcode = '95054'
2.3.5.2.  JPQL Outer Joins

LEFT JOIN and LEFT OUTER JOIN are synonymous. They enable the retrieval of a set of entities where matching values in the join condition may be absent. The syntax for a left outer join is:

  • LEFT [OUTER] JOIN join_association_path_expression [AS] identification_variable

For example:

SELECT pub FROM Publisher pub LEFT JOIN pub.magazines mag WHERE pub.revenue > 1000000

The keyword OUTER may optionally be used:

SELECT pub FROM Publisher pub LEFT OUTER JOIN pub.magazines mags WHERE pub.revenue > 1000000

An important use case for LEFT JOIN is in enabling the prefetching of related data items as a side effect of a query. This is accomplished by specifying the LEFT JOIN as a FETCH JOIN.

2.3.5.3.  JPQL Fetch Joins

A FETCH JOIN enables the fetching of an association as a side effect of the execution of a query. A FETCH JOIN is specified over an entity and its related entities. The syntax for a fetch join is

  • fetch_join ::= [ LEFT [OUTER] | INNER ] JOIN FETCH join_association_path_expression

The association referenced by the right side of the FETCH JOIN clause must be an association that belongs to an entity that is returned as a result of the query. It is not permitted to specify an identification variable for the entities referenced by the right side of the FETCH JOIN clause, and hence references to the implicitly fetched entities cannot appear elsewhere in the query.

The following query returns a set of magazines. As a side effect, the associated articles for those magazines are also retrieved, even though they are not part of the explicit query result. The persistent fields or properties of the articles that are eagerly fetched are fully initialized. The initialization of the relationship properties of the articles that are retrieved is determined by the metadata for the Article entity class.

SELECT mag FROM Magazine mag LEFT JOIN FETCH mag.articles WHERE mag.id = 1

A fetch join has the same join semantics as the corresponding inner or outer join, except that the related objects specified on the right-hand side of the join operation are not returned in the query result or otherwise referenced in the query. Hence, for example, if magazine id 1 has five articles, the above query returns five references to the magazine 1 entity.

The FETCH JOIN construct must not be used in the FROM clause of a subquery.

2.3.6.  JPQL Collection Member Declarations

An identification variable declared by a collection_member_declaration ranges over values of a collection obtained by navigation using a path expression. Such a path expression represents a navigation involving the association-fields of an entity abstract schema type. Because a path expression can be based on another path expression, the navigation can use the association-fields of related entities.

An identification variable of a collection member declaration is declared using a special operator, the reserved identifier IN . The argument to the IN operator is a collection-valued path expression. The path expression evaluates to a collection type specified as a result of navigation to a collection-valued association-field of an entity or embeddable class abstract schema type.

The syntax for declaring a collection member identification variable is as follows:

  • collection_member_declaration ::= IN (collection_valued_path_expression) [AS] identification_variable

For example, the query

SELECT DISTINCT mag FROM Magazine mag
    JOIN mag.articles art
    JOIN art.author auth
    WHERE auth.lastName = 'Grisham'

can equivalently be expressed as follows, using the IN operator:

SELECT DISTINCT mag FROM Magazine mag,
    IN(mag.articles) art
    WHERE art.author.lastName = 'Grisham'

In this example, articles is the name of an association-field whose value is a collection of instances of the abstract schema type Article. The identification variable art designates a member of this collection, a single Article abstract schema type instance. In this example, mag is an identification variable of the abstract schema type Magazine.

2.3.7.  JPQL FROM Clause and SQL

The Java Persistence query language treats the FROM clause similarly to SQL in that the declared identification variables affect the results of the query even if they are not used in the WHERE clause. Application developers should use caution in defining identification variables because the domain of the query can depend on whether there are any values of the declared type.

For example, the FROM clause below defines a query over all orders that have line items and existing products. If there are no Product instances in the database, the domain of the query is empty and no order is selected.

SELECT o
FROM Order AS o JOIN o.lineItems l JOIN l.product p

2.3.8.  JPQL Polymorphism

Java Persistence queries are automatically polymorphic. The FROM clause of a query designates not only instances of the specific entity classes to which explicitly refers but of subclasses as well. The instances returned by a query include instances of the subclasses that satisfy the query criteria.

Non-polymorphic queries or queries whose polymorphism is restricted can be specified using entity type expressions in the WHERE clause to restrict the domain of the query. See Section 2.6.4, “ Entity Type Expressions ”.

2.4.  JPQL WHERE Clause

The WHERE clause of a query consists of a conditional expression used to select objects or values that satisfy the expression. The WHERE clause restricts the result of a select statement or the scope of an update or delete operation.

A WHERE clause is defined as follows:

  • where_clause ::= WHERE conditional_expression

The GROUP BY construct enables the aggregation of values according to the properties of an entity class. The HAVING construct enables conditions to be specified that further restrict the query result as restrictions upon the groups.

The syntax of the HAVING clause is as follows:

  • having_clause ::= HAVING conditional_expression

The GROUP BY and HAVING constructs are further discussed in Section 2.7, “ JPQL GROUP BY, HAVING ”.

2.5.  JPQL Conditional Expressions

The following sections describe the language constructs that can be used in a conditional expression of the WHERE clause or HAVING clause.

State-fields that are mapped in serialized form or as LOBs may not be portably used in conditional expressions.

Note

The implementation is not expected to perform such query operations involving such fields in memory rather than in the database.

2.5.1.  JPQL Literals

A string literal is enclosed in single quotes--for example: 'literal'. A string literal that includes a single quote is represented by two single quotes--for example: 'literal''s'. String literals in queries, like Java String literals, use unicode character encoding. The use of Java escape notation is not supported in query string literals.

Exact numeric literals support the use of Java integer literal syntax as well as SQL exact numeric literal syntax.

Approximate literals support the use of Java floating point literal syntax as well as SQL approximate numeric literal syntax.

Enum literals support the use of Java enum literal syntax. The enum class name must be specified.

Appropriate suffixes can be used to indicate the specific type of a numeric literal in accordance with the Java Language Specification. The boolean literals are TRUE and FALSE. Although predefined reserved literals appear in upper case, they are case insensitive.

The JDBC escape syntax may be used for the specification of date, time, and timestamp literals. For example:

SELECT o
FROM Customer c JOIN c.orders o
WHERE c.name = 'Smith'
AND o.submissionDate < {d '2008-12-31'}
            

Date, time, and timestamp literals are passed as is to the JDBC driver in use.

Entity type literals are specified by entity names—for example: Customer.

Although reserved literals appear in upper case, they are case insensitive.

2.5.2.  JPQL Identification Variables

All identification variables used in the WHERE or HAVING clause of a SELECT or DELETE statement must be declared in the FROM clause, as described in Section 2.3.2, “ JPQL Identification Variables ”. The identification variables used in the WHERE clause of an UPDATE statement must be declared in the UPDATE clause.

Identification variables are existentially quantified in the WHERE and HAVING clause. This means that an identification variable represents a member of a collection or an instance of an entity's abstract schema type. An identification variable never designates a collection in its entirety.

2.5.3.  JPQL Path Expressions

It is illegal to use a collection_valued_path_expression within a WHERE or HAVING clause as part of a conditional expression except in an empty_collection_comparison_expression, in a collection_member_expression, or as an argument to the SIZE operator.

2.5.4.  JPQL Input Parameters

Either positional or named parameters may be used. Positional and named parameters may not be mixed in a single query.

Input parameters can only be used in the WHERE clause or HAVING clause of a query.

Note that if an input parameter value is null, comparison operations or arithmetic operations involving the input parameter will return an unknown value. See Section 2.11, “ JPQL Null Values ”.

All input parameters must be single-valued, except in IN expressions (see Section 2.5.9, “ JPQL In Expressions ” ), which support the use of collection-valued input parameters.

2.5.4.1.  JPQL Positional Parameters

The following rules apply to positional parameters.

  • Input parameters are designated by the question mark (?) prefix followed by an integer. For example: ?1.

  • Input parameters are numbered starting from 1.

  • The same parameter can be used more than once in the query string.

  • The ordering of the use of parameters within the query string need not conform to the order of the positional parameters.

2.5.4.2.  JPQL Named Parameters

A named parameter is an identifier that is prefixed by the ":" symbol. It follows the rules for identifiers defined in Section 2.3.1, “ JPQL FROM Identifiers ”. Named parameters are case sensitive.

Example:

SELECT pub FROM Publisher pub WHERE pub.revenue > :rev

The same named parameter can be used more than once in the query string.

2.5.5.  JPQL Conditional Expression Composition

Conditional expressions are composed of other conditional expressions, comparison operations, logical operations, path expressions that evaluate to boolean values, boolean literals, and boolean input parameters.

The scalar expressions described in Section 2.6, “ JPQL Scalar Expressions ” can be used in conditional expressions.

Standard bracketing () for ordering expression evaluation is supported.

Aggregate functions can only be used in conditional expressions in a HAVING clause. See Section 2.7, “ JPQL GROUP BY, HAVING ”.

Conditional expressions are defined as follows:

  • conditional_expression ::= conditional_term | conditional_expression OR conditional_term

  • conditional_term ::= conditional_factor | conditional_term AND conditional_factor

  • conditional_factor ::= [ NOT ] conditional_primary

  • conditional_primary ::= simple_cond_expression | (conditional_expression)

  • simple_cond_expression ::= comparison_expression | between_expression | like_expression | in_expression | null_comparison_expression | empty_collection_comparison_expression | collection_member_expression | exists_expression

2.5.6.  JPQL Operators and Operator Precedence

The operators are listed below in order of decreasing precedence.

  • Navigation operator (.)

  • Arithmetic operators: +, - unary *, / multiplication and division +, - addition and subtraction

  • Comparison operators: =, >, >=, <, <=, <> (not equal), [ NOT ] BETWEEN, [ NOT ] LIKE, [ NOT ] IN, IS [ NOT ] NULL, IS [ NOT ] EMPTY, [ NOT ] MEMBER [ OF ]

  • Logical operators: NOT, AND, OR

The following sections describe other operators used in specific expressions.

2.5.7.  JPQL Comparison Expressions

The syntax for the use of comparison expressions in a conditional expression is as follows:

  • comparison_expression ::= string_expression comparison_operator {string_expression | all_or_any_expression} | boolean_expression { =|<> } {boolean_expression | all_or_any_expression} | enum_expression { =|<> } {enum_expression | all_or_any_expression} | datetime_expression comparison_operator {datetime_expression | all_or_any_expression} | entity_expression { = | <> } {entity_expression | all_or_any_expression} | arithmetic_expression comparison_operator {arithmetic_expression | all_or_any_expression} | entity_type_expression { = | <> } entity_type_expression}

  • comparison_operator ::= = | > | >= | < | <= | <>

Examples:

item.cost * 1.08 <= 100.00
CONCAT(person.lastName, ‘, ’, person.firstName)) = ‘Jones, Sam’
TYPE(e) = ExemptEmployee

Note

Comparisons over instances of embeddable class types are not supported.

2.5.8.  JPQL Between Expressions

The syntax for the use of the comparison operator [ NOT ] BETWEEN in a conditional expression is as follows:

arithmetic_expression [NOT] BETWEEN arithmetic_expression AND arithmetic_expression | string_expression [NOT] BETWEEN string_expression AND string_expression | datetime_expression [NOT] BETWEEN datetime_expression AND datetime_expression

The BETWEEN expression

x BETWEEN y AND z

is semantically equivalent to:

y <= x AND x <= z

The rules for unknown and NULL values in comparison operations apply. See Section 2.11, “ JPQL Null Values ” .

Examples are:

p.age BETWEEN 15 and 19

is equivalent to:

p.age >= 15 AND p.age <= 19

The following expression:

p.age NOT BETWEEN 15 and 19

excludes the range, and is equivalent to:

p.age < 15 OR p.age > 19

In the following example, transactionHistory is a list of credit card transactions defined using an order column.

SELECT t
FROM CreditCard c JOIN c.transactionHistory t
WHERE c.holder.name = ‘John Doe’ AND INDEX(t) BETWEEN 0 AND 9

2.5.9.  JPQL In Expressions

The syntax for the use of the comparison operator [ NOT ] IN in a conditional expression is as follows:

  • in_expression ::= state_field_path_expression [NOT] IN {( in_item {, in_item}* ) | (subquery) | collection_valued_input_parameter }

  • in_item ::= literal | single_valued_input_parameter

The state_field_path_expression must have a string, numeric, date, time, timestamp, or enum value.

The literal and/or input_parameter values must be like the same abstract schema type of the state_field_path_expression in type. (See Section 2.12, “ JPQL Equality and Comparison Semantics ” ).

The results of the subquery must be like the same abstract schema type of the state_field_path_expression in type. Subqueries are discussed in Section 2.5.16, “ JPQL Subqueries ”.

Examples:

o.country IN ('UK', 'US', 'France')
is true for UK and false for Peru, and is equivalent to the expression:
(o.country = 'UK') OR (o.country = 'US') OR (o.country = ' France')
In the following expression:
o.country NOT IN ('UK', 'US', 'France')
is false for UK and true for Peru, and is equivalent to the expression:
NOT ((o.country = 'UK') OR (o.country = 'US') OR (o.country = 'France'))

There must be at least one element in the comma separated list that defines the set of values for the IN expression.

If the value of a state_field_path_expression or in_item in an IN or NOT IN expression is NULL or unknown, the value of the expression is unknown.

Note that use of a collection-valued input parameter will mean that a static query cannot be precompiled.

2.5.10.  JPQL Like Expressions

The syntax for the use of the comparison operator [ NOT ] LIKE in a conditional expression is as follows:

like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]

The string_expression must have a string value. The pattern_value is a string literal or a string-valued input parameter in which an underscore (_) stands for any single character, a percent (%) character stands for any sequence of characters (including the empty sequence), and all other characters stand for themselves. The optional escape_character is a single-character string literal or a character-valued input parameter (i.e., char or Character) and is used to escape the special meaning of the underscore and percent characters in pattern_value.

Examples:

  • address.phone LIKE '12%3'
    

    is true for '123' '12993' and false for '1234'

  • asentence.word LIKE 'l_se'

    is true for 'lose' and false for 'loose'

  • aword.underscored LIKE '\_%' ESCAPE '\'

    is true for '_foo' and false for 'bar'

  • address.phone NOT LIKE '12%3'

    is false for '123' and '12993' and true for '1234'.

If the value of the string_expression or pattern_value is NULL or unknown, the value of the LIKE expression is unknown. If the escape_character is specified and is NULL, the value of the LIKE expression is unknown.

2.5.11.  JPQL Null Comparison Expressions

The syntax for the use of the comparison operator IS NULL in a conditional expression is as follows:

null_comparison_expression ::= {single_valued_path_expression | input_parameter } IS [NOT] NULL

A null comparison expression tests whether or not the single-valued path expression or input parameter is a NULL value.

Null comparisons over instances of embeddable class types are not supported.

2.5.12.  JPQL Empty Collection Comparison Expressions

The syntax for the use of the comparison operator IS EMPTY in an empty_collection_comparison_expression is as follows:

empty_collection_comparison_expression ::= collection_valued_path_expression IS [NOT] EMPTY

This expression tests whether or not the collection designated by the collection-valued path expression is empty (i.e. has no elements).

For example, the following query will return all magazines that don't have any articles at all:

SELECT mag FROM Magazine mag WHERE mag.articles IS EMPTY

If the value of the collection-valued path expression in an empty collection comparison expression is unknown, the value of the empty comparison expression is unknown.

2.5.13.  JPQL Collection Member Expressions

The syntax for the use of the comparison operator MEMBER OF in an collection_member_expression is as follows:

  • collection_member_expression ::= entity_or_value_expression [NOT] MEMBER [OF] collection_valued_path_expression

  • entity_or_value_expression ::= single_valued_object_path_expression | state_field_path_expression | simple_entity_or_value_expression

  • simple_entity_or_value_expression ::= identification_variable | input_parameter | literal

This expression tests whether the designated value is a member of the collection specified by the collection-valued path expression.

Expressions that evaluate to embeddable types are not supported in collection member expressions.

If the collection valued path expression designates an empty collection, the value of the MEMBER OF expression is FALSE and the value of the NOT MEMBER OF expression is TRUE. Otherwise, if the value of the collection_valued_path_expression or entity_or_value_expression in the collection member expression is NULL or unknown, the value of the collection member expression is unknown.

The use of the reserved word OF is optional in this expression.

Example:

SELECT p
FROM Person p
WHERE 'Joe' MEMBER OF p.nicknames
                

2.5.14.  JPQL Exists Expressions

An EXISTS expression is a predicate that is true only if the result of the subquery consists of one or more values and that is false otherwise. The syntax of an exists expression is

  • exists_expression ::= [NOT] EXISTS (subquery)

Example:

SELECT DISTINCT auth FROM Author auth
    WHERE EXISTS
        (SELECT spouseAuthor FROM Author spouseAuthor WHERE spouseAuthor = auth.spouse)

The result of this query consists of all authors whose spouse is also an author.

2.5.15.  JPQL All or Any Expressions

An ALL conditional expression is a predicate over a subquery that is true if the comparison operation is true for all values in the result of the subquery or the result of the subquery is empty. An ALL conditional expression is false if the result of the comparison is false for at least one value of the result of the subquery, and is unknown if neither true nor false.

An ANY conditional expression is a predicate over a subquery that is true if the comparison operation is true for some value in the result of the subquery. An ANY conditional expression is false if the result of the subquery is empty or if the comparison operation is false for every value in the result of the subquery, and is unknown if neither true nor false. The keyword SOME is synonymous with ANY.

The comparison operators used with ALL or ANY conditional expressions are =, <, <=, >, >=, <>. The result of the subquery must be like that of the other argument to the comparison operator in type. See Section 2.12, “ JPQL Equality and Comparison Semantics ”. The syntax of an ALL or ANY expression is specified as follows:

  • all_or_any_expression ::= { ALL | ANY | SOME} (subquery)

The following example select the authors who make the highest salary for their magazine:

SELECT auth FROM Author auth
    WHERE auth.salary >= ALL(SELECT a.salary FROM Author a WHERE a.magazine = auth.magazine)

2.5.16.  JPQL Subqueries

Subqueries may be used in the WHERE or HAVING clause. The syntax for subqueries is as follows:

  • subquery ::= simple_select_clause subquery_from_clause [where_clause] [groupby_clause] [having_clause]

  • simple_select_clause ::= SELECT [DISTINCT] simple_select_expression

  • subquery_from_clause ::= FROM subselect_identification_variable_declaration {, subselect_identification_variable_declaration | collection_member_declaration }*

  • subselect_identification_variable_declaration ::= identification_variable_declaration | derived_path_expression [AS] identification_variable {join}* | derived_collection_member_declaration

  • simple_select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable

  • derived_path_expression ::= superquery_identification_variable.{single_valued_object_field.}*collection_valued_field | superquery_identification_variable.{single_valued_object_field.}*single_valued_object_field

  • derived_collection_member_declaration ::= IN superquery_identification_variable.{single_valued_object_field.}*collection_valued_field

Subqueries are restricted to the WHERE and HAVING clauses in this release. Support for subqueries in the FROM clause will be considered in a later release of the specification.

Examples:

SELECT DISTINCT auth FROM Author auth
    WHERE EXISTS (SELECT spouseAuth FROM Author spouseAuth WHERE spouseAuth = auth.spouse)
SELECT mag FROM Magazine mag
    WHERE (SELECT COUNT(art) FROM mag.articles art) > 10

Note that some contexts in which a subquery can be used require that the subquery be a scalar subquery (i.e., produce a single result). This is illustrated in the following example involving a numeric comparison operation.

SELECT goodPublisher FROM Publisher goodPublisher
    WHERE goodPublisher.revenue < (SELECT AVG(p.revenue) FROM Publisher p)

SELECT goodCustomer
FROM Customer goodCustomer
WHERE goodCustomer.balanceOwed < (
SELECT AVG(c.balanceOwed)/2.0 FROM Customer c)

2.6.  JPQL Scalar Expressions

Numeric, string, datetime, case, and entity type expressions result in scalar values.

Scalar expressions may be used in the SELECT clause of a query as well as in the WHERE and HAVING clauses.

scalar_expression::= arithmetic_expression | string_primary | enum_primary | datetime_primary | boolean_primary | case_expression | entity_type_expression

2.6.1.  Arithmetic Expressions

The arithmetic operators are:

  • +, - unary
  • *, / multiplication and division
  • +, - addition and subtraction

Arithmetic operations use numeric promotion.

Arithmetic functions are described in Section 2.6.2.2, “ JPQL Arithmetic Functions ”.

2.6.2.  String, Arithmetic, and Datetime Functional Expressions

JPQL includes the built-in functions described in subsections Section 2.6.2.1, “ JPQL String Functions ”, Section 2.6.2.2, “ JPQL Arithmetic Functions ”, Section 2.6.2.3, “ JPQL Datetime Functions ”, which may be used in the SELECT, WHERE or HAVING clause of a query.

If the value of any argument to a functional expression is null or unknown, the value of the functional expression is unknown.

2.6.2.1.  JPQL String Functions

  • functions_returning_strings ::= CONCAT(string_primary, string_primary) | SUBSTRING(string_primary, simple_arithmetic_expression[, simple_arithmetic_expression]) | TRIM([[trim_specification] [trim_character] FROM] string_primary) | LOWER(string_primary) | UPPER(string_primary)

  • trim_specification ::= LEADING | TRAILING | BOTH

  • functions_returning_numerics ::= LENGTH(string_primary) | LOCATE(string_primary, string_primary[, simple_arithmetic_expression])

The CONCAT function returns a string that is a concatenation of its arguments.

The second and third arguments of the SUBSTRING function denote the starting position and length of the substring to be returned. These arguments are integers. The third argument is optional. If it is not specified, the substring from the start position to the end of the string is returned. The first position of a string is denoted by 1. The SUBSTRING function returns a string.

The TRIM function trims the specified character from a string. If the character to be trimmed is not specified, it is assumed to be space (or blank). The optional trim_character is a single-character string literal or a character-valued input parameter (i.e., char or Character). If a trim specification is not provided, BOTH is assumed. The TRIM function returns the trimmed string.

The LOWER and UPPER functions convert a string to lower and upper case, respectively. They return a string.

The LOCATE function returns the position of a given string within a string, starting the search at a specified position. It returns the first position at which the string was found as an integer. The first argument is the string to be located; the second argument is the string to be searched; the optional third argument is an integer that represents the string position at which the search is started (by default, the beginning of the string to be searched). The first position in a string is denoted by 1. If the string is not found, 0 is returned.

The LENGTH function returns the length of the string in characters as an integer.

2.6.2.2.  JPQL Arithmetic Functions

  • functions_returning_numerics ::= ABS(simple_arithmetic_expression) | SQRT(simple_arithmetic_expression) | MOD(simple_arithmetic_expression, simple_arithmetic_expression) | SIZE(collection_valued_path_expression) | INDEX(identification_variable)

The ABS function takes a numeric argument and returns a number (integer, float, or double) of the same type as the argument to the function.

The SQRT function takes a numeric argument and returns a double.

Note that not all databases support the use of a trim character other than the space character; use of this argument may result in queries that are not portable. Note that not all databases support the use of the third argument to LOCATE; use of this argument may result in queries that are not portable.

The MOD function takes two integer arguments and returns an integer.

The SIZE function returns an integer value, the number of elements of the collection. If the collection is empty, the SIZE function evaluates to zero. Numeric arguments to these functions may correspond to the numeric Java object types as well as the primitive numeric types.

The INDEX function returns an integer value corresponding to the position of its argument in an ordered list. The INDEX function can only be applied to identification variables denoting types for which an order column has been specified.

2.6.2.3.  JPQL Datetime Functions

functions_returning_datetime:= CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP

The datetime functions return the value of current date, time, and timestamp on the database server.

2.6.3.  Case Expressions

The following forms of case expressions are supported: general case expressions, simple case expressions, coalesce expressions, and nullif expressions.

CASEELSEEND
  • case_expression::= general_case_expression | simple_case_expression | coalesce_expression | nullif_expression
  • general_case_expression::= CASE when_clause {when_clause}* ELSE scalar_expression END
  • when_clause::= WHEN conditional_expression THEN scalar_expression
  • case_operand::= state_field_path_expression | type_discriminator
  • simple_when_clause::= WHEN scalar_expression THEN scalar_expression
  • coalesce_expression::= COALESCE(scalar_expression {, scalar_expression}+)
  • nullif_expression::= NULLIF(scalar_expression, scalar_expression)

Examples:

UPDATE Employee e
SET e.salary =
    CASE WHEN e.rating = 1 THEN e.salary * 1.1
         WHEN e.rating = 2 THEN e.salary * 1.05
         ELSE e.salary * 1.01
    END
UPDATE Employee e
SET e.salary =
    CASE e.rating WHEN 1 THEN e.salary * 1.1
                  WHEN 2 THEN e.salary * 1.05
                  ELSE e.salary * 1.01
    END
SELECT e.name,
    CASE TYPE(e) WHEN Exempt THEN 'Exempt'
                 WHEN Contractor THEN 'Contractor'
                 WHEN Intern THEN 'Intern'
                 ELSE 'NonExempt'
    END
FROM Employee e
WHERE e.dept.name = 'Engineering'
SELECT e.name,
       f.name,
       CONCAT(CASE WHEN f.annualMiles > 50000 THEN 'Platinum '
                   WHEN f.annualMiles > 25000 THEN 'Gold '
                   ELSE ''
                   END,
              'Frequent Flyer')
FROM Employee e JOIN e.frequentFlierPlan f

2.6.4.  Entity Type Expressions

An entity type expression can be used to restrict query polymorphism. The TYPE operator returns the exact type of the argument.

The syntax of an entity type expression is as follows:

  • entity_type_expression ::= type_discriminator | entity_type_literal | input_parameter
  • type_discriminator ::= TYPE(identification_variable | single_valued_object_path_expression | input_parameter )

An entity_type_literal is designated by the entity name.

The Java class of the entity is used as an input parameter to specify the entity type.

Examples:

SELECT e
FROM Employee e
WHERE TYPE(e) IN (Exempt, Contractor)
SELECT e
FROM Employee e
WHERE TYPE(e) IN (:empType1, :empType2)
SELECT e
FROM Employee e
WHERE TYPE(e) IN :empTypes
SELECT TYPE(e)
FROM Employee e
WHERE TYPE(e) <> Exempt

2.7.  JPQL GROUP BY, HAVING

The GROUP BY construct enables the aggregation of values according to a set of properties. The HAVING construct enables conditions to be specified that further restrict the query result. Such conditions are restrictions upon the groups.

The syntax of the GROUP BY and HAVING clauses is as follows:

  • groupby_clause ::= GROUP BY groupby_item {, groupby_item}*

  • groupby_item ::= single_valued_path_expression | identification_variable

  • having_clause ::= HAVING conditional_expression

If a query contains both a WHERE clause and a GROUP BY clause, the effect is that of first applying the where clause, and then forming the groups and filtering them according to the HAVING clause. The HAVING clause causes those groups to be retained that satisfy the condition of the HAVING clause.

The requirements for the SELECT clause when GROUP BY is used follow those of SQL: namely, any item that appears in the SELECT clause (other than as an argument to an aggregate function) must also appear in the GROUP BY clause. In forming the groups, null values are treated as the same for grouping purposes.

Grouping by an entity is permitted. In this case, the entity must contain no serialized state fields or LOB-valued state fields that are eagerly fetched.

Grouping by embeddables is not supported.

The HAVING clause must specify search conditions over the grouping items or aggregate functions that apply to grouping items.

If there is no GROUP BY clause and the HAVING clause is used, the result is treated as a single group, and the select list can only consist of aggregate functions. The use of HAVING in the absence of GROUP BY is not required to be supported by a JPA implementation. Portable applications should not rely on HAVING without the use of GROUP BY.

Note

OpenJPA supports the use of HAVING in the absence of GROUP BY if the underlying database supports it.

Examples:

SELECT c.status, AVG(c.filledOrderCount), COUNT(c)
FROM Customer c
GROUP BY c.status
HAVING c.status IN (1, 2)
SELECT c.country, COUNT(c)
FROM Customer c
GROUP BY c.country
HAVING COUNT(c) > 30

2.8.  JPQL SELECT Clause

The SELECT clause denotes the query result. More than one value may be returned from the SELECT clause of a query.

The SELECT clause can contain one or more of the following elements: a single range variable or identification variable that ranges over an entity abstract schema type, a single-valued path expression, a scalar expression, an aggregate expression, a constructor expression.

The SELECT clause has the following syntax:

  • select_clause ::= SELECT [DISTINCT] select_item {, select_item}*

  • select_item ::= select_expression [ [AS] result_variable]

  • select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable | OBJECT(identification_variable) | constructor_expression

  • constructor_expression ::= NEW constructor_name ( constructor_item {, constructor_item}*)

  • constructor_item ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable

  • aggregate_expression ::= { AVG | MAX | MIN | SUM } ([DISTINCT] state_field_path_expression) | COUNT ([DISTINCT] identification_variable | state_field_path_expression | single_valued_object_path_expression)

For example:

SELECT pub.id, pub.revenue
    FROM Publisher pub JOIN pub.magazines mag WHERE mag.price > 5.00

In the following example, videoInventory is a Map from the entity Movie to the number of copies in stock:

SELECT v.location.street, KEY(i).title, VALUE(i)
FROM VideoStore v JOIN v.videoInventory i
WHERE v.location.zipcode = '94301' AND VALUE(i) > 0

Note that the SELECT clause must be specified to return only single-valued expressions. The query below is therefore not valid:

SELECT mag.authors FROM Magazine AS mag

The DISTINCT keyword is used to specify that duplicate values must be eliminated from the query result.

If DISTINCT is not specified, duplicate values are not eliminated.

The result of DISTINCT over embeddable objects or map entry results is undefined.

Standalone identification variables in the SELECT clause may optionally be qualified by the OBJECT operator. The SELECT clause must not use the OBJECT operator to qualify path expressions.

A result_variable may be used to name a select_item in the query result. For example,

SELECT c, COUNT(l) AS itemCount
FROM Customer c JOIN c.Orders o JOIN o.lineItems l
WHERE c.address.state = ‘CA’
ORDER BY itemCount
 

2.8.1.  JPQL Result Type of the SELECT Clause

The type of the query result specified by the SELECT clause of a query is an entity abstract schema type, a state-field type, the result of of a scalar expression, the result of an aggregate function, the result of a construction operation, or some sequence of these.

The result type of the SELECT clause is defined by the result types of the select_expressions contained in it. When multiple select expressions are used in the SELECT clause, the result of the query is of type Object[], and the elements in this result correspond in order to the order of their specification in the SELECT clause and in type to the result types of each of the select expressions.

The type of the result of a select_expression is as follows:

  • The result type of an identification_variable is the type of the entity object or embeddable object to which the identification variable corresponds. The type of an identification_variable that refers to an entity abstract schema type is the type of the entity to which that identification variable corresponds or a subtype as determined by the object/relational mapping.

  • The result type of a single_valued_path_expression that is a state_field_path_expression results in an object of the same type as the corresponding state field of the entity or embeddable class. If the state field of the entity is a primitive type, the result type is the corresponding object type.

  • The result type of a single_valued_path_expression that is a single_valued_object_path_expression is the type of the entity object or embeddable object to which the path expression corresponds. A single_valued_object_path_expression that results in an entity object will result in an entity of the type of the relationship field or the subtype of the relationship field of the entity object as determined by the object/relational mapping.

  • The result type of a single_valued_path_expression that is an identification_variable to which the KEY or VALUE function has been applied is determined by the type of the map key or value respectively, as defined by the above rules

  • The result type of a single_valued_path_expression that is an identification_variable to which the ENTRY function has been applied is java.util.Map.Entry, where the key and value types of the map entry are determined by the above rules as applied to the map key and map value respectively.

  • The result type of a scalar_expression is the type of the scalar value to which the expression evaluates. The result type of a numeric scalar_expression is defined in Section 2.6, “ JPQL Scalar Expressions ”

  • The result type of an entity_type_expression scalar expression is the Java class to which the resulting abstract schema type corresponds.

  • The result type of aggregate_expression is defined in Section 2.8.5, “ JPQL Aggregate Functions ”.

  • The result type of a constructor_expression is the type of the class for which the constructor is defined. The types of the arguments to the constructor are defined by the above rules.

2.8.2.  JPQL Constructor Expressions

A constructor may be used in the SELECT list to return one or more Java instances. The specified class is not required to be an entity or to be mapped to the database. The constructor name must be fully qualified.

If an entity class name is specified in the SELECT NEW clause, the resulting entity instances are in the new state.

If a single_valued_path_expression or identification_variable that is an argument to the constructor references an entity, the resulting entity instance referenced by that single_valued_path_expression or identification_variable will be in the managed state.

If PublisherInfo is an entity class, the following 2 queries return instances of PublisherInfo that will be in the new state. In the second example, mag is an identification_variable passed as an argument to the constructor PublisherInfo(Magazine mag); the entity instances of Magazine created during query evaluation will be in the managed state. Example:

SELECT NEW com.company.PublisherInfo(pub.id, pub.revenue, mag.price)
    FROM Publisher pub JOIN pub.magazines mag WHERE mag.price > 5.00
SELECT NEW com.company.PublisherInfo(mag)
    FROM Publisher pub JOIN pub.magazines mag WHERE mag.price > 5.00

2.8.3.  JPQL Null Values in the Query Result

If the result of a query corresponds to a association-field or state-field whose value is null, that null value is returned in the result of the query method. The IS NOT NULL construct can be used to eliminate such null values from the result set of the query.

Note, however, that state-field types defined in terms of Java numeric primitive types cannot produce NULL values in the query result. A query that returns such a state-field type as a result type must not return a null value.

2.8.4.  JPQL Embeddables in the Query Result

If the result of a query corresponds to an identification variable or state field whose value is an embeddable, the embeddable instance returned by the query will not be in the managed state (i.e., it will not be part of the state of any managed entity).

In the following example, the Address instances returned by the query will reference Phone instances. While the Phone instances will be managed, the Address> instances referenced by the addr result variable will not be. Modifications to these embeddable instances are not allowed.

@Entity
public class Employee {
  @Id int id;
  Address address;
  ...
}

@Embeddable
public class Address {
  String street;
  ...
  @OneToOne Phone phone; // fetch=EAGER
}

@Entity
public class Phone {
  @Id int id;
  ...
  @OneToOne(mappedBy="address.phone") Employee emp; // fetch=EAGER
}

SELECT e.address AS addr
FROM Employee e

2.8.5.  JPQL Aggregate Functions

The result of a query may be the result of an aggregate function applied to a path expression.

The following aggregate functions can be used in the SELECT clause of a query: AVG, COUNT, MAX, MIN, SUM.

For all aggregate functions except COUNT, the path expression that is the argument to the aggregate function must terminate in a state-field. The path expression argument to COUNT may terminate in either a state-field or a association-field, or the argument to COUNT may be an identification variable.

Arguments to the functions SUM and AVG must be numeric. Arguments to the functions MAX and MIN must correspond to orderable state-field types (i.e., numeric types, string types, character types, or date types).

The Java type that is contained in the result of a query using an aggregate function is as follows:

  • COUNT returns Long.

  • MAX, MIN return the type of the state-field to which they are applied.

  • AVG returns Double.

  • SUM returns Long when applied to state-fields of integral types (other than BigInteger); Double when applied to state-fields of floating point types; BigInteger when applied to state-fields of type BigInteger; and BigDecimal when applied to state-fields of type BigDecimal.

If SUM , AVG, MAX, or MIN is used, and there are no values to which the aggregate function can be applied, the result of the aggregate function is NULL.

If COUNT is used, and there are no values to which COUNT can be applied, the result of the aggregate function is 0.

The argument to an aggregate function may be preceded by the keyword DISTINCT to specify that duplicate values are to be eliminated before the aggregate function is applied. It is legal to specify DISTINCT with MAX or MIN, but it does not affect the result.

Null values are eliminated before the aggregate function is applied, regardless of whether the keyword DISTINCT is specified.

The use of DISTINCT with COUNT is not supported for arguments of embeddable types or map entry types.

2.8.5.1.  JPQL Aggregate Examples

The following query returns the average price of all magazines:

SELECT AVG(mag.price) FROM Magazine mag

The following query returns the sum of all the prices from all the magazines published by 'Larry':

SELECT SUM(mag.price) FROM Publisher pub JOIN pub.magazines mag WHERE pub.firstName = 'Larry'

The following query returns the total number of magazines:

SELECT COUNT(mag) FROM Magazine mag

2.8.5.2.  JPQL Numeric Expressions in the SELECT Clause

The type of a numeric expression in the query result is determined as follows:

An operand that corresponds to a persistent state-field is of the same type as that persistent state-field.

An operand that corresponds to one of arithmetic functions described in Section 2.6.2.2, “ JPQL Arithmetic Functions ” is of the type defined by Section 2.6.2.2, “ JPQL Arithmetic Functions ”.

An operand that corresponds to one of an aggregate functions described in Section 2.8.5, “ JPQL Aggregate Functions ” is of the type defined by Section 2.8.5, “ JPQL Aggregate Functions ”.

The result of a case expression, coalesce expression, nullif expression, or arithmetic expression (+, -, *, /) is determined by applying the following rule to its operands.

  • If there is an operand of type Double or double, the result of the operation is of type Double;

  • otherwise, if there is an operand of type Float or float, the result of the operation is of type Float;

  • otherwise, if there is an operand of type BigDecimal, the result of the operation is of type Big- Decimal;

  • otherwise, if there is an operand of type BigInteger, the result of the operation is of type BigInteger;

  • otherwise, if there is an operand of type Long or long, the result of the operation is of type Long;

  • otherwise, if there is an operand of integral type, the result of the operation is of type Integer.

2.9.  JPQL ORDER BY Clause

The ORDER BY clause allows the objects or values that are returned by the query to be ordered. The syntax of the ORDER BY clause is

  • orderby_clause ::= ORDER BY orderby_item {, orderby_item}*

  • orderby_item ::= { state_field_path_expression | result_variable } [ASC | DESC]

An orderby_item must be one of the following:

  • A state_field_path_expression that evaluates to an orderable state field of an entity or embeddable class abstract schema type designated in the SELECT clause by one of the following:

    • a general_identification_variable

    • a single_valued_object_path_expression

  • A state_field_path_expression that evaluates to the same state field of the same entity or embeddable abstract schema type as a state_field_path_expression in the SELECT clause.

  • A result_variable that refers to an orderable item in the SELECT clause for which the same result_variable has been specified. This may be the result of an aggregate_expression, a scalar_expression, or a state_field_path_expression in the SELECT clause.

For example, the five queries below are legal.

SELECT pub FROM Publisher pub ORDER BY pub.revenue, pub.name
SELECT o
FROM Customer c JOIN c.orders o JOIN c.address a
WHERE a.state = ‘CA’
ORDER BY o.quantity DESC, o.totalcost
SELECT o.quantity, a.zipcode
FROM Customer c JOIN c.orders o JOIN c.address a
WHERE a.state = ‘CA’
ORDER BY o.quantity, a.zipcode
SELECT o.quantity, o.cost*1.08 AS taxedCost, a.zipcode
FROM Customer c JOIN c.orders o JOIN c.address a
WHERE a.state = ‘CA’ AND a.county = ‘Santa Clara’
ORDER BY o.quantity, taxedCost, a.zipcode
SELECT AVG(o.quantity) as q, a.zipcode
FROM Customer c JOIN c.orders o JOIN c.address a
WHERE a.state = ‘CA’
GROUP BY a.zipcode
ORDER BY q DESC

The following two queries are not legal because the orderby_item is not reflected in the SELECT clause of the query.

SELECT p.product_name
FROM Order o JOIN o.lineItems l JOIN l.product p JOIN o.customer c
WHERE c.lastname = ‘Smith’ AND c.firstname = ‘John’
ORDER BY p.price
SELECT p.product_name
FROM Order o, IN(o.lineItems) l JOIN o.customer c
WHERE c.lastname = ‘Smith’ AND c.firstname = ‘John’
ORDER BY o.quantity

If more than one orderby_item is specified, the left-to-right sequence of the orderby_item elements determines the precedence, whereby the leftmost orderby_item has highest precedence.

The keyword ASC specifies that ascending ordering be used; the keyword DESC specifies that descending ordering be used. Ascending ordering is the default.

SQL rules for the ordering of null values apply: that is, all null values must appear before all non-null values in the ordering or all null values must appear after all non-null values in the ordering, but it is not specified which.

The ordering of the query result is preserved in the result of the query method if the ORDER BY clause is used.

2.10.  JPQL Bulk Update and Delete

Bulk update and delete operations apply to entities of a single entity class (together with its subclasses, if any). Only one entity abstract schema type may be specified in the FROM or UPDATE clause. The syntax of these operations is as follows:

  • update_statement ::= update_clause [where_clause]

  • update_clause ::= UPDATE entity_name [[AS] identification_variable] SET update_item {, update_item}*

  • update_item ::= [identification_variable.]{state_field | single_valued_object_field} = new_value

  • new_value ::= scalar_expression | simple_entity_expression | NULL

  • delete_statement ::= delete_clause [where_clause]

  • delete_clause ::= DELETE FROM entity_name [[AS] identification_variable]

The syntax of the WHERE clause is described in Section 2.4, “ JPQL WHERE Clause ”.

A delete operation only applies to entities of the specified class and its subclasses. It does not cascade to related entities.

The new_value specified for an update operation must be compatible in type with the state-field to which it is assigned.

Bulk update maps directly to a database update operation, bypassing optimistic locking checks. Portable applications must manually update the value of the version column, if desired, and/or manually validate the value of the version column.

The persistence context is not synchronized with the result of the bulk update or delete.

Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a transaction in a new persistence context or at the beginning of a transaction (before entities have been accessed whose state might be affected by such operations).

Examples:

DELETE FROM Publisher pub WHERE pub.revenue > 1000000.0
DELETE FROM Publisher pub WHERE pub.revenue = 0 AND pub.magazines IS EMPTY
UPDATE Publisher pub SET pub.status = 'outstanding'
    WHERE pub.revenue < 1000000 AND 20 > (SELECT COUNT(mag) FROM pub.magazines mag)

2.11.  JPQL Null Values

When the target of a reference does not exist in the database, its value is regarded as NULL. SQL 92 NULL semantics defines the evaluation of conditional expressions containing NULL values. The following is a brief description of these semantics:

  • Comparison or arithmetic operations with a NULL value always yield an unknown value.

  • Two NULL values are not considered to be equal, the comparison yields an unknown value.

  • Comparison or arithmetic operations with an unknown value always yield an unknown value.

  • The IS NULL and IS NOT NULL operators convert a NULL state-field or single-valued association-field value into the respective TRUE or FALSE value.

Note: The JPQL defines the empty string, "", as a string with 0 length, which is not equal to a NULL value. However, NULL values and empty strings may not always be distinguished when queries are mapped to some databases. Application developers should therefore not rely on the semantics of query comparisons involving the empty string and NULL value.

2.12.  JPQL Equality and Comparison Semantics

Only the values of like types are permitted to be compared. A type is like another type if they correspond to the same Java language type, or if one is a primitive Java language type and the other is the wrappered Java class type equivalent (e.g., int and Integer are like types in this sense). There is one exception to this rule: it is valid to compare numeric values for which the rules of numeric promotion apply. Conditional expressions attempting to compare non-like type values are disallowed except for this numeric case.

Note that the arithmetic operators and comparison operators are permitted to be applied to state-fields and input parameters of the wrappered Java class equivalents to the primitive numeric Java types.

Two entities of the same abstract schema type are equal if and only if they have the same primary key value.

Equality/inequality comparisons over enums are supported.

Comparisons over instances of embeddable class or map entry types are not supported.

2.13.  JPQL BNF

The following is the BNF for the Java Persistence query language, from section 4.14 of the JSR 317 specification.

select_item ::= select_expression [[AS] result_variable]

  • QL_statement ::= select_statement | update_statement | delete_statement

  • select_statement ::= select_clause from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

  • update_statement ::= update_clause [where_clause]

  • delete_statement ::= delete_clause [where_clause]

  • from_clause ::= FROM identification_variable_declaration {, {identification_variable_declaration | collection_member_declaration}}*

  • identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*

  • range_variable_declaration ::= entity_name [ AS ] identification_variable

  • join ::= join_spec join_association_path_expression [ AS ] identification_variable

  • fetch_join ::= join_spec FETCH join_association_path_expression

  • join_spec ::= [ LEFT [ OUTER ]| INNER ] JOIN

  • join_association_path_expression ::= join_collection_valued_path_expression | join_single_valued_path_expression

  • join_collection_valued_path_expression ::= identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field

  • join_single_valued_path_expression ::= identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field

  • collection_member_declaration ::= IN (join_collection_valued_path_expression) [ AS ] identification_variable

  • qualified_identification_variable ::= KEY(identification_variable) | VALUE(identification_variable) | ENTRY(identification_variable)

  • single_valued_path_expression ::= qualified_identification_variable | state_field_path_expression | single_valued_object_path_expression

  • general_identification_variable ::= identification_variable | KEY(identification_variable) | VALUE(identification_variable)

  • state_field_path_expression ::= general_identification_variable.{single_valued_object_field.}*state_field

  • single_valued_object_path_expression ::= general_identification_variable.{single_valued_object_field.}* single_valued_object_field

  • collection_valued_path_expression ::= general_identification_variable.{single_valued_object_field.}*collection_valued_field

  • update_clause ::= UPDATE entity_name [[ AS ] identification_variable] SET update_item {, update_item}*

  • update_item ::= [identification_variable.]{state_field | single_valued_object_field}= new_value

  • new_value ::= scalar_expression | simple_entity_expression | NULL

  • delete_clause ::= DELETEFROM entity_name [[ AS ] identification_variable]

  • select_clause ::= SELECT [ DISTINCT ] select_item {, select_item}*

  • select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable | OBJECT (identification_variable)| constructor_expression

  • constructor_expression ::= NEW constructor_name( constructor_item {, constructor_item}*)

  • constructor_item ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable

  • aggregate_expression ::= { AVG | MAX | MIN | SUM }([ DISTINCT ] state_field_path_expression) | COUNT ([ DISTINCT ] identification_variable | state_field_path_expression | single_valued_object_path_expression)

  • where_clause ::= WHERE conditional_expression

  • groupby_clause ::= GROUPBY groupby_item {, groupby_item}*

  • groupby_item ::= single_valued_path_expression | identification_variable

  • having_clause ::= HAVING conditional_expression

  • orderby_clause ::= ORDERBY orderby_item {, orderby_item}*

  • orderby_item ::= state_field_path_expression | result_variable [ ASC | DESC ]

  • subquery ::= simple_select_clause subquery_from_clause [where_clause] [groupby_clause] [having_clause]

  • subquery_from_clause ::= FROM subselect_identification_variable_declaration {, subselect_identification_variable_declaration | collection_member_declaration}*

  • subselect_identification_variable_declaration ::= identification_variable_declaration | derived_path_expression [ AS ] identification_variable | derived_collection_member_declaration

  • derived_path_expression ::= superquery_identification_variable.{single_valued_object_field.}*collection_valued_field | superquery_identification_variable.{single_valued_object_field.}*single_valued_object_field

  • derived_collection_member_declaration ::= IN superquery_identification_variable.{single_valued_object_field.}*collection_valued_field

  • simple_select_clause ::= SELECT [ DISTINCT ] simple_select_expression

  • simple_select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable

  • scalar_expression ::= simple_arithmetic_expression | string_primary | enum_primary | datetime_primary | boolean_primary | case_expression | entity_type_expression

  • conditional_expression ::= conditional_term | conditional_expression OR conditional_term

  • conditional_term ::= conditional_factor | conditional_term AND conditional_factor

  • conditional_factor ::= [ NOT ] conditional_primary

  • conditional_primary ::= simple_cond_expression |(conditional_expression)

  • simple_cond_expression ::= comparison_expression | between_expression | like_expression | in_expression | null_comparison_expression | empty_collection_comparison_expression | collection_member_expression | exists_expression

  • between_expression ::= arithmetic_expression [ NOT ] BETWEEN arithmetic_expression AND arithmetic_expression | string_expression [ NOT ] BETWEEN string_expression AND string_expression | datetime_expression [ NOT ] BETWEEN datetime_expression AND datetime_expression

  • in_expression ::= {state_field_path_expression | type_discriminator} [ NOT ] IN {( in_item {, in_item}*) | (subquery) | collection_valued_input_parameter }

  • in_item ::= literal | single_valued_input_parameter

  • like_expression ::= string_expression [ NOT ] LIKE pattern_value [ ESCAPE escape_character]

  • null_comparison_expression ::= {single_valued_path_expression | input_parameter} IS [ NOT ] NULL

  • empty_collection_comparison_expression ::= collection_valued_path_expression IS [ NOT ] EMPTY

  • collection_member_expression ::= entity_expression [ NOT ] MEMBER [ OF ] collection_valued_path_expression

  • entity_or_value_expression ::= single_valued_object_path_expression | state_field_path_expression | simple_entity_or_value_expression

  • simple_entity_or_value_expression ::= identification_variable | input_parameter | literal

  • exists_expression ::= [ NOT ] EXISTS (subquery)

  • all_or_any_expression ::= { ALL | ANY | SOME }(subquery)

  • comparison_expression ::= string_expressioncomparison_operator{string_expression|all_or_any_expression}| boolean_expression {=|<>} {boolean_expression | all_or_any_expression} | enum_expression {=|<>} {enum_expression | all_or_any_expression} | datetime_expression comparison_operator {datetime_expression | all_or_any_expression} | entity_expression {= |<> } {entity_expression | all_or_any_expression} | arithmetic_expression comparison_operator {arithmetic_expression | all_or_any_expression} | entity_type_expression { =|<>>} entity_type_expression}

  • comparison_operator ::== |> |>= |< |<= |<>

  • arithmetic_expression ::= simple_arithmetic_expression |(subquery)

  • simple_arithmetic_expression ::= arithmetic_term | simple_arithmetic_expression {+ |- } arithmetic_term

  • arithmetic_term ::= arithmetic_factor | arithmetic_term {* |/ } arithmetic_factor

  • arithmetic_factor ::= [{+ |-}] arithmetic_primary

  • arithmetic_primary ::= state_field_path_expression | numeric_literal | (simple_arithmetic_expression) | input_parameter | functions_returning_numerics | aggregate_expression | case_expression

  • string_expression ::= string_primary |(subquery)

  • string_primary ::= state_field_path_expression | string_literal | input_parameter | functions_returning_strings | aggregate_expression | case_expression

  • datetime_expression ::= datetime_primary |(subquery)

  • datetime_primary ::= state_field_path_expression | input_parameter | functions_returning_datetime | aggregate_expression | case_expression | date_time_timestamp_literal

  • boolean_expression ::= boolean_primary |(subquery)

  • boolean_primary ::= state_field_path_expression | boolean_literal | input_parameter | case_expression

  • enum_expression ::= enum_primary |(subquery)

  • enum_primary ::= state_field_path_expression | enum_literal | input_parameter | case_expression

  • entity_expression ::= single_valued_object_path_expression | simple_entity_expression

  • simple_entity_expression ::= identification_variable | input_parameter

  • entity_type_expression ::= type_discriminator | entity_type_literal | input_parameter

  • type_discriminator ::= TYPE(identification_variable | single_valued_object_path_expression | input_parameter)

  • functions_returning_numerics ::= LENGTH (string_primary)| LOCATE (string_primary,string_primary [, simple_arithmetic_expression]) | ABS (simple_arithmetic_expression) | SQRT (simple_arithmetic_expression) | MOD (simple_arithmetic_expression, simple_arithmetic_expression) | SIZE (collection_valued_path_expression) | INDEX(identification_variable)

  • functions_returning_datetime ::= CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP

  • functions_returning_strings ::= CONCAT (string_primary, string_primary) | SUBSTRING (string_primary, simple_arithmetic_expression[,simple_arithmetic_expression])| TRIM ([[trim_specification] [trim_character] FROM ] string_primary) | LOWER (string_primary) | UPPER (string_primary)

  • trim_specification ::= LEADING | TRAILING | BOTH

  • case_expression ::= general_case_expression | simple_case_expression | coalesce_expression | nullif_expression

  • general_case_expression::= CASE when_clause {when_clause}* ELSE scalar_expression END

  • when_clause::= WHEN conditional_expression THEN scalar_expression

  • simple_case_expression::= CASE case_operand simple_when_clause {simple_when_clause}* ELSE scalar_expression END

  • case_operand::= state_field_path_expression | type_discriminator

  • simple_when_clause::= WHEN scalar_expression THEN scalar_expression

  • coalesce_expression::= COALESCE(scalar_expression {, scalar_expression}+)

  • nullif_expression::= NULLIF(scalar_expression, scalar_expression)

Chapter 11.  JPA Criteria

JPA 2.0 specification introduces a new API to define queries dynamically via construction of an object-based javax.persistence.CriteriaQuery instance, rather than string-based approach used in JPQL (Java Persistence Query Language). This dynamic query definition capability, referred as Criteria API, is based on the abstract persistent schema of the entities, their embedded objects and their relationships. The syntax is designed to construct a Query Tree whose nodes represent the semantic query elements such as projections, conditional predicates of WHERE clause or GROUP BY elements etc.

1. Constructing a CriteriaQuery

The CriteriaBuilder interface is the factory for CriteriaQuery. A CriteriaBuilder is obtained from either an EntityManagerFactory or an EntityManager as follows:

EntityManager em = ... ;
CriteriaBuilder queryBuilder = em.getCriteriaBuilder();
CriteriaQuery qdef = queryBuilder.createQuery();
    	

The first step in constructing a query definition is specification of query roots. Query roots specify the domain objects on which the query is evaluated. Query root is an instance of the Root<T> interface. A query root is added to a CriteriaQuery by addRoot(Class c) method.

    	Root<Customer> customer = qdef.from(Customer.class);
    	

A query domain can be further refined by joining to other domain objects. For example, for the above query definition to operate over customers and their orders, use join(String attribute):

Root<Order> order = customer.join(customer.get(Customer_.orders));
		

where Customer_.orders represent a field of canonical metamodel class for Customer. These canonical metamodel classes are generated during compilation by processing the persistent annotation in the source code of Customer.java.

The condition of a query definition is set via where(Predicate p) where the argument designates a conditional predicate. Conditional predicates are often composed of one or more comparisons between the attribute values of the domain objects and some variable. For example, to select the Customer whose name is "John Doe" and has orders that are not yet delivered, you can build the predicate and set it to the query definition as:

           qdef.where(customer.get(Customer_.name).equal("John Doe")
               .and(order.get(Order_.status).equal(OrderStatus.DELIVERED).not()));
		

The select() method defines the result of the query. If left unspecified, the select projection is assumed to be the root domain object. However, you can specify the selected projections explicitly as a list:

            qdef.select(customer.get(Customer_.name), order.get(Order_.status));
		

An attribute of a domain object can also be specified by navigating via get(String attr). The attribute should refer to a valid persistent property of the receiving domain object, however no such validation is enforced during the construction of the query definition. All validation is deferred until the query is actually executed. On the other hand, using canonical metamodel for path navigate enforces compile type checking.

2. Executing a CriteriaQuery

A CriteriaQuery is executed in a similar fashion to a string-based JPQL query via the EntityManager and Query interfaces.

EntityManager em = ...
Query query = em.createQuery(qdef);
List result = query.getResultList();
    

A query definition can use named parameters, and the parameter values are set as usual in the Query instance.

A developerworks article explains details and further usage of Criteria API and its OpenJPA extensions.

3. Extension to Criteria API

Criteria API has provided an alternative means to string-based JPQL to execute a query. However, JPA 2.0 specification has not explicitly specified any equivalence between a dynamically constructed CriteriaQuery and a JPQL string. OpenJPA provides a mechanism to convert a CriteriaQuery to an equivalent JPQL query string via the extended OpenJPACriteriaQuery API.

public interface OpenJPACriteriaQuery extends CriteriaQuery {
    /**
     * Gets equivalent JPQL String for the given CriteriaQuery.
     */
    public String toCQL();
}
    

4. Generation of Canonical MetaModel classes

Annotation processing tool generates source code for a metamodel class given the annotated source code of persistent entity. This tool is invoked during compilation for JDK6 compiler if OpenJPA and JPA libraries are specified in the compiler -classpath option and Annotation processor option -Aopenjpa.metamodel=true is specified.

 $ javac -classpath path/to/openjpa-all.jar -Aopenjpa.metamodel=true mypackage/MyEntity.java
    

will generate source code for canonical meta-model class mypackage.MyEntity_. The source code is generated relative to the directory specified in -s option of javac compiler and defaulted to the current directory.

The Annotation Processor recognizes the following options specified in the command-line with -A (none of them are mandatory).

  • -Aopenjpa.log=TRACE|INFO|WARN|ERROR : The logging level. Default is WARN.

  • -Aopenjpa.source=<n> : where <n> denotes the integral number for Java source version of the generated code. Default is 6.

  • -Aopenjpa.naming=class name : fully-qualified name of a class implementing org.apache.openjpa.meta.MetaDataFactory that determines the name of a meta-class given the name of the original persistent Java entity class. Defaults to org.apache.openjpa.persistence.PersistenceMetaDataFactory which appends an underscore character (_) to the original Java class name.

  • -Aopenjpa.header=<url> : A url whose content will appear as comment header to the generated file(s). Recognizes special value ASL for Apache Source License header as comment. By default, adds an OpenJPA proprietary text as comment block.

  • -Aopenjpa.addGeneratedAnnotation=auto|force|false : Configure whether the annotation javax.annotation.Generated should be added to the generated metamodel classes. This annotation is not available on Java 9 upwards unless the javax.annotation-api is in the classpath. The default is auto. Set to force to add the annotation even if it is not in the classpath or to false to never add it to the generated class.

Chapter 12.  SQL Queries

JPQL is a powerful query language, but there are times when it is not enough. Maybe you're migrating a JDBC application to JPA on a strict deadline, and you don't have time to translate your existing SQL selects to JPQL. Or maybe a certain query requires database-specific SQL your JPA implementation doesn't support. Or maybe your DBA has spent hours crafting the perfect select statement for a query in your application's critical path. Whatever the reason, SQL queries can remain an essential part of an application.

You are probably familiar with executing SQL queries by obtaining a java.sql.Connection, using the JDBC APIs to create a Statement, and executing that Statement to obtain a ResultSet. And of course, you are free to continue using this low-level approach to SQL execution in your JPA applications. However, JPA also supports executing SQL queries through the javax.persistence.Query interface introduced in Chapter 10, JPA Query . Using a JPA SQL query, you can retrieve either persistent objects or projections of column values. The following sections detail each use.

1.  Creating SQL Queries

The EntityManager has two factory methods suitable for creating SQL queries:

public Query createNativeQuery(String sqlString, Class resultClass);
public Query createNativeQuery(String sqlString, String resultSetMapping);

The first method is used to create a new Query instance that will return instances of the specified class.

The second method uses a SqlResultSetMapping to determine the type of object or objects to return. The example below shows these methods in action.

Example 12.1.  Creating a SQL Query

EntityManager em = ...;
Query query = em.createNativeQuery("SELECT * FROM MAG", Magazine.class);
processMagazines(query.getResultList());

Note

In addition to SELECT statements, OpenJPA supports stored procedure invocations as SQL queries. OpenJPA will assume any SQL that does not begin with the SELECT keyword (ignoring case) is a stored procedure call, and invoke it as such at the JDBC level.

2.  Retrieving Persistent Objects with SQL

When you give a SQL Query a candidate class, it will return persistent instances of that class. At a minimum, your SQL must select the class' primary key columns, discriminator column (if mapped), and version column (also if mapped). The JPA runtime uses the values of the primary key columns to construct each result object's identity, and possibly to match it with a persistent object already in the EntityManager's cache. When an object is not already cached, the implementation creates a new object to represent the current result row. It might use the discriminator column value to make sure it constructs an object of the correct subclass. Finally, the query records available version column data for use in optimistic concurrency checking, should you later change the result object and flush it back to the database.

Aside from the primary key, discriminator, and version columns, any columns you select are used to populate the persistent fields of each result object. JPA implementations will compete on how effectively they map your selected data to your persistent instance fields.

Let's make the discussion above concrete with an example. It uses the following simple mapping between a class and the database:

Example 12.2.  Retrieving Persistent Objects

Query query = em.createNativeQuery("SELECT ISBN, TITLE, PRICE, "
    + "VERS FROM MAG WHERE PRICE > 5 AND PRICE < 10", Magazine.class);
List<Magazine> results = (List<Magazine>) query.getResultList();
for (Magazine mag : results)
    processMagazine(mag);

The query above works as advertised, but isn't very flexible. Let's update it to take in parameters for the minimum and maximum price, so we can reuse it to find magazines in any price range:

Example 12.3.  SQL Query Parameters

Query query = em.createNativeQuery("SELECT ISBN, TITLE, PRICE, "
    + "VERS FROM MAG WHERE PRICE > ?1 AND PRICE < ?2", Magazine.class);

query.setParameter(1, 5d);
query.setParameter(2, 10d);

List<Magazine> results = (List<Magazine>) query.getResultList();
for (Magazine mag : results)
    processMagazine(mag);

Like JDBC prepared statements, SQL queries represent parameters with question marks, but are followed by an integer to represent its index.

Chapter 13.  Mapping Metadata

Object-relational mapping is the process of mapping entities to relational database tables. In JPA, you perform object/relational mapping through mapping metadata. Mapping metadata uses annotations to describe how to link your object model to your relational model.

Note

OpenJPA offers tools to automate mapping and schema creation. See Chapter 7, Mapping in the Reference Guide.

Throughout this chapter, we will draw on the object model introduced in Chapter 5, Metadata . We present that model again below. As we discuss various aspects of mapping metadata, we will zoom in on specific areas of the model and show how we map the object layer to the relational layer.

All mapping metadata is optional. Where no explicit mapping metadata is given, JPA uses the defaults defined by the specification. As we present each mapping throughout this chapter, we also describe the defaults that apply when the mapping is absent.

Note

Mapping metadata is used primarily with schema generation. This metadata should not be relied upon for validation prior to communicating with the database. For example using the @Column(nullable=false) annotation does not do up front validation that the value in the entity is correct.

1.  Table

The Table annotation specifies the table for an entity class. If you omit the Table annotation, base entity classes default to a table with their unqualified class name. The default table of an entity subclass depends on the inheritance strategy, as you will see in Section 6, “ Inheritance ”.

Tables have the following properties:

  • String name: The name of the table. Defaults to the unqualified entity class name.

  • String schema: The table's schema. If you do not name a schema, JPA uses the default schema for the database connection.

  • String catalog: The table's catalog. If you do not name a catalog, JPA uses the default catalog for the database connection.

  • UniqueConstraint[] uniqueConstraints: An array of unique constraints to place on the table. We cover unique constraints below. Defaults to an empty array.

The equivalent XML element is table. It has the following attributes, which correspond to the annotation properties above:

  • name

  • schema

  • catalog

The table element also accepts nested unique-constraint elements representing unique constraints. We will detail unique constraints shortly.

Sometimes, some of the fields in a class are mapped to secondary tables. In that case, use the class' Table annotation to name what you consider the class' primary table. Later, we will see how to map certain fields to other tables.

The example below maps classes to tables to separate schemas. The CONTRACT, SUB, and LINE_ITEM tables are in the CNTRCT schema; all other tables are in the default schema.

Example 13.1.  Mapping Classes

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
public class Magazine {
    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART")
public class Article {
    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {
    ...
}

@Entity
@Table(name="AUTH")
public class Author {
    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {
    ...
}

@Entity
@Table(schema="CNTRCT")
public class Contract
    extends Document {
    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
public class Subscription {
    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {
    ...
}

The same mapping information expressed in XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        ...
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        ...
    </entity>
    <entity class="org.mag.Article">
        <table name="ART"/>
        ...
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        ...
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        ...
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        ...
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        ...
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        ...
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        ...
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        ...
    </entity>
    <embeddable class="org.mag.pub.Address">
        ...
    </embeddable>
</entity-mappings>

2.  Unique Constraints

Unique constraints ensure that the data in a column or combination of columns is unique for each row. A table's primary key, for example, functions as an implicit unique constraint. In JPA, you represent other unique constraints with an array of UniqueConstraint annotations within the table annotation. The unique constraints you define are used during table creation to generate the proper database constraints, and may also be used at runtime to order INSERT, UPDATE , and DELETE statements. For example, suppose there is a unique constraint on the columns of field F. In the same transaction, you remove an object A and persist a new object B, both with the same F value. The JPA runtime must ensure that the SQL deleting A is sent to the database before the SQL inserting B to avoid a unique constraint violation.

UniqueConstraint has these properties:

  • String name: The name of the constraint. OpenJPA will choose a name if you do not provide one, or will create an anonymous constraint.

  • String[] columnNames: The names of the columns the constraint spans.

In XML, unique constraints are represented by nesting unique-constraint elements within the table element. Each unique-constraint element in turn nests column-name text elements to enumerate the constraint's columns.

Example 13.2.  Defining a Unique Constraint

The following defines a unique constraint on the TITLE column of the ART table:

@Entity
@Table(name="ART", uniqueConstraints=@UniqueConstraint(name="TITLE_CNSTR", columnNames="TITLE"))
public class Article {
    ...
}

The same metadata expressed in XML form:

<entity class="org.mag.Article">
    <table name="ART">
        <unique-constraint>
            <name>TITLE_CNSTR</name>
            <column-name>TITLE</column-name>
        </unique-constraint>
    </table>
    ...
</entity>

3.  Column

In the previous section, we saw that a UniqueConstraint uses an array of column names. Field mappings, however, use full-fledged Column annotations. Column annotations have the following properties:

  • String name: The column name. Defaults to the field name.

  • String columnDefinition: The database-specific column type name. This property is only used by vendors that support creating tables from your mapping metadata. During table creation, the vendor will use the value of the columnDefinition as the declared column type. If no columnDefinition is given, the vendor will choose an appropriate default based on the field type combined with the column's length, precision, and scale.

  • int length: The column length. This property is typically only used during table creation, though some vendors might use it to validate data before flushing. CHAR and VARCHAR columns typically default to a length of 255; other column types use the database default.

  • int precision: The precision of a numeric column. This property is often used in conjunction with scale to form the proper column type name during table creation.

  • int scale: The number of decimal digits a numeric column can hold. This property is often used in conjunction with precision to form the proper column type name during table creation.

  • boolean nullable: Whether the column can store null values. Vendors may use this property both for table creation and at runtime; however, it is never required. Defaults to true.

  • boolean insertable: By setting this property to false, you can omit the column from SQL INSERT statements. Defaults to true.

  • boolean updatable: By setting this property to false, you can omit the column from SQL UPDATE statements. Defaults to true.

  • String table: Sometimes you will need to map fields to tables other than the primary table. This property allows you specify that the column resides in a secondary table. We will see how to map fields to secondary tables later in the chapter.

The equivalent XML element is column. This element has attributes that are exactly equivalent to the Column annotation's properties described above:

  • name

  • column-definition

  • length

  • precision

  • scale

  • insertable

  • updatable

  • table

4.  Identity Mapping

With our new knowledge of columns, we can map the identity fields of our entities. The diagram below now includes primary key columns for our model's tables. The primary key column for Author uses nonstandard type INTEGER64, and the Magazine.isbn field is mapped to a VARCHAR(9) column instead of a VARCHAR(255) column, which is the default for string fields. We do not need to point out either one of these oddities to the JPA implementation for runtime use. If, however, we want to use the JPA implementation to create our tables for us, it needs to know about any desired non-default column types. Therefore, the example following the diagram includes this data in its encoding of our mappings.

Note that many of our identity fields do not need to specify column information, because they use the default column name and type.

Example 13.3.  Identity Mapping

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
public class Article {

    @Id private long id;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Column(name="AID", columnDefinition="INTEGER64")
    @Id private long id;

    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...
}

@Entity
@Table(schema="CNTRCT")
public class Contract
    extends Document {
    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
public class Subscription {

    @Id private long id;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {
    ...
}

The same metadata for Magazine and Company expressed in XML form:

<entity class="org.mag.Magazine">
    <id-class class="org.mag.Magazine.Magazine.MagazineId"/>
    <table name="MAG"/>
    <attributes>
        <id name="isbn">
            <column length="9"/>
        </id>
        <id name="title"/>
      ...
    </attributes>
</entity>
<entity class="org.mag.pub.Company">
    <table name="COMP"/>
    <attributes>
        <id name="id">
            <column name="CID"/>
        </id>
        ...
    </attributes>
</entity>

5.  Generators

One aspect of identity mapping not covered in the previous section is JPA's ability to automatically assign a value to your numeric identity fields using generators. We discussed the available generator types in Section 2.3, “ Id ”. Now we show you how to define named generators.

5.1.  Sequence Generator

Most databases allow you to create native sequences. These are database structures that generate increasing numeric values. The SequenceGenerator annotation represents a named database sequence. You can place the annotation on any package, entity class, persistent field declaration (if your entity uses field access), or getter method for a persistent property (if your entity uses property access). SequenceGenerator has the following properties:

  • String name: The generator name. This property is required.

  • String sequenceName: The name of the database sequence. If you do not specify the database sequence, your vendor will choose an appropriate default.

  • int initialValue: The initial sequence value.

  • int allocationSize: The number of values to allocate in memory for each trip to the database. Allocating values in memory allows the JPA runtime to avoid accessing the database for every sequence request. This number also specifies the amount that the sequence value is incremented each time the sequence is accessed. Defaults to 50.

  • String schema: The sequence's schema. If you do not name a schema, JPA uses the default schema for the database connection.

Note

OpenJPA allows you to use one of OpenJPA's built-in generator implementations in the sequenceName property. You can also set the sequenceName to system to use the system sequence defined by the openjpa.Sequence configuration property. See the Reference Guide's Section 6, “ Generators ” for details.

The XML element for a sequence generator is sequence-generator . Its attributes mirror the above annotation's properties:

  • name

  • sequence-name

  • initial-value

  • allocation-size

  • schema

To use a sequence generator, set your GeneratedValue annotation's strategy property to GenerationType.SEQUENCE, and its generator property to the sequence generator's declared name. Or equivalently, set your generated-value XML element's strategy attribute to SEQUENCE and its generator attribute to the generator name.

5.2.  Table Generator

A TableGenerator refers to a database table used to store increasing sequence values for one or more entities. As with SequenceGenerator, you can place the TableGenerator annotation on any package, entity class, persistent field declaration (if your entity uses field access), or getter method for a persistent property (if your entity uses property access). TableGenerator has the following properties:

  • String name: The generator name. This property is required.

  • String table: The name of the generator table. If left unspecified, your vendor will choose a default table.

  • String schema: The named table's schema.

  • String catalog: The named table's catalog.

  • String pkColumnName: The name of the primary key column in the generator table. If unspecified, your implementation will choose a default.

  • String valueColumnName: The name of the column that holds the sequence value. If unspecified, your implementation will choose a default.

  • String pkColumnValue: The primary key column value of the row in the generator table holding this sequence value. You can use the same generator table for multiple logical sequences by supplying different pkColumnValue s. If you do not specify a value, the implementation will supply a default.

  • int initialValue: The value of the generator's first issued number.

  • int allocationSize: The number of values to allocate in memory for each trip to the database. Allocating values in memory allows the JPA runtime to avoid accessing the database for every sequence request. This number also specifies the amount that the sequence value is incremented each time the generator table is updated. Defaults to 50.

The XML equivalent is the table-generator element. This element's attributes correspond exactly to the above annotation's properties:

  • name

  • table

  • schema

  • catalog

  • pk-column-name

  • value-column-name

  • pk-column-value

  • initial-value

  • allocation-size

To use a table generator, set your GeneratedValue annotation's strategy property to GenerationType.TABLE, and its generator property to the table generator's declared name. Or equivalently, set your generated-value XML element's strategy attribute to TABLE and its generator attribute to the generator name.

5.3.  Example

Let's take advantage of generators in our entity model. Here are our updated mappings.

Example 13.4.  Generator Mapping

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
@SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
    private long id;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
    @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
        valueColumnName="AID")
    @Column(name="AID", columnDefinition="INTEGER64")
    private long id;

    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(generate=GenerationType.IDENTITY)
    private long id;

    ...
}

@Entity
@Table(schema="CNTRCT")
public class Contract
    extends Document {

    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
public class Subscription {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {
    ...
}

The same metadata for Article and Author expressed in XML form:

<entity class="org.mag.Article">
    <table name="ART">
        <unique-constraint>
            <column-name>TITLE</column-name>
        </unique-constraint>
    </table>
    <sequence-generator name="ArticleSeq" sequence-name="ART_SEQ"/>
    <attributes>
        <id name="id">
            <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
        </id>
        ...
    </attributes>
</entity>
<entity class="org.mag.pub.Author">
    <table name="AUTH"/>
    <attributes>
        <id name="id">
            <column name="AID" column-definition="INTEGER64"/>
            <generated-value strategy="TABLE" generator="AuthorGen"/>
            <table-generator name="AuthorGen" table="AUTH_GEN"
                pk-column-name="PK" value-column-name="AID"/>
        </id>
        ...
    </attributes>
</entity>

6.  Inheritance

In the 1990's programmers coined the term impedance mismatch to describe the difficulties in bridging the object and relational worlds. Perhaps no feature of object modeling highlights the impedance mismatch better than inheritance. There is no natural, efficient way to represent an inheritance relationship in a relational database.

Luckily, JPA gives you a choice of inheritance strategies, making the best of a bad situation. The base entity class defines the inheritance strategy for the hierarchy with the Inheritance annotation. Inheritance has the following properties:

  • InheritanceType strategy: Enum value declaring the inheritance strategy for the hierarchy. Defaults to InheritanceType.SINGLE_TABLE. We detail each of the available strategies below.

The corresponding XML element is inheritance, which has a single attribute:

  • strategy: One of SINGLE_TABLE, JOINED, or TABLE_PER_CLASS.

The following sections describe JPA's standard inheritance strategies.

Note

OpenJPA allows you to vary your inheritance strategy for each class, rather than forcing a single strategy per inheritance hierarchy. See Section 7, “ Additional JPA Mappings ” in the Reference Guide for details.

6.1.  Single Table

The InheritanceType.SINGLE_TABLE strategy maps all classes in the hierarchy to the base class' table.

In our model, Subscription is mapped to the CNTRCT.SUB table. LifetimeSubscription, which extends Subscription, adds its field data to this table as well.

Example 13.5.  Single Table Mapping

@Entity
@Table(name="SUB", schema="CNTRCT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Subscription {
    ...
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

The same metadata expressed in XML form:

<entity class="org.mag.subcribe.Subscription">
    <table name="SUB" schema="CNTRCT"/>
    <inheritance strategy="SINGLE_TABLE"/>
    ...
</entity>
<entity class="org.mag.subscribe.LifetimeSubscription">
    ...
</entity>

Single table inheritance is the default strategy. Thus, we could omit the @Inheritance annotation in the example above and get the same result.

Note

Mapping subclass state to the superclass table is often called flat inheritance mapping.

6.1.1.  Advantages

Single table inheritance mapping is the fastest of all inheritance models, since it never requires a join to retrieve a persistent instance from the database. Similarly, persisting or updating a persistent instance requires only a single INSERT or UPDATE statement. Finally, relations to any class within a single table inheritance hierarchy are just as efficient as relations to a base class.

6.1.2.  Disadvantages

The larger the inheritance model gets, the "wider" the mapped table gets, in that for every field in the entire inheritance hierarchy, a column must exist in the mapped table. This may have undesirable consequence on the database size, since a wide or deep inheritance hierarchy will result in tables with many mostly-empty columns.

6.2.  Joined

The InheritanceType.JOINED strategy uses a different table for each class in the hierarchy. Each table only includes state declared in its class. Thus to load a subclass instance, the JPA implementation must read from the subclass table as well as the table of each ancestor class, up to the base entity class.

Note

Using joined subclass tables is also called vertical inheritance mapping.

PrimaryKeyJoinColumn annotations tell the JPA implementation how to join each subclass table record to the corresponding record in its direct superclass table. In our model, the LINE_ITEM.ID column joins to the CONTRACT.ID column. The PrimaryKeyJoinColumn annotation has the following properties:

  • String name: The name of the subclass table column. When there is a single identity field, defaults to that field's column name.

  • String referencedColumnName: The name of the superclass table column this subclass table column joins to. When there is a single identity field, defaults to that field's column name.

  • String columnDefinition: This property has the same meaning as the columnDefinition property on the Column annotation, described in Section 3, “ Column ”.

The XML equivalent is the primary-key-join-column element. Its attributes mirror the annotation properties described above:

  • name

  • referenced-column-name

  • column-definition

The example below shows how we use InheritanceTable.JOINED and a primary key join column to map our sample model according to the diagram above. Note that a primary key join column is not strictly needed, because there is only one identity column, and the subclass table column has the same name as the superclass table column. In this situation, the defaults suffice. However, we include the primary key join column for illustrative purposes.

Example 13.6.  Joined Subclass Tables

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
public class Contract
    extends Document {
    ...
}

public class Subscription {
    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
    public static class LineItem
        extends Contract {
        ...
    }
}

The same metadata expressed in XML form:

<entity class="org.mag.subcribe.Contract">
    <table schema="CNTRCT"/>
    <inheritance strategy="JOINED"/>
    ...
</entity>
<entity class="org.mag.subscribe.Subscription.LineItem">
    <table name="LINE_ITEM" schema="CNTRCT"/>
    <primary-key-join-column name="ID" referenced-column-name="PK"/>
    ...
</entity>

When there are multiple identity columns, you must define multiple PrimaryKeyJoinColumns using the aptly-named PrimaryKeyJoinColumns annotation. This annotation's value is an array of PrimaryKeyJoinColumn s. We could rewrite LineItem's mapping as:

@Entity
@Table(name="LINE_ITEM", schema="CNTRCT")
@PrimaryKeyJoinColumns({
    @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
})
public static class LineItem
    extends Contract {
    ...
}

In XML, simply list as many primary-key-join-column elements as necessary.

6.2.1.  Advantages

The joined strategy has the following advantages:

  1. Using joined subclass tables results in the most normalized database schema, meaning the schema with the least spurious or redundant data.

  2. As more subclasses are added to the data model over time, the only schema modification that needs to be made is the addition of corresponding subclass tables in the database (rather than having to change the structure of existing tables).

  3. Relations to a base class using this strategy can be loaded through standard joins and can use standard foreign keys, as opposed to the machinations required to load polymorphic relations to table-per-class base types, described below.

6.2.2.  Disadvantages

Aside from certain uses of the table-per-class strategy described below, the joined strategy is often the slowest of the inheritance models. Retrieving any subclass requires one or more database joins, and storing subclasses requires multiple INSERT or UPDATE statements. This is only the case when persistence operations are performed on subclasses; if most operations are performed on the least-derived persistent superclass, then this mapping is very fast.

Note

When executing a select against a hierarchy that uses joined subclass table inheritance, you must consider how to load subclass state. Section 8, “ Eager Fetching ” in the Reference Guide describes OpenJPA's options for efficient data loading.

6.3.  Table Per Class

Like the JOINED strategy, the InheritanceType.TABLE_PER_CLASS strategy uses a different table for each class in the hierarchy. Unlike the JOINED strategy, however, each table includes all state for an instance of the corresponding class. Thus to load a subclass instance, the JPA implementation must only read from the subclass table; it does not need to join to superclass tables.

Suppose that our sample model's Magazine class has a subclass Tabloid. The classes are mapped using the table-per-class strategy, as in the diagram above. In a table-per-class mapping, Magazine's table MAG contains all state declared in the base Magazine class. Tabloid maps to a separate table, TABLOID. This table contains not only the state declared in the Tabloid subclass, but all the base class state from Magazine as well. Thus the TABLOID table would contain columns for isbn, title, and other Magazine fields. These columns would default to the names used in Magazine's mapping metadata. Section 8.3, “ Embedded Mapping ” will show you how to use AttributeOverrides and AssociationOverride s to override superclass field mappings.

Example 13.7.  Table Per Class Mapping

@Entity
@Table(name="MAG")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Magazine {
    ...
}

@Entity
@Table(name="TABLOID")
public class Tabloid
    extends Magazine {
    ...
}

And the same classes in XML:

<entity class="org.mag.Magazine">
    <table name="MAG"/>
    <inheritance strategy="TABLE_PER_CLASS"/>
    ...
</entity>
<entity class="org.mag.Tabloid">
    <table name="TABLOID"/>
    ...
</entity>

6.3.1.  Advantages

The table-per-class strategy is very efficient when operating on instances of a known class. Under these conditions, the strategy never requires joining to superclass or subclass tables. Reads, joins, inserts, updates, and deletes are all efficient in the absence of polymorphic behavior. Also, as in the joined strategy, adding additional classes to the hierarchy does not require modifying existing class tables.

6.3.2.  Disadvantages

Polymorphic relations to non-leaf classes in a table-per-class hierarchy have many limitations. When the concrete subclass is not known, the related object could be in any of the subclass tables, making joins through the relation impossible. This ambiguity also affects identity lookups and queries; these operations require multiple SQL SELECTs (one for each possible subclass), or a complex UNION.

Note

Section 8.1, “ Table Per Class ” in the Reference Guide describes the limitations OpenJPA places on table-per-class mapping.

6.4.  Putting it All Together

Now that we have covered JPA's inheritance strategies, we can update our mapping document with inheritance information. Here is the complete model:

And here is the corresponding mapping metadata:

Example 13.8.  Inheritance Mapping

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
@SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
    private long id;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
    @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
        valueColumnName="AID")
    @Column(name="AID", columnDefinition="INTEGER64")
    private long id;

    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...
}

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
public class Contract
    extends Document {
    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Subscription {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
public class TrialSubscription
    extends Subscription {
    ...
}

The same metadata expressed in XML form:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            ...
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART">
            <unique-constraint>
               <column-name>TITLE</column-name>
            </unique-constraint>
        </table>
        <sequence-generator name="ArticleSeq" sequence-name="ART_SEQ"/>
        <attributes>
            <id name="id">
                <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
            </id>
           ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            <id name="id">
                <column name="CID"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <id name="id">
                <column name="AID" column-definition="INTEGER64"/>
                <generated-value strategy="TABLE" generator="AuthorGen"/>
                <table-generator name="AuthorGen" table="AUTH_GEN"
                    pk-column-name="PK" value-column-name="AID"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        <inheritance strategy="JOINED"/>
        <attributes>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <inheritance strategy="SINGLE_TABLE"/>
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <primary-key-join-column name="ID" referenced-column-name="PK"/>
        ...
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        ...
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        ...
    </entity>
</entity-mappings>

7.  Discriminator

The single table inheritance strategy results in a single table containing records for two or more different classes in an inheritance hierarchy. Similarly, using the joined strategy results in the superclass table holding records for superclass instances as well as for the superclass state of subclass instances. When selecting data, JPA needs a way to differentiate a row representing an object of one class from a row representing an object of another. That is the job of the discriminator column.

The discriminator column is always in the table of the base entity. It holds a different value for records of each class, allowing the JPA runtime to determine what class of object each row represents.

The DiscriminatorColumn annotation represents a discriminator column. It has these properties:

  • String name: The column name. Defaults to DTYPE .

  • length: For string discriminator values, the length of the column. Defaults to 31.

  • String columnDefinition: This property has the same meaning as the columnDefinition property on the Column annotation, described in Section 3, “ Column ”.

  • DiscriminatorType discriminatorType: Enum value declaring the discriminator strategy of the hierarchy.

The corresponding XML element is discriminator-column. Its attributes mirror the annotation properties above:

  • name

  • length

  • column-definition

  • discriminator-type: One of STRING, CHAR, or INTEGER.

The DiscriminatorValue annotation specifies the discriminator value for each class. Though this annotation's value is always a string, the implementation will parse it according to the DiscriminatorColumn's discriminatorType property above. The type defaults to DiscriminatorType.STRING, but may be DiscriminatorType.CHAR or DiscriminatorType.INTEGER. If you do not specify a DiscriminatorValue, the provider will choose an appropriate default.

The corresponding XML element is discriminator-value. The text within this element is parsed as the discriminator value.

Note

OpenJPA assumes your model employs a discriminator column if any of the following are true:

  1. The base entity explicitly declares an inheritance type of SINGLE_TABLE.

  2. The base entity sets a discriminator value.

  3. The base entity declares a discriminator column.

Only SINGLE_TABLE inheritance hierarchies require a discriminator column and values. JOINED hierarchies can use a discriminator to make some operations more efficient, but do not require one. TABLE_PER_CLASS hierarchies have no use for a discriminator.

OpenJPA defines additional discriminator strategies; see Section 7, “ Additional JPA Mappings ” in the Reference Guide for details. OpenJPA also supports final entity classes. OpenJPA does not use a discriminator on final classes.

We can now translate our newfound knowledge of JPA discriminators into concrete JPA mappings. We first extend our diagram with discriminator columns:

Next, we present the updated mapping document. Notice that in this version, we have removed explicit inheritance annotations when the defaults sufficed. Also, notice that entities using the default DTYPE discriminator column mapping do not need an explicit DiscriminatorColumn annotation.

Example 13.9.  Discriminator Mapping

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
@DiscriminatorValue("Mag")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
@SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
    private long id;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
    @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
        valueColumnName="AID")
    @Column(name="AID", columnDefinition="INTEGER64")
    private long id;

    ...
}

@Embeddable
public class Address {
    ...
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...
}

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="CTYPE")
public class Contract
    extends Document {
    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
@DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
@DiscriminatorValue("1")
public class Subscription {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {
        ...
    }
}

@Entity(name="Lifetime")
@DiscriminatorValue("2")
public class LifetimeSubscription
    extends Subscription {
    ...
}

@Entity(name="Trial")
@DiscriminatorValue("3")
public class TrialSubscription
    extends Subscription {
    ...
}

The same metadata expressed in XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            ...
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        <discriminator-value>Mag</discriminator-value>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART">
            <unique-constraint>
                <column-name>TITLE</column-name>
            </unique-constraint>
        </table>
        <sequence-generator name="ArticleSeq" sequence-name="ART_SEQ"/>
        <attributes>
            <id name="id">
                <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            <id name="id">
                <column name="CID"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <id name="id">
                <column name="AID" column-definition="INTEGER64"/>
                <generated-value strategy="TABLE" generator="AuthorGen"/>
                <table-generator name="AuthorGen" table="AUTH_GEN"
                    pk-column-name="PK" value-column-name="AID"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        <inheritance strategy="JOINED"/>
        <discriminator-column name="CTYPE"/>
        <attributes>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <inheritance strategy="SINGLE_TABLE"/>
        <discriminator-value>1</discriminator-value>
        <discriminator-column name="KIND" discriminator-type="INTEGER"/>
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <primary-key-join-column name="ID" referenced-column-name="PK"/>
        ...
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        <discriminator-value>2</discriminator-value>
        ...
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        <discriminator-value>3</discriminator-value>
        ...
    </entity>
</entity-mappings>

8.  Field Mapping

The following sections enumerate the myriad of field mappings JPA supports. JPA augments the persistence metadata covered in Chapter 5, Metadata with many new object-relational annotations. As we explore the library of standard mappings, we introduce each of these enhancements in context.

Note

OpenJPA supports many additional field types, and allows you to create custom mappings for unsupported field types or database schemas. See the Reference Guide's Chapter 7, Mapping for complete coverage of OpenJPA's mapping capabilities.

8.1.  Basic Mapping

A basic field mapping stores the field value directly into a database column. The following field metadata types use basic mapping. These types were defined in Section 2, “ Field and Property Metadata ”.

In fact, you have already seen examples of basic field mappings in this chapter - the mapping of all identity fields in Example 13.3, “ Identity Mapping ”. As you saw in that section, to write a basic field mapping you use the Column annotation to describe the column the field value is stored in. We discussed the Column annotation in Section 3, “ Column ”. Recall that the name of the column defaults to the field name, and the type of the column defaults to an appropriate type for the field type. These defaults allow you to sometimes omit the annotation altogether.

8.1.1.  LOBs

Adding the Lob marker annotation to a basic field signals that the data is to be stored as a LOB (Large OBject). If the field holds string or character data, it will map to a CLOB (Character Large OBject) database column. If the field holds any other data type, it will be stored as binary data in a BLOB (Binary Large OBject) column. The implementation will serialize the Java value if needed.

The equivalent XML element is lob, which has no children or attributes.

Note

OpenJPA also supports LOB streaming. See Section 7.11, “ LOB Streaming ” in the Reference Guide for details.

8.1.2.  Enumerated

You can apply the Enumerated annotation to your Enum fields to control how they map to the database. The Enumerated annotation's value one of the following constants from the EnumType enum:

  • EnumType.ORDINAL: The default. The persistence implementation places the ordinal value of the enum in a numeric column. This is an efficient mapping, but may break if you rearrange the Java enum declaration.

  • EnumType.STRING: Store the name of the enum value rather than the ordinal. This mapping uses a VARCHAR column rather than a numeric one.

The Enumerated annotation is optional. Any un-annotated enumeration field defaults to ORDINAL mapping.

The corresponding XML element is enumerated. Its embedded text must be one of STRING or ORIDINAL.

8.1.3.  Temporal Types

The Temporal annotation determines how the implementation handles your basic java.util.Date and java.util.Calendar fields at the JDBC level. The Temporal annotation's value is a constant from the TemporalType enum. Available values are:

  • TemporalType.TIMESTAMP: The default. Use JDBC's timestamp APIs to manipulate the column data.

  • TemporalType.DATE: Use JDBC's SQL date APIs to manipulate the column data.

  • TemporalType.TIME: Use JDBC's time APIs to manipulate the column data.

If the Temporal annotation is omitted, the implementation will treat the data as a timestamp.

The corresponding XML element is temporal, whose text value must be one of: TIME, DATE, or TIMESTAMP.

8.1.4.  The Updated Mappings

Below we present an updated diagram of our model and its associated database schema, followed by the corresponding mapping metadata. Note that the mapping metadata relies on defaults where possible. Also note that as a mapped superclass, Document can define mappings that will automatically transfer to its subclass' tables. In Section 8.3, “ Embedded Mapping ”, you will see how a subclass can override its mapped superclass' mappings.

Example 13.10.  Basic Field Mapping

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
@DiscriminatorValue("Mag")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    @Column(name="VERS")
    @Version private int version;

    private String name;
    private double price;

    @Column(name="COPIES")
    private int copiesSold;

    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
@SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
    private long id;

    @Column(name="VERS")
    @Version private int version;

    private String title;
    private byte[] content;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    @Column(name="VERS")
    @Version private int version;

    private String name;

    @Column(name="REV")
    private double revenue;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
    @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
        valueColumnName="AID")
    @Column(name="AID", columnDefinition="INTEGER64")
    private long id;

    @Column(name="VERS")
    @Version private int version;

    @Column(name="FNAME")
    private String firstName;

    @Column(name="LNAME")
    private String lastName;

    ...
}

@Embeddable
public class Address {
    ...
}

package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(name="VERS")
    @Version private int version;

    ...
}

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="CTYPE")
public class Contract
    extends Document {

    @Lob
    private String terms;

    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
@DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
@DiscriminatorValue("1")
public class Subscription {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(name="VERS")
    @Version private int version;

    @Column(name="START")
    private Date startDate;

    @Column(name="PAY")
    private double payment;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {

        @Column(name="COMM")
        private String comments;

        private double price;
        private long num;
        ...
    }
}

@Entity(name="Lifetime")
@DiscriminatorValue("2")
public class LifetimeSubscription
    extends Subscription {

    @Basic(fetch=FetchType.LAZY)
    @Column(name="ELITE")
    private boolean getEliteClub() { ... }
    public void setEliteClub(boolean elite) { ... }

    ...
}

@Entity(name="Trial")
@DiscriminatorValue("3")
public class TrialSubscription
    extends Subscription {

    @Column(name="END")
    public Date getEndDate() { ... }
    public void setEndDate(Date end) { ... }

    ...
}

The same metadata expressed in XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            <version name="version">
                <column name="VERS"/>
            </version>
            ...
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        <discriminator-value>Mag</discriminator-value>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            <basic name="name"/>
            <basic name="price"/>
            <basic name="copiesSold">
                <column name="COPIES"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART">
            <unique-constraint>
               <column-name>TITLE</column-name>
            </unique-constraint>
        </table>
        <sequence-generator name="ArticleSeq", sequenceName="ART_SEQ"/>
        <attributes>
            <id name="id">
                <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
            </id>
            <basic name="title"/>
            <basic name="content"/>
            <version name="version">
                <column name="VERS"/>
            </version>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            <id name="id">
                <column name="CID"/>
            </id>
            <basic name="name"/>
            <basic name="revenue">
                <column name="REV"/>
            </basic>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <id name="id">
                <column name="AID" column-definition="INTEGER64"/>
                <generated-value strategy="TABLE" generator="AuthorGen"/>
                <table-generator name="AuthorGen" table="AUTH_GEN"
                    pk-column-name="PK" value-column-name="AID"/>
            </id>
            <basic name="firstName">
                <column name="FNAME"/>
            </basic>
            <basic name="lastName">
                <column name="LNAME"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        <inheritance strategy="JOINED"/>
        <discriminator-column name="CTYPE"/>
        <attributes>
            <basic name="terms">
                <lob/>
            </basic>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <inheritance strategy="SINGLE_TABLE"/>
        <discriminator-value>1</discriminator-value>
        <discriminator-column name="KIND" discriminator-type="INTEGER"/>
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            <basic name="payment">
                <column name="PAY"/>
            </basic>
            <basic name="startDate">
                <column name="START"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <primary-key-join-column name="ID" referenced-column-name="PK"/>
        <attributes>
            <basic name="comments">
                <column name="COMM"/>
            </basic>
            <basic name="price"/>
            <basic name="num"/>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        <discriminator-value>2</discriminator-value>
        <attributes>
            <basic name="eliteClub" fetch="LAZY">
                <column name="ELITE"/>
            </basic>
            ...
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        <discriminator-value>3</discriminator-value>
        <attributes>
            <basic name="endDate">
                <column name="END"/>
            </basic>
            ...
        </attributes>
    </entity>
</entity-mappings>

8.2.  Secondary Tables

Sometimes a logical record is spread over multiple database tables. JPA calls a class' declared table the primary table, and calls other tables that make up a logical record secondary tables. You can map any persistent field to a secondary table. Just write the standard field mapping, then perform these two additional steps:

  1. Set the table attribute of each of the field's columns or join columns to the name of the secondary table.

  2. Define the secondary table on the entity class declaration.

You define secondary tables with the SecondaryTable annotation. This annotation has all the properties of the Table annotation covered in Section 1, “ Table ” , plus a pkJoinColumns property.

The pkJoinColumns property is an array of PrimaryKeyJoinColumns dictating how to join secondary table records to their owning primary table records. Each PrimaryKeyJoinColumn joins a secondary table column to a primary key column in the primary table. See Section 6.2, “ Joined ” above for coverage of PrimaryKeyJoinColumn's properties.

The corresponding XML element is secondary-table. This element has all the attributes of the table element, but also accepts nested primary-key-join-column elements.

In the following example, we move the Article.content field we mapped in Section 8.1, “ Basic Mapping ” into a joined secondary table, like so:

Example 13.11.  Secondary Table Field Mapping

package org.mag;

@Entity
@Table(name="ART")
@SecondaryTable(name="ART_DATA",
    pkJoinColumns=@PrimaryKeyJoinColumn(name="ART_ID", referencedColumnName="ID"))
public class Article {

    @Id private long id;

    @Column(table="ART_DATA")
    private byte[] content;

    ...
}

And in XML:

<entity class="org.mag.Article">
    <table name="ART"/>
    <secondary-table name="ART_DATA">
        <primary-key-join-column name="ART_ID" referenced-column-name="ID"/>
    </secondary-table>
    <attributes>
        <id name="id"/>
        <basic name="content">
            <column table="ART_DATA"/>
        </basic>
        ...
    </attributes>
</entity>

8.3.  Embedded Mapping

Chapter 5, Metadata describes JPA's concept of embeddable objects. The field values of embedded objects are stored as part of the owning record, rather than as a separate database record. Thus, instead of mapping a relation to an embeddable object as a foreign key, you map all the fields of the embeddable instance to columns in the owning field's table.

JPA defaults the embedded column names and descriptions to those of the embeddable class' field mappings. The AttributeOverride annotation overrides a basic embedded mapping. This annotation has the following properties:

  • String name: The name of the embedded class' field being mapped to this class' table.

  • Column column: The column defining the mapping of the embedded class' field to this class' table.

The corresponding XML element is attribute-override. It has a single name attribute to name the field being overridden, and a single column child element.

To declare multiple overrides, use the AttributeOverrides annotation, whose value is an array of AttributeOverride s. In XML, simply list multiple attribute-override elements in succession.

To override a many to one or one to one relationship, use the AssociationOverride annotation in place of AttributeOverride. AssociationOverride has the following properties:

  • String name: The name of the embedded class' field being mapped to this class' table.

  • JoinColumn[] joinColumns: The foreign key columns joining to the related record.

The corresponding XML element is association-override. It has a single name attribute to name the field being overridden, and one or more join-column child elements.

To declare multiple relation overrides, use the AssociationOverrides annotation, whose value is an array of AssociationOverride s. In XML, simply list multiple association-override elements in succession.

Example 13.12.  Embedded Field Mapping

In this example, Company overrides the default mapping of Address.street and Address.city. All other embedded mappings are taken from the Address embeddable class.

package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="street", column=@Column(name="STRT")),
        @AttributeOverride(name="city", column=@Column(name="ACITY"))
    })
    private Address address;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    // use all defaults from Address class mappings
    private Address address;

    ...
}

@Embeddable
public class Address {

    private String street;
    private String city;
    @Column(columnDefinition="CHAR(2)")
    private String state;
    private String zip;
}

The same metadata expressed in XML:

<entity class="org.mag.pub.Company">
    <table name="COMP"/>
    <attributes>
        ...
        <embedded name="address">
            <attribute-override name="street">
               <column name="STRT"/>
            </attribute-override>
            <attribute-override name="city">
               <column name="ACITY"/>
            </attribute-override>
        </embedded>
    </attributes>
</entity>
<entity class="org.mag.pub.Author">
    <table name="AUTH"/>
    <attributes>
        <embedded name="address">
            <!-- use all defaults from Address -->
        </embedded>
    </attributes>
</entity>
<embeddable class="org.mag.pub.Address">
    <attributes>
        <basic name="street"/>
        <basic name="city"/>
        <basic name="state">
            <column column-definition="CHAR(2)"/>
        </basic>
        <basic name="zip"/>
    </attributes>
</embeddable>

You can also use attribute overrides on an entity class to override mappings defined by its mapped superclass or table-per-class superclass. The example below re-maps the Document.version field to the Contract table's CVERSION column.

Example 13.13.  Mapping Mapped Superclass Field

@MappedSuperclass
public abstract class Document {

    @Column(name="VERS")
    @Version private int version;

    ...
}

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="CTYPE")
@AttributeOverride(name="version", column=@Column(name="CVERSION"))
public class Contract
    extends Document {
    ...
}

The same metadata expressed in XML form:

<mapped-superclass class="org.mag.subcribe.Document">
    <attributes>
        <version name="version">
            <column name="VERS">
        </version>
        ...
    </attributes>
</mapped-superclass>
<entity class="org.mag.subcribe.Contract">
    <table schema="CNTRCT"/>
    <inheritance strategy="JOINED"/>
    <discriminator-column name="CTYPE"/>
    <attribute-override name="version">
        <column name="CVERSION"/>
    </attribute-override>
    <attributes>
        ...
    </attributes>
</entity>

8.4.  Direct Relations

A direct relation is a non-embedded persistent field that holds a reference to another entity. many to one and one to one metadata field types are mapped as direct relations. Our model has three direct relations: Magazine's publisher field is a direct relation to a Company, Magazine's coverArticle field is a direct relation to Article, and the LineItem.magazine field is a direct relation to a Magazine. Direct relations are represented in the database by foreign key columns:

You typically map a direct relation with JoinColumn annotations describing how the local foreign key columns join to the primary key columns of the related record. The JoinColumn annotation exposes the following properties:

  • String name: The name of the foreign key column. Defaults to the relation field name, plus an underscore, plus the name of the referenced primary key column.

  • String referencedColumnName: The name of the primary key column being joined to. If there is only one identity field in the related entity class, the join column name defaults to the name of the identity field's column.

  • boolean unique: Whether this column is guaranteed to hold unique values for all rows. Defaults to false.

JoinColumn also has the same nullable , insertable, updatable, columnDefinition, and table properties as the Column annotation. See Section 3, “ Column ” for details on these properties.

The join-column element represents a join column in XML. Its attributes mirror the above annotation's properties:

  • name

  • referenced-column-name

  • unique

  • nullable

  • insertable

  • updatable

  • column-definition

  • table

When there are multiple columns involved in the join, as when a LineItem references a Magazine in our model, the JoinColumns annotation allows you to specify an array of JoinColumn values. In XML, simply list multiple join-column elements.

Note

OpenJPA supports many non-standard joins. See Section 6, “ Non-Standard Joins ” in the Reference Guide for details.

Example 13.14.  Direct Relation Field Mapping

package org.mag;

@Table(name="AUTH")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    @OneToOne
    @JoinColumn(name="COVER_ID" referencedColumnName="ID")
    private Article coverArticle;

    @ManyToOne
    @JoinColumn(name="PUB_ID" referencedColumnName="CID")
    private Company publisher;

    ...
}

@Table(name="ART")
public class Article {

    @Id private long id;

    ...
}


package org.mag.pub;

@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    ...
}


package org.mag.subscribe;

public class Subscription {
    ...

    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {

        @ManyToOne
        @JoinColumns({
            @JoinColumn(name="MAG_ISBN" referencedColumnName="ISBN"),
            @JoinColumn(name="MAG_TITLE" referencedColumnName="TITLE")
        })
        private Magazine magazine;

        ...
    }
}

The same metadata expressed in XML form:

<entity class="org.mag.Magazine">
    <table name="MAG"/>
    <id-class="org.mag.Magazine.MagazineId"/>
    <attributes>
        <id name="isbn">
            <column length="9"/>
        </id>
        <id name="title"/>
        <one-to-one name="coverArticle">
            <join-column name="COVER_ID" referenced-column-name="ID"/>
        </one-to-one>
        <many-to-one name="publisher">
            <join-column name="PUB_IC" referenced-column-name="CID"/>
        </many-to-one>
        ...
    </attributes>
</entity>
<entity class="org.mag.Article">
    <table name="ART"/>
    <attributes>
        <id name="id"/>
        ...
    </attributes>
</entity>
<entity class="org.mag.pub.Company">
    <table name="COMP"/>
    <attributes>
        <id name="id">
            <column name="CID"/>
        </id>
        ...
    </attributes>
</entity>
<entity class="org.mag.subscribe.Subscription.LineItem">
    <table name="LINE_ITEM" schema="CNTRCT"/>
    <primary-key-join-column name="ID" referenced-column-name="PK"/>
    <attributes>
        <many-to-one name="magazine">
            <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
            <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
        </many-to-one>
        ...
    </attributes>
</entity>

When the entities in a one to one relation join on shared primary key values rather than separate foreign key columns, use the PrimaryKeyJoinColumn(s) annotation or primary-key-join-column elements in place of JoinColumn(s) / join-column elements.

8.5.  Join Table

A join table consists of two foreign keys. Each row of a join table associates two objects together. JPA uses join tables to represent collections of entity objects: one foreign key refers back to the collection's owner, and the other refers to a collection element.

one to many and many to many metadata field types can map to join tables. Several fields in our model use join table mappings, including Magazine.articles and Article.authors.

You define join tables with the JoinTable annotation. This annotation has the following properties:

  • String name: Table name. If not given, the name of the table defaults to the name of the owning entity's table, plus an underscore, plus the name of the related entity's table.

  • String catalog: Table catalog.

  • String schema: Table schema.

  • JoinColumn[] joinColumns: Array of JoinColumn showing how to associate join table records with the owning row in the primary table. This property mirrors the pkJoinColumns property of the SecondaryTable annotation in functionality. See Section 8.2, “ Secondary Tables ” to refresh your memory on secondary tables.

    If this is a bidirectional relation (see Section 2.10.1, “ Bidirectional Relations ” ), the name of a join column defaults to the inverse field name, plus an underscore, plus the referenced primary key column name. Otherwise, the join column name defaults to the field's owning entity name, plus an underscore, plus the referenced primary key column name.

  • JoinColumn[] inverseJoinColumns: Array of JoinColumns showing how to associate join table records with the records that form the elements of the collection. These join columns are used just like the join columns for direct relations, and they have the same naming defaults. Read Section 8.4, “ Direct Relations ” for a review of direct relation mapping.

join-table is the corresponding XML element. It has the same attributes as the table element, but includes the ability to nest join-column and inverse-join-column elements as children. We have seen join-column elements already; inverse-join-column elements have the same attributes.

Here are the join table mappings for the diagram above.

Example 13.15.  Join Table Mapping

package org.mag;

@Entity
@Table(name="MAG")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    @OneToMany(...)
    @OrderBy
    @JoinTable(name="MAG_ARTS",
        joinColumns={
            @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
            @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
        },
        inverseJoinColumns=@JoinColumn(name="ART_ID", referencedColumnName="ID"))
    private Collection<Article> articles;

    ...
}

@Entity
@Table(name="ART")
public class Article {

    @Id private long id;

    @ManyToMany(cascade=CascadeType.PERSIST)
    @OrderBy("lastName, firstName")
    @JoinTable(name="ART_AUTHS",
        joinColumns=@JoinColumn(name="ART_ID", referencedColumnName="ID"),
        inverseJoinColumns=@JoinColumn(name="AUTH_ID", referencedColumnName="AID"))
    private Collection<Author> authors;

    ...
}


package org.mag.pub;

@Entity
@Table(name="AUTH")
public class Author {

    @Column(name="AID", columnDefinition="INTEGER64")
    @Id private long id;

    ...
}

The same metadata expressed in XML:

<entity class="org.mag.Magazine">
    <table name="MAG"/>
    <attributes>
        <id name="isbn">
            <column length="9"/>
        </id>
        <id name="title"/>
        <one-to-many name="articles">
            <order-by/>
            <join-table name="MAG_ARTS">
                <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
                <inverse-join-column name="ART_ID" referenced-column-name="ID"/>
            </join-table>
        </one-to-many>
        ...
    </attributes>
</entity>
<entity class="org.mag.Article">
    <table name="ART"/>
        <attributes>
            <id name="id"/>
            <many-to-many name="authors">
                <order-by>lastName, firstName</order-by>
                <join-table name="ART_AUTHS">
                    <join-column name="ART_ID" referenced-column-name="ID"/>
                    <inverse-join-column name="AUTH_ID" referenced-column-name="AID"/>
                </join-table>
            <cascade>
                <cascade-persist/>
            </cascade>
            </many-to-many>
            ...
       </attributes>
</entity>
<entity class="org.mag.pub.Author">
    <table name="AUTH"/>
    <attributes>
        <id name="id">
            <column name="AID" column-definition="INTEGER64"/>
        </id>
        ...
    </attributes>
</entity>

8.6.  Bidirectional Mapping

Section 2.10.1, “ Bidirectional Relations ” introduced bidirectional relations. To map a bidirectional relation, you map one field normally using the annotations we have covered throughout this chapter. Then you use the mappedBy property of the other field's metadata annotation or the corresponding mapped-by XML attribute to refer to the mapped field. Look for this pattern in these bidirectional relations as you peruse the complete mappings below:

  • Magazine.publisher and Company.mags.

  • Article.authors and Author.articles.

8.7.  Map Mapping

All map fields in JPA are modeled on either one to many or many to many associations. The map key is always derived from an associated entity's field. Thus map fields use the same mappings as any one to many or many to many fields, namely dedicated join tables or bidirectional relations. The only additions are the MapKey annotation and map-key element to declare the key field. We covered these additions in Section 2.14, “ Map Key ”.

The example below maps Subscription's map of LineItems to the SUB_ITEMS join table. The key for each map entry is the LineItem's num field value.

Example 13.16.  Join Table Map Mapping

package org.mag.subscribe;

@Entity
@Table(name="SUB", schema="CNTRCT")
public class Subscription {

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @MapKey(name="num")
    @JoinTable(name="SUB_ITEMS", schema="CNTRCT",
        joinColumns=@JoinColumn(name="SUB_ID"),
        inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
    private Map<Long,LineItem> items;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {

        private long num;

        ...
    }
}

The same metadata expressed in XML:

<entity class="org.mag.subscribe.Subscription">
    <table name="SUB" schema="CNTRCT"/>
    <attributes>
        ...
        <one-to-many name="items">
            <map-key name="num">
            <join-table name="SUB_ITEMS" schema="CNTRCT">
                <join-column name="SUB_ID"/>
                <inverse-join-column name="ITEM_ID"/>
            </join-table>
            <cascade>
                <cascade-persist/>
                <cascade-remove/>
            </cascade>
        </one-to-many>
        ...
    </attributes>
</entity>
<entity class="org.mag.subscribe.Subscription.LineItem">
    <table name="LINE_ITEM" schema="CNTRCT"/>
    <attributes>
        ...
        <basic name="num"/>
        ...
    </attributes>
</entity>

9.  The Complete Mappings

We began this chapter with the goal of mapping the following object model:

That goal has now been met. In the course of explaining JPA's object-relational mapping metadata, we slowly built the requisite schema and mappings for the complete model. First, the database schema:

And finally, the complete entity mappings. We have trimmed the mappings to take advantage of JPA defaults where possible.

Example 13.17.  Full Entity Mappings

package org.mag;

@Entity
@IdClass(Magazine.MagazineId.class)
@Table(name="MAG")
@DiscriminatorValue("Mag")
public class Magazine {

    @Column(length=9)
    @Id private String isbn;
    @Id private String title;

    @Column(name="VERS")
    @Version private int version;

    private String name;
    private double price;

    @Column(name="COPIES")
    private int copiesSold;

    @OneToOne(fetch=FetchType.LAZY,
        cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name="COVER_ID")
    private Article coverArticle;

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @OrderBy
    @JoinTable(name="MAG_ARTS",
        joinColumns={
            @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
            @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
        },
        inverseJoinColumns=@JoinColumn(name="ART_ID"))
    private Collection<Article> articles;

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
    @JoinColumn(name="PUB_ID")
    private Company publisher;

    @Transient private byte[] data;


    ...

    public static class MagazineId {
        ...
    }
}

@Entity
@Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
@SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
    private long id;

    @Column(name="VERS")
    @Version private int version;

    private String title;
    private byte[] content;

    @ManyToMany(cascade=CascadeType.PERSIST)
    @OrderBy("lastName, firstName")
    @JoinTable(name="ART_AUTHS",
        joinColumns=@JoinColumn(name="ART_ID"),
        inverseJoinColumns=@JoinColumn(name="AUTH_ID"))
    private Collection<Author> authors;

    ...
}


package org.mag.pub;

@Entity
@Table(name="COMP")
public class Company {

    @Column(name="CID")
    @Id private long id;

    @Column(name="VERS")
    @Version private int version;

    private String name;

    @Column(name="REV")
    private double revenue;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="street", column=@Column(name="STRT")),
        @AttributeOverride(name="city", column=@Column(name="ACITY"))
    })
    private Address address;

    @OneToMany(mappedBy="publisher", cascade=CascadeType.PERSIST)
    private Collection<Magazine> mags;

    @OneToMany(cascade=CascadeType.PERSIST,CascadeType.REMOVE)
    @JoinTable(name="COMP_SUBS",
        joinColumns=@JoinColumn(name="COMP_ID"),
        inverseJoinColumns=@JoinColumn(name="SUB_ID"))
    private Collection<Subscription> subscriptions;

    ...
}

@Entity
@Table(name="AUTH")
public class Author {

    @Id
    @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
    @TableGenerator(name="AuthorGen", tableName="AUTH_GEN", pkColumnName="PK",
        valueColumnName="AID")
    @Column(name="AID", columnDefinition="INTEGER64")
    private long id;

    @Column(name="VERS")
    @Version private int version;

    @Column(name="FNAME")
    private String firstName;

    @Column(name="LNAME")
    private String lastName;

    private Address address;

    @ManyToMany(mappedBy="authors", cascade=CascadeType.PERSIST)
    private Collection<Article> arts;

    ...
}

@Embeddable
public class Address {

    private String street;
    private String city;
    @Column(columnDefinition="CHAR(2)")
    private String state;
    private String zip;
}


package org.mag.subscribe;

@MappedSuperclass
public abstract class Document {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(name="VERS")
    @Version private int version;

    ...
}

@Entity
@Table(schema="CNTRCT")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="CTYPE")
public class Contract
    extends Document {

    @Lob
    private String terms;

    ...
}

@Entity
@Table(name="SUB", schema="CNTRCT")
@DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER)
@DiscriminatorValue("1")
public class Subscription {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;

    @Column(name="VERS")
    @Version private int version;

    @Column(name="START")
    private Date startDate;

    @Column(name="PAY")
    private double payment;

    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @MapKey(name="num")
    @JoinTable(name="SUB_ITEMS", schema="CNTRCT",
        joinColumns=@JoinColumn(name="SUB_ID"),
        inverseJoinColumns=@JoinColumn(name="ITEM_ID"))
    private Map<Long,LineItem> items;

    ...

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    public static class LineItem
        extends Contract {

        @Column(name="COMM")
        private String comments;

        private double price;
        private long num;

        @ManyToOne
        @JoinColumns({
             @JoinColumn(name="MAG_ISBN", referencedColumnName="ISBN"),
             @JoinColumn(name="MAG_TITLE", referencedColumnName="TITLE")
        })
        private Magazine magazine;
        ...
    }
}

@Entity(name="Lifetime")
@DiscriminatorValue("2")
public class LifetimeSubscription
    extends Subscription {

    @Basic(fetch=FetchType.LAZY)
    @Column(name="ELITE")
    private boolean getEliteClub() { ... }
    public void setEliteClub(boolean elite) { ... }

    ...
}

@Entity(name="Trial")
@DiscriminatorValue("3")
public class TrialSubscription
    extends Subscription {

    @Column(name="END")
    public Date getEndDate() { ... }
    public void setEndDate(Date end) { ... }

    ...
}

The same metadata expressed in XML form:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
    version="1.0">
    <mapped-superclass class="org.mag.subscribe.Document">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            <version name="version">
                <column name="VERS"/>
            </version>
        </attributes>
    </mapped-superclass>
    <entity class="org.mag.Magazine">
        <table name="MAG"/>
        <id-class="org.mag.Magazine.MagazineId"/>
        <discriminator-value>Mag</discriminator-value>
        <attributes>
            <id name="isbn">
                <column length="9"/>
            </id>
            <id name="title"/>
            <basic name="name"/>
            <basic name="price"/>
            <basic name="copiesSold">
                <column name="COPIES"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            <many-to-one name="publisher" fetch="LAZY">
                <join-column name="PUB_ID"/>
                <cascade>
                    <cascade-persist/>
                </cascade>
            </many-to-one>
            <one-to-many name="articles">
                <order-by/>
                <join-table name="MAG_ARTS">
                    <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                    <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
                    <inverse-join-column name="ART_ID"/>
                </join-table>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
            <one-to-one name="coverArticle" fetch="LAZY">
                <join-column name="COVER_ID"/>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-one>
            <transient name="data"/>
        </attributes>
    </entity>
    <entity class="org.mag.Article">
        <table name="ART">
            <unique-constraint>
               <column-name>TITLE</column-name>
            </unique-constraint>
        </table>
        <sequence-generator name="ArticleSeq", sequenceName="ART_SEQ"/>
        <attributes>
            <id name="id">
                <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
            </id>
            <basic name="title"/>
            <basic name="content"/>
            <version name="version">
                <column name="VERS"/>
            </version>
            <many-to-many name="articles">
                <order-by>lastName, firstName</order-by>
                <join-table name="ART_AUTHS">
                    <join-column name="ART_ID" referenced-column-name="ID"/>
                    <inverse-join-column name="AUTH_ID" referenced-column-name="AID"/>
                </join-table>
            </many-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Company">
        <table name="COMP"/>
        <attributes>
            <id name="id">
                <column name="CID"/>
            </id>
            <basic name="name"/>
            <basic name="revenue">
                <column name="REV"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            <one-to-many name="mags" mapped-by="publisher">
                <cascade>
                    <cascade-persist/>
                </cascade>
            </one-to-many>
            <one-to-many name="subscriptions">
                <join-table name="COMP_SUBS">
                    <join-column name="COMP_ID"/>
                    <inverse-join-column name="SUB_ID"/>
                </join-table>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
            <embedded name="address">
                <attribute-override name="street">
                   <column name="STRT"/>
                </attribute-override>
                <attribute-override name="city">
                   <column name="ACITY"/>
                </attribute-override>
            </embedded>
        </attributes>
    </entity>
    <entity class="org.mag.pub.Author">
        <table name="AUTH"/>
        <attributes>
            <id name="id">
                <column name="AID" column-definition="INTEGER64"/>
                <generated-value strategy="TABLE" generator="AuthorGen"/>
                <table-generator name="AuthorGen" table="AUTH_GEN"
                    pk-column-name="PK" value-column-name="AID"/>
            </id>
            <basic name="firstName">
                <column name="FNAME"/>
            </basic>
            <basic name="lastName">
                <column name="LNAME"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            <many-to-many name="arts" mapped-by="authors">
                <cascade>
                    <cascade-persist/>
                </cascade>
            </many-to-many>
            <embedded name="address"/>
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Contract">
        <table schema="CNTRCT"/>
        <inheritance strategy="JOINED"/>
        <discriminator-column name="CTYPE"/>
        <attributes>
            <basic name="terms">
                <lob/>
            </basic>
        </attributes>
    </entity>
    <entity class="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <inheritance strategy="SINGLE_TABLE"/>
        <discriminator-value>1</discriminator-value>
        <discriminator-column name="KIND" discriminator-type="INTEGER"/>
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
            <basic name="payment">
                <column name="PAY"/>
            </basic>
            <basic name="startDate">
                <column name="START"/>
            </basic>
            <version name="version">
                <column name="VERS"/>
            </version>
            <one-to-many name="items">
                <map-key name="num">
                <join-table name="SUB_ITEMS" schema="CNTRCT">
                    <join-column name="SUB_ID"/>
                    <inverse-join-column name="ITEM_ID"/>
                </join-table>
                <cascade>
                    <cascade-persist/>
                    <cascade-remove/>
                </cascade>
            </one-to-many>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.Subscription.LineItem">
        <table name="LINE_ITEM" schema="CNTRCT"/>
        <attributes>
            <basic name="comments">
                <column name="COMM"/>
            </basic>
            <basic name="price"/>
            <basic name="num"/>
            <many-to-one name="magazine">
                <join-column name="MAG_ISBN" referenced-column-name="ISBN"/>
                <join-column name="MAG_TITLE" referenced-column-name="TITLE"/>
            </many-to-one>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
        <discriminator-value>2</discriminator-value>
        <attributes>
            <basic name="eliteClub" fetch="LAZY">
                <column name="ELITE"/>
            </basic>
        </attributes>
    </entity>
    <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
        <discriminator-value>3</discriminator-value>
        <attributes>
            <basic name="endDate">
                <column name="END"/>
            </basic>
        </attributes>
    </entity>
    <embeddable class="org.mag.pub.Address">
        <attributes>
            <basic name="street"/>
            <basic name="city"/>
            <basic name="state">
                <column column-definition="CHAR(2)"/>
            </basic>
            <basic name="zip"/>
        </attributes>
    </embeddable>
</entity-mappings>

Chapter 14.  Conclusion

This concludes our overview of the JPA specification. The OpenJPA Reference Guide contains detailed documentation on all aspects of the OpenJPA implementation and core development tools.

Part 3. Reference Guide

Table of Contents

1. Introduction
1. Intended Audience
2. Configuration
1. Introduction
2. Runtime Configuration
3. Command Line Configuration
3.1. Code Formatting
4. Plugin Configuration
5. OpenJPA Properties
5.1. openjpa.AutoClear
5.2. openjpa.AutoDetach
5.3. openjpa.BrokerFactory
5.4. openjpa.BrokerImpl
5.5. openjpa.Callbacks
5.6. openjpa.ClassResolver
5.7. openjpa.Compatibility
5.8. openjpa.ConnectionDriverName
5.9. openjpa.Connection2DriverName
5.10. openjpa.ConnectionFactory
5.11. openjpa.ConnectionFactory2
5.12. openjpa.ConnectionFactoryName
5.13. openjpa.ConnectionFactory2Name
5.14. openjpa.ConnectionFactoryMode
5.15. openjpa.ConnectionFactoryProperties
5.16. openjpa.ConnectionFactory2Properties
5.17. openjpa.ConnectionPassword
5.18. openjpa.Connection2Password
5.19. openjpa.ConnectionProperties
5.20. openjpa.Connection2Properties
5.21. openjpa.ConnectionURL
5.22. openjpa.Connection2URL
5.23. openjpa.ConnectionUserName
5.24. openjpa.Connection2UserName
5.25. openjpa.ConnectionRetainMode
5.26. openjpa.DataCache
5.27. openjpa.DataCacheManager
5.28. openjpa.DataCacheMode
5.29. openjpa.DataCacheTimeout
5.30. openjpa.DetachState
5.31. openjpa.DynamicDataStructs
5.32. openjpa.DynamicEnhancementAgent
5.33. openjpa.FetchBatchSize
5.34. openjpa.EncryptionProvider
5.35. openjpa.FetchGroups
5.36. openjpa.FlushBeforeQueries
5.37. openjpa.IgnoreChanges
5.38. openjpa.Id
5.39. openjpa.InitializeEagerly
5.40. openjpa.Instrumentation
5.41. openjpa.InverseManager
5.42. openjpa.LockManager
5.43. openjpa.LockTimeout
5.44. openjpa.Log
5.45. openjpa.ManagedRuntime
5.46. openjpa.Mapping
5.47. openjpa.MaxFetchDepth
5.48. openjpa.MetaDataFactory
5.49. openjpa.MetaDataRepository
5.50. openjpa.Multithreaded
5.51. openjpa.Optimistic
5.52. openjpa.OptimizeIdCopy
5.53. openjpa.OrphanedKeyAction
5.54. openjpa.NontransactionalRead
5.55. openjpa.NontransactionalWrite
5.56. openjpa.ProxyManager
5.57. openjpa.PostLoadOnMerge
5.58. openjpa.QueryCache
5.59. openjpa.QueryCompilationCache
5.60. openjpa.ReadLockLevel
5.61. openjpa.RemoteCommitProvider
5.62. openjpa.RestoreState
5.63. openjpa.RetainState
5.64. openjpa.RetryClassRegistration
5.65. openjpa.RuntimeUnenhancedClasses
5.66. openjpa.SavepointManager
5.67. openjpa.Sequence
5.68. openjpa.Specification
5.69. openjpa.TransactionMode
5.70. openjpa.UseTCCLinSelectNew
5.71. openjpa.WriteLockLevel
6. OpenJPA JDBC Properties
6.1. openjpa.jdbc.ConnectionDecorators
6.2. openjpa.jdbc.DBDictionary
6.3. openjpa.jdbc.DriverDataSource
6.4. openjpa.jdbc.EagerFetchMode
6.5. openjpa.jdbc.FetchDirection
6.6. openjpa.jdbc.JDBCListeners
6.7. openjpa.jdbc.LRSSize
6.8. openjpa.jdbc.MappingDefaults
6.9. openjpa.jdbc.MappingFactory
6.10. openjpa.jdbc.QuerySQLCache
6.11. openjpa.jdbc.ResultSetType
6.12. openjpa.jdbc.Schema
6.13. openjpa.jdbc.SchemaFactory
6.14. openjpa.jdbc.Schemas
6.15. openjpa.jdbc.SQLFactory
6.16. openjpa.jdbc.SubclassFetchMode
6.17. openjpa.jdbc.SynchronizeMappings
6.18. openjpa.jdbc.TransactionIsolation
6.19. openjpa.jdbc.UpdateManager
6.20. Compatibility with Specification
3. Logging and Auditing
1. Logging Channels
2. OpenJPA Logging
3. Disabling Logging
4. Log4J
5. Apache Commons Logging
5.1. JDK java.util.logging
6. SLF4J
7. Custom Log
8. OpenJPA Audit
8.1. Configuration
8.2. Developing custom auditing
4. JDBC
1. Using the OpenJPA DataSource
1.1. Optional Connection Pooling
1.2. Configuring the OpenJPA DataSource
1.3. Configuring Apache Commons DBCP
2. Using a Third-Party DataSource
2.1. Managed and XA DataSources
2.2. Setting the DataSource at runtime
2.2.1. Using different DataSources for each EntityManager
2.2.1.1. Benefits
2.2.1.2. Limitations
2.2.1.3. Error handling
3. Runtime Access to DataSource
4. Database Support
4.1. DBDictionary Properties
4.2. FirebirdDictionary Properties
4.3. MySQLDictionary Properties
4.4. OracleDictionary Properties
4.5. SybaseDictionary Properties
4.6. DB2 Properties
4.7. Delimited Identifiers Support
5. Setting the Transaction Isolation
6. Setting the SQL Join Syntax
7. Accessing Multiple Databases
8. Configuring the Use of JDBC Connections
9. Statement Batching
10. Large Result Sets
11. Default Schema
12. Schema Reflection
12.1. Schemas List
12.2. Schema Factory
13. Schema Tool
14. XML Schema Format
5. Persistent Classes
1. Persistent Class List
2. Enhancement
2.1. Enhancing at Build Time
2.2. Enhancing JPA Entities on Deployment
2.3. Enhancing at Runtime
2.4. Enhancing Dynamically at Runtime
2.5. Omitting the OpenJPA enhancer
3. Managed Interfaces
4. Object Identity
4.1. Datastore Identity
4.2. Entities as Identity Fields
4.3. Application Identity Tool
4.4. Autoassign / Identity Strategy Caveats
5. Managed Inverses
6. Persistent Fields
6.1. Restoring State
6.2. Typing and Ordering
6.3. Calendar Fields and TimeZones
6.4. Proxies
6.4.1. Smart Proxies
6.4.2. Large Result Set Proxies
6.4.3. Custom Proxies
6.4.4. Serialization
6.5. Externalization
6.5.1. External Values
7. Fetch Groups
7.1. Custom Fetch Groups
7.2. Custom Fetch Group Configuration
7.3. Per-field Fetch Configuration
7.4. Implementation Notes
8. Eager Fetching
8.1. Configuring Eager Fetching
8.2. Eager Fetching Considerations and Limitations
6. Metadata
1. Metadata Factory
2. Metadata Repository
3. Additional JPA Metadata
3.1. Datastore Identity
3.2. Surrogate Version
3.3. Persistent Field Values
3.4. Persistent Collection Fields
3.5. Persistent Map Fields
4. Metadata Extensions
4.1. Class Extensions
4.1.1. Fetch Groups
4.1.2. Data Cache
4.1.3. Detached State
4.2. Field Extensions
4.2.1. Dependent
4.2.2. Load Fetch Group
4.2.3. LRS
4.2.4. Inverse-Logical
4.2.5. Read-Only
4.2.6. Type
4.2.7. Externalizer
4.2.8. Factory
4.2.9. External Values
4.3. Example
4.4. XML extensions
7. Mapping
1. Forward Mapping
1.1. Using the Mapping Tool
1.2. Generating DDL SQL
1.3. Runtime Forward Mapping
2. Reverse Mapping
2.1. Customizing Reverse Mapping
3. Meet-in-the-Middle Mapping
4. Mapping Defaults
5. Mapping Factory
6. Non-Standard Joins
7. Additional JPA Mappings
7.1. Datastore Identity Mapping
7.2. Surrogate Version Mapping
7.3. Multi-Column Mappings
7.4. Join Column Attribute Targets
7.5. Embedded Mapping
7.6. Collections
7.6.1. Container Table
7.6.2. Element Join Columns
7.6.3. Order Column
7.7. One-Sided One-Many Mapping
7.8. Maps
7.8.1. Key Columns
7.8.2. Key Join Columns
7.8.3. Key Embedded Mapping
7.8.4. Examples
7.9. Indexes and Constraints
7.9.1. Indexes
7.9.2. Foreign Keys
7.9.3. Unique Constraints
7.10. XML Column Mapping
7.11. LOB Streaming
8. Mapping Limitations
8.1. Table Per Class
9. Mapping Extensions
9.1. Class Extensions
9.1.1. Subclass Fetch Mode
9.1.2. Strategy
9.1.3. Discriminator Strategy
9.1.4. Version Strategy
9.2. Field Extensions
9.2.1. Eager Fetch Mode
9.2.2. Nonpolymorphic
9.2.3. Class Criteria
9.2.4. Strategy
10. Custom Mappings
10.1. Custom Class Mapping
10.2. Custom Discriminator and Version Strategies
10.3. Custom Field Mapping
10.3.1. Value Handlers
10.3.2. Field Strategies
10.3.3. Configuration
11. Orphaned Keys
8. Deployment
1. Factory Deployment
1.1. Standalone Deployment
1.2. EntityManager Injection
2. Integrating with the Transaction Manager
3. XA Transactions
3.1. Using OpenJPA with XA Transactions
9. Runtime Extensions
1. Architecture
1.1. Broker Finalization
1.2. Broker Customization and Eviction
2. JPA Extensions
2.1. OpenJPAEntityManagerFactory
2.2. OpenJPAEntityManager
2.3. OpenJPAQuery
2.4. Extent
2.5. StoreCache
2.6. QueryResultCache
2.7. FetchPlan
2.8. OpenJPAEntityTransaction
2.9. OpenJPAPersistence
3. Object Locking
3.1. Configuring Default Locking
3.2. Configuring Lock Levels at Runtime
3.3. Object Locking APIs
3.4. Lock Manager
3.5. Rules for Locking Behavior
3.6. Known Issues and Limitations
4. Savepoints
4.1. Using Savepoints
4.2. Configuring Savepoints
5. MethodQL
6. Generators
6.1. Runtime Access
7. Transaction Events
8. Non-Relational Stores
10. Caching
1. Data Cache
1.1. Data Cache Configuration
1.1.1. openjpa.DataCache Configuration
1.1.2. Integration with JPA standard shared cache mode
1.1.3. Distributing instances across cache partitions
1.2. Data Cache Usage
1.2.1. Using the JPA standard Cache interface
1.2.2. Using the OpenJPA StoreCache extensions
1.3. Cache Statistics
1.4. Query Cache
1.5. Cache Extension
1.6. Important Notes
1.7. Known Issues and Limitations
2. Query Compilation Cache
3. Prepared SQL Cache
11. Encryption Provider
12. Remote and Offline Operation
1. Detach and Attach
1.1. Detach Behavior
1.2. Attach Behavior
1.3. Defining the Detached Object Graph
1.3.1. Detached State
1.3.2. Detached State Field
2. Remote Event Notification Framework
2.1. Remote Commit Provider Configuration
2.1.1. JMS
2.1.2. TCP
2.1.3. Common Properties
2.2. Customization
13. Slice: Distributed Persistence
1. Overview
2. Salient Features
2.1. Transparency
2.2. Scaling
2.3. Distributed Query
2.4. Data Distribution
2.5. Data Replication
2.6. Heterogeneous Database
2.7. Distributed Transaction
2.8. Collocation Constraint
3. Usage
3.1. How to activate Slice Runtime?
3.2. How to configure each database slice?
3.3. Implement DistributionPolicy interface
3.4. Implement ReplicationPolicy interface
4. Configuration Properties
4.1. Global Properties
4.1.1. openjpa.slice.DistributionPolicy
4.1.2. openjpa.slice.Lenient
4.1.3. openjpa.slice.Master
4.1.4. openjpa.slice.Names
4.1.5. openjpa.slice.ThreadingPolicy
4.1.6. openjpa.slice.TransactionPolicy
4.2. Per-Slice Properties
14. Third Party Integration
1. Apache Ant
1.1. Common Ant Configuration Options
1.2. Enhancer Ant Task
1.3. Application Identity Tool Ant Task
1.4. Mapping Tool Ant Task
1.5. Reverse Mapping Tool Ant Task
1.6. Schema Tool Ant Task
2. Apache Commons DBCP
2.1. Apache Commons DBCP Configuration Options
15. Optimization Guidelines
16. Instrumentation
1. Configuration
1.1. JMX Platform MBean Enablement
2. Custom Providers and Instruments

Chapter 1.  Introduction

Table of Contents

1. Intended Audience

OpenJPA is a JDBC-based implementation of the JPA standard. This document is a reference for the configuration and use of OpenJPA.

1.  Intended Audience

This document is intended for OpenJPA developers. It assumes strong knowledge of Java, familiarity with the eXtensible Markup Language (XML), and an understanding of JPA. If you are not familiar with JPA, please read the JPA Overview before proceeding.

Certain sections of this guide cover advanced topics such as custom object-relational mapping, enterprise integration, and using OpenJPA with third-party tools. These sections assume prior experience with the relevant subject.

Chapter 2.  Configuration

Table of Contents

1. Introduction
2. Runtime Configuration
3. Command Line Configuration
3.1. Code Formatting
4. Plugin Configuration
5. OpenJPA Properties
5.1. openjpa.AutoClear
5.2. openjpa.AutoDetach
5.3. openjpa.BrokerFactory
5.4. openjpa.BrokerImpl
5.5. openjpa.Callbacks
5.6. openjpa.ClassResolver
5.7. openjpa.Compatibility
5.8. openjpa.ConnectionDriverName
5.9. openjpa.Connection2DriverName
5.10. openjpa.ConnectionFactory
5.11. openjpa.ConnectionFactory2
5.12. openjpa.ConnectionFactoryName
5.13. openjpa.ConnectionFactory2Name
5.14. openjpa.ConnectionFactoryMode
5.15. openjpa.ConnectionFactoryProperties
5.16. openjpa.ConnectionFactory2Properties
5.17. openjpa.ConnectionPassword
5.18. openjpa.Connection2Password
5.19. openjpa.ConnectionProperties
5.20. openjpa.Connection2Properties
5.21. openjpa.ConnectionURL
5.22. openjpa.Connection2URL
5.23. openjpa.ConnectionUserName
5.24. openjpa.Connection2UserName
5.25. openjpa.ConnectionRetainMode
5.26. openjpa.DataCache
5.27. openjpa.DataCacheManager
5.28. openjpa.DataCacheMode
5.29. openjpa.DataCacheTimeout
5.30. openjpa.DetachState
5.31. openjpa.DynamicDataStructs
5.32. openjpa.DynamicEnhancementAgent
5.33. openjpa.FetchBatchSize
5.34. openjpa.EncryptionProvider
5.35. openjpa.FetchGroups
5.36. openjpa.FlushBeforeQueries
5.37. openjpa.IgnoreChanges
5.38. openjpa.Id
5.39. openjpa.InitializeEagerly
5.40. openjpa.Instrumentation
5.41. openjpa.InverseManager
5.42. openjpa.LockManager
5.43. openjpa.LockTimeout
5.44. openjpa.Log
5.45. openjpa.ManagedRuntime
5.46. openjpa.Mapping
5.47. openjpa.MaxFetchDepth
5.48. openjpa.MetaDataFactory
5.49. openjpa.MetaDataRepository
5.50. openjpa.Multithreaded
5.51. openjpa.Optimistic
5.52. openjpa.OptimizeIdCopy
5.53. openjpa.OrphanedKeyAction
5.54. openjpa.NontransactionalRead
5.55. openjpa.NontransactionalWrite
5.56. openjpa.ProxyManager
5.57. openjpa.PostLoadOnMerge
5.58. openjpa.QueryCache
5.59. openjpa.QueryCompilationCache
5.60. openjpa.ReadLockLevel
5.61. openjpa.RemoteCommitProvider
5.62. openjpa.RestoreState
5.63. openjpa.RetainState
5.64. openjpa.RetryClassRegistration
5.65. openjpa.RuntimeUnenhancedClasses
5.66. openjpa.SavepointManager
5.67. openjpa.Sequence
5.68. openjpa.Specification
5.69. openjpa.TransactionMode
5.70. openjpa.UseTCCLinSelectNew
5.71. openjpa.WriteLockLevel
6. OpenJPA JDBC Properties
6.1. openjpa.jdbc.ConnectionDecorators
6.2. openjpa.jdbc.DBDictionary
6.3. openjpa.jdbc.DriverDataSource
6.4. openjpa.jdbc.EagerFetchMode
6.5. openjpa.jdbc.FetchDirection
6.6. openjpa.jdbc.JDBCListeners
6.7. openjpa.jdbc.LRSSize
6.8. openjpa.jdbc.MappingDefaults
6.9. openjpa.jdbc.MappingFactory
6.10. openjpa.jdbc.QuerySQLCache
6.11. openjpa.jdbc.ResultSetType
6.12. openjpa.jdbc.Schema
6.13. openjpa.jdbc.SchemaFactory
6.14. openjpa.jdbc.Schemas
6.15. openjpa.jdbc.SQLFactory
6.16. openjpa.jdbc.SubclassFetchMode
6.17. openjpa.jdbc.SynchronizeMappings
6.18. openjpa.jdbc.TransactionIsolation
6.19. openjpa.jdbc.UpdateManager
6.20. Compatibility with Specification

1.  Introduction

This chapter describes the OpenJPA configuration framework. It concludes with descriptions of all the configuration properties recognized by OpenJPA. You may want to browse these properties now, but it is not necessary. Most of them will be referenced later in the documentation as we explain the various features they apply to.

2.  Runtime Configuration

The OpenJPA runtime includes a comprehensive system of configuration defaults and overrides:

  • OpenJPA first looks for an optional openjpa.xml resource. OpenJPA searches for this resource in each top-level directory of your CLASSPATH. OpenJPA will also find the resource if you place it within a META-INF directory in any top-level directory of the CLASSPATH. The openjpa.xml resource contains property settings in JPA's XML format.

  • You can customize the name or location of the above resource by specifying the correct resource path in the openjpa.properties System property.

  • You can override any value defined in the above resource by setting the System property of the same name to the desired value.

  • In JPA, the values in the standard META-INF/persistence.xml bootstrapping file used by the Persistence class at runtime override the values in the above resource, as well as any System property settings. The Map passed to Persistence.createEntityManagerFactory at runtime also overrides previous settings, including properties defined in persistence.xml.

  • In JPA, The Map passed to the methods defined in the Query and EntityManager interfaces at runtime also overrides previous settings, including properties defined in persistence.xml. The Map is in effect only during the method invocation.

  • When using JCA deployment the config-property values in your ra.xml file override other settings.

  • All OpenJPA command-line tools accept flags that allow you to specify the configuration resource to use, and to override any property. Section 3, “ Command Line Configuration ” describes these flags.

Note

Internally, the OpenJPA runtime environment and development tools manipulate property settings through a general Configuration interface, and in particular its OpenJPAConfiguration and JDBCConfiguration subclasses. For advanced customization, OpenJPA's extended runtime interfaces and its development tools allow you to access these interfaces directly. See the Javadoc for details.

3.  Command Line Configuration

OpenJPA development tools share the same set of configuration defaults and overrides as the runtime system. They also allow you to specify property values on the command line:

  • -properties/-p <configuration file or resource>: Use the -properties flag, or its shorter -p form, to specify a configuration file to use. Note that OpenJPA always searches the default file locations described above, so this flag is only needed when you do not have a default resource in place, or when you wish to override the defaults. The given value can be the path to a file, or the resource name of a file somewhere in the CLASSPATH. OpenJPA will search the given location as well as the location prefixed by META-INF/ . Thus, to point an OpenJPA tool at META-INF/my-persistence.xml, you can use:

    <tool> -p my-persistence.xml
    

    If you want to run a tool against just one particular persistence unit in a configuration file, you can do so by specifying an anchor along with the resource. If you do not specify an anchor, the tools will run against all the persistence units defined within the specified resource, or the default resource if none is specified. If the persistence unit is defined within the default resource location, then you can just specify the raw anchor itself:

    <tool> -p my-persistence.xml#sales-persistence-unit
    <tool> -p #invoice-persistence-unit
    
  • -<property name> <property value>: Any configuration property that you can specify in a configuration file can be overridden with a command line flag. The flag name is always the last token of the corresponding property name, with the first letter in either upper or lower case. For example, to override the openjpa.ConnectionUserName property, you could pass the -connectionUserName <value> flag to any tool. Values set this way override both the values in the configuration file and values set via System properties.

3.1.  Code Formatting

Some OpenJPA development tools generate Java code. These tools share a common set of command-line flags for formatting their output to match your coding style. All code formatting flags can begin with either the codeFormat or cf prefix.

  • -codeFormat./-cf.tabSpaces <spaces>: The number of spaces that make up a tab, or 0 to use tab characters. Defaults to using tab characters.

  • -codeFormat./-cf.spaceBeforeParen <true/t | false/f>: Whether or not to place a space before opening parentheses on method calls, if statements, loops, etc. Defaults to false.

  • -codeFormat./-cf.spaceInParen <true/t | false/f>: Whether or not to place a space within parentheses; i.e. method( arg) . Defaults to false.

  • -codeFormat./-cf.braceOnSameLine <true/t | false/f>: Whether or not to place opening braces on the same line as the declaration that begins the code block, or on the next line. Defaults to true .

  • -codeFormat./-cf.braceAtSameTabLevel <true/t | false/f> : When the braceOnSameLine option is disabled, you can choose whether to place the brace at the same tab level of the contained code. Defaults to false.

  • -codeFormat./-cf.scoreBeforeFieldName <true/t | false/f> : Whether to prefix an underscore to names of private member variables. Defaults to false.

  • -codeFormat./-cf.linesBetweenSections <lines>: The number of lines to skip between sections of code. Defaults to 1.

Example 2.1.  Code Formatting with the Application Id Tool

java org.apache.openjpa.enhance.ApplicationIdTool -cf.spaceBeforeParen true -cf.tabSpaces 4

4.  Plugin Configuration

Because OpenJPA is a highly customizable environment, many configuration properties relate to the creation and configuration of system plugins. Plugin properties have a syntax very similar to that of Java annotations. They allow you to specify both what class to use for the plugin and how to configure the public fields or bean properties of the instantiated plugin instance. The easiest way to describe the plugin syntax is by example:

OpenJPA has a pluggable L2 caching mechanism that is controlled by the openjpa.DataCache configuration property. Suppose that you have created a new class, com.xyz.MyDataCache, that you want OpenJPA to use for caching. You've made instances of MyDataCache configurable via two methods, setCacheSize(int size) and setRemoteHost(String host). The sample below shows how you would tell OpenJPA to use an instance of your custom plugin with a max size of 1000 and a remote host of cacheserver .

<property name="openjpa.DataCache"
    value="com.xyz.MyDataCache(CacheSize=1000, RemoteHost=cacheserver)"/>

As you can see, plugin properties take a class name, followed by a comma-separated list of values for the plugin's public fields or bean properties in parentheses. OpenJPA will match each named property to a field or setter method in the instantiated plugin instance, and set the field or invoke the method with the given value (after converting the value to the right type, of course). The first letter of the property names can be in either upper or lower case. The following would also have been valid:

com.xyz.MyDataCache(cacheSize=1000, remoteHost=cacheserver)

If you do not need to pass any property settings to a plugin, you can just name the class to use:

com.xyz.MyDataCache

Similarly, if the plugin has a default class that you do not want to change, you can simply specify a list of property settings, without a class name. For example, OpenJPA's query cache companion to the data cache has a default implementation suitable to most users, but you still might want to change the query cache's size. It has a CacheSize property for this purpose:

CacheSize=1000

Finally, many of OpenJPA's built-in options for plugins have short alias names that you can use in place of the full class name. The data cache property, for example, has an available alias of true for the standard cache implementation. The property value simply becomes:

true

The standard cache implementation class also has a CacheSize property, so to use the standard implementation and configure the size, specify:

true(CacheSize=1000)

The remainder of this chapter reviews the set of configuration properties OpenJPA recognizes.

5.  OpenJPA Properties

5.1. openjpa.AutoClear
5.2. openjpa.AutoDetach
5.3. openjpa.BrokerFactory
5.4. openjpa.BrokerImpl
5.5. openjpa.Callbacks
5.6. openjpa.ClassResolver
5.7. openjpa.Compatibility
5.8. openjpa.ConnectionDriverName
5.9. openjpa.Connection2DriverName
5.10. openjpa.ConnectionFactory
5.11. openjpa.ConnectionFactory2
5.12. openjpa.ConnectionFactoryName
5.13. openjpa.ConnectionFactory2Name
5.14. openjpa.ConnectionFactoryMode
5.15. openjpa.ConnectionFactoryProperties
5.16. openjpa.ConnectionFactory2Properties
5.17. openjpa.ConnectionPassword
5.18. openjpa.Connection2Password
5.19. openjpa.ConnectionProperties
5.20. openjpa.Connection2Properties
5.21. openjpa.ConnectionURL
5.22. openjpa.Connection2URL
5.23. openjpa.ConnectionUserName
5.24. openjpa.Connection2UserName
5.25. openjpa.ConnectionRetainMode
5.26. openjpa.DataCache
5.27. openjpa.DataCacheManager
5.28. openjpa.DataCacheMode
5.29. openjpa.DataCacheTimeout
5.30. openjpa.DetachState
5.31. openjpa.DynamicDataStructs
5.32. openjpa.DynamicEnhancementAgent
5.33. openjpa.FetchBatchSize
5.34. openjpa.EncryptionProvider
5.35. openjpa.FetchGroups
5.36. openjpa.FlushBeforeQueries
5.37. openjpa.IgnoreChanges
5.38. openjpa.Id
5.39. openjpa.InitializeEagerly
5.40. openjpa.Instrumentation
5.41. openjpa.InverseManager
5.42. openjpa.LockManager
5.43. openjpa.LockTimeout
5.44. openjpa.Log
5.45. openjpa.ManagedRuntime
5.46. openjpa.Mapping
5.47. openjpa.MaxFetchDepth
5.48. openjpa.MetaDataFactory
5.49. openjpa.MetaDataRepository
5.50. openjpa.Multithreaded
5.51. openjpa.Optimistic
5.52. openjpa.OptimizeIdCopy
5.53. openjpa.OrphanedKeyAction
5.54. openjpa.NontransactionalRead
5.55. openjpa.NontransactionalWrite
5.56. openjpa.ProxyManager
5.57. openjpa.PostLoadOnMerge
5.58. openjpa.QueryCache
5.59. openjpa.QueryCompilationCache
5.60. openjpa.ReadLockLevel
5.61. openjpa.RemoteCommitProvider
5.62. openjpa.RestoreState
5.63. openjpa.RetainState
5.64. openjpa.RetryClassRegistration
5.65. openjpa.RuntimeUnenhancedClasses
5.66. openjpa.SavepointManager
5.67. openjpa.Sequence
5.68. openjpa.Specification
5.69. openjpa.TransactionMode
5.70. openjpa.UseTCCLinSelectNew
5.71. openjpa.WriteLockLevel

OpenJPA defines many configuration properties. Most of these properties are provided for advanced users who wish to customize OpenJPA's behavior; the majority of developers can omit them. The following properties apply to any OpenJPA back-end, though the given descriptions are tailored to OpenJPA's default JDBC store.

A few of the properties recognized by OpenJPA have been standardized in JPA 2.0 specification using equivalent names. These properties can be specified either by the JPA standard key or equivalent OpenJPA key. Specifying the same key once as JPA standard key and again as equivalent OpenJPA key in the same configuration, however, is not allowed. The following table lists these standard JPA properties and their OpenJPA equivalent.

Table 2.1.  Standard JPA Properties and OpenJPA equivalents

Standard JPA 2.0OpenJPA Equivalent
javax.persistence.jdbc.driveropenjpa.ConnectionDriverName
javax.persistence.jdbc.urlopenjpa.ConnectionURL
javax.persistence.jdbc.useropenjpa.ConnectionUserName
javax.persistence.jdbc.passwordopenjpa.ConnectionPassword

5.1.  openjpa.AutoClear

Property name: openjpa.AutoClear

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getAutoClear

Resource adaptor config-property: AutoClear

Default: datastore

Possible values: datastore, all

Description: When to automatically clear instance state: on entering a datastore transaction, or on entering any transaction.

5.2.  openjpa.AutoDetach

Property name: openjpa.AutoDetach

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getAutoDetach

Resource adaptor config-property: AutoDetach

Default: - null

Possible values: close, commit, nontx-read, rollback, none

Description: A comma-separated list of events when managed instances will be automatically detached. When using the OpenJPA EntityManager this defaults to close, and rollback per the JPA spec. If you need to change this setting, you need to set it directly on an instantiated EntityManager.

none option is exclusive. It can not be specified with any other option. none option implies that managed objects will not be detached from the persistence context, the second-class object fields such as collections or date will not be proxied unlike normal circumstances. This option is relevant for specific use cases where the user application would not refer to the managed objects after the transaction and/or the context ends e.g. typical batch insertion scenario.

5.3.  openjpa.BrokerFactory

Property name: openjpa.BrokerFactory

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getBrokerFactory

Resource adaptor config-property: BrokerFactory

Default: jdbc

Possible values: jdbc, abstractstore, remote

Description: A plugin string (see Section 4, “ Plugin Configuration ”) describing the org.apache.openjpa.kernel.BrokerFactory type to use.

5.4.  openjpa.BrokerImpl

Property name: openjpa.BrokerImpl

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getBrokerImpl

Resource adaptor config-property: BrokerImpl

Default: default

Description: A plugin string (see Section 4, “ Plugin Configuration ”) describing the org.apache.openjpa.kernel.Broker type to use at runtime. See Section 1.2, “ Broker Customization and Eviction ” on for details.

5.5. openjpa.Callbacks

Property name: openjpa.Callbacks

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getCallbackOptionsInstance

Resource adaptor config-property: Callbacks

Default: default

Description: A plugin string (see Section 4, “ Plugin Configuration ”) to fine tune some of the configurable properties related to callbacks. The plug-in supports two boolean properties:

  • PostPersistCallbackImmediate: whether the post-persist callback is invoked as soon as a new instance is managed. The default is false, implies that the post-persist callback is invoked after the instance been committed or flushed to the datastore.

  • AllowsMultipleMethodsForSameCallback: whether multiple methods of the same class can handle the same callback event. Defaults to false.

5.6.  openjpa.ClassResolver

Property name: openjpa.ClassResolver

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getClassResolver

Resource adaptor config-property: ClassResolver

Default: default

Description: A plugin string (see Section 4, “ Plugin Configuration ”) describing the org.apache.openjpa.util.ClassResolver implementation to use for class name resolution. You may wish to plug in your own resolver if you have special classloading needs.

5.7.  openjpa.Compatibility

Property name: openjpa.Compatibility

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getCompatibility

Resource adaptor config-property: Compatibility

Default: -

Description: Encapsulates options to mimic the behavior of previous OpenJPA releases.

5.8.  openjpa.ConnectionDriverName

Property name: openjpa.ConnectionDriverName

Configuration API: org.apache.openjpa.conf.OpenJPAConfiguration.getConnectionDriverName

Resource adaptor config-property: