Jpa Online Final

3,488 views
3,400 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,488
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
167
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Jpa Online Final

  1. 1. Subscribe Now for FREE! refcardz.com tech facts at your fingertips CONTENTS INCLUDE: n Getting Started With JPA Getting Started with JPA n Mapping an Object n Obtaining and Using an Entity Manager n Transactions n Querying n Hot Tips and more... By Mike Keith Listing 1 – Pet entity class GETTING STarTED WITh JPa @Entity @Table(name=quot;PET_INFOquot;) The Java Persistence API (JPA) is the Java standard for mapping public class Pet { Java objects to a relational database. Even though proprietary @Id mapping products like Hibernate and TopLink still exist, they @Column(name=quot;IDquot;) are now focused on providing their functionality through int licenseNumber; String name; the JPA API, allowing all applications to be portable across PetType type; JPA implementations. This refcard will give users enough @ManyToOne to understand the basics of JPA and get started writing JPA @JoinColumn(name=quot;OWNER_IDquot;) applications. It covers entities, identifiers, O-R mappings, Owner owner; using an entity manager, creating and executing queries, and ... configuration of the persistence.xml file. } Listing 2 - Owner entity class @Entity MaPPING aN ObJECT public class Owner { @Id The basic unit of persistence in JPA is the entity, which is www.dzone.com int id; nothing more than a regular Java class with metadata to String name; describe how its state maps to the database tables. Metadata @Column(name=quot;PHONE_NUMquot;) may be in the form of annotations on the entity class itself, or it String phoneNumber; may be an accompanying XML file, but we are using annotations @OneToOne Address address; since they are easier to specify and understand. @OneToMany(mappedBy=quot;ownerquot;) List<Pet> pets; ... Hot When used together, XML mappings can over- } Tip ride the values specified in annotations In a bidirectional relationship pair, such as the @OneToMany relationship in Owner to Pet and the @ManyToOne relationship Every entity class should have an @Entity marker and an back from Pet to Owner, only one foreign key is required in one identifier field, indicated by @Id, that is mapped to the primary of the tables to manage both sides of the relationship. As a key column in the database. When a field contains simple general rule, the side that does not have the foreign key in it data and maps to a regular column in the database we call it a specifies a mappedBy attribute in the relationship annotation and basic mapping, thus an identifier field is a special kind of basic specifies the field in the related entity that maps the foreign key. mapping. When an entity has a field that references one or more → other entities, that field maps to a foreign key column, and is Getting Started with JPa called a relationship field. Other than the identifier field, basic mappings do not need to be annotated, but relationships must Get More Refcardz be specified by their relationship cardinality. (They’re free!) Defaulting rules in JPA mean that you are not required to specify n Authoritative content table names and column names that an entity is mapped to. If you are not happy with the JPA-assigned defaults then you can n Designed for developers always override them through the use of additional mapping n Written by top experts metadata. For example, by putting @Table on the entity class n Latest tools & technologies you can make the table name explicit, and by annotating a n Hot tips & examples basic mapping field with @Column you can define the particular n Bonus content online column that maps the state in that field. Likewise @JoinColumn n New issue every 1-2 weeks is used to override the name of the foreign key column for relationship references. Subscribe Now for FREE! An example of two mapped entities are the Pet and Owner Refcardz.com classes shown in Listings 1 and 2. DZone, Inc. | www.dzone.com
  2. 2. 2 Getting Started with JPa tech facts at your fingertips Mapping an Object, continued USING aN ENTITy MaNaGEr The possible mapping annotations that can be used are: @Basic @Enumerated @ManyToOne @Temporal The basic purpose of an entity manager is to perform create/ @Embedded @Lob @OneToMany @Transient read/update/delete (CRUD) operations on entities. Listing 5 @EmbeddedId @ManyToMany @OneToOne shows methods that perform these operations. Listing 5 – Invoking the entity manager Annotations used to override the names of the tables or public Pet createPet(int idNum, String name, PetType columns in the database are: type) { @AttributeOverride(s) @PrimaryKeyJoinColumn(s) Pet pet = new Pet(idNum, name, type); @AssociationOverride(s) @SecondaryTable(s) em.persist(pet); @Column @SequenceGenerator return pet; @DiscriminatorColumn @Table } @JoinColumn(s) @TableGenerator public Pet findPet(int id) { @JoinTable return em.find(Pet.class, id); } Other annotations used to indicate the type of the class or public Pet changeName(int id, String newName) { other aspects of the model are: Pet pet = this.findPet(id); pet.setName(newName); @Entity @IdClass @MappedSuperclass return pet; @Embeddable @Inheritance @OrderBy } @GeneratedValue @DiscriminatorValue @Version public void deletePet(int id) { @Id @MapKey Pet pet = this.findPet(id); em.remove(pet); } ObTaINING aN ENTITy MaNaGEr Note that finding the pet is the first step to being able to perform update and delete operations on it. Also, an update The EntityManager class is the main API in JPA. It is used does not even involve invoking the entity manager, but requires to create new entities, manufacture queries to return sets reading the pet, loading it into the entity manager and then of existing entities, merge in the state of remotely modified modifying it. The modification will be reflected in the database entities, delete entities from the database, and more. when the transaction is committed. There are, generally speaking, two main kinds of entity managers: The merge() method can also be used to create container-managed The managed entity managers may only be obtained Hot entities, but is most useful for merging in entity within a container that supports the JPA Service Tip Provider Interface (SPI). changes made on the client side. non-managed Non-managed entity managers may be obtained in any environment where a JPA provider is on the classpath. Listing 3 shows an example of obtaining a non-managed entity manager by first obtaining TraNSaCTIONS an EntityManagerFactory instance from the Persistence root class. Since we just mentioned transactions, but didn’t explain them, Listing 3 – Obtaining a non-managed entity manager now would be a good time to state that JPA supports two different kinds of transactions. import javax.persistence.*; ... JTA container transactions Used when running in container mode EntityManagerFactory emf = Persistence resource local transactions Typically used when running in non-container .createEntityManagerFactory(quot;PetShopquot;); mode. EntityManager em = emf.createEntityManager(); ... JTA transactions are started and committed using the usual em.close(); container techniques, either calling the UserTransaction API or making use of container-managed transaction demarcation In Listing 4 we see how a standard host container can provide a in EJB or Spring. For example, if the methods in Listing 5 were simpler way to obtain an entity manager. The only catch is that in a session bean that had a Required transaction attribute this is only supported within standard Java EE components (or containers that are compliant to the JPA container contract), so setting then a transaction would be started at the beginning and committed at the end of each client method invocation. this example uses a stateless session bean. When using local transactions the transaction must be Listing 4 – Injecting a managed entity manager demarcated manually by invoking on the EntityTransaction @Stateless instance accessed from the entity manager. Each of the three public class MyBean implements MyInterface { @PersistenceContext(unitName=quot;PetShopquot;) methods in Listing 5 that caused the database to change would EntityManager em; need to have begin and commit calls, as shown in Listing 6 for ... the persist method. Methods that only read from the database } do not need to occur within a transaction. DZone, Inc. | www.dzone.com
  3. 3. 3 Getting Started with JPa tech facts at your fingertips Transactions, continued Querying, continued Listing 6 – Using EntityTransaction configuration method calls may be made on the query instance public Pet createPet(int idNum, String name, PetType to configure it. Listing 7 shows an example of creating and type) { executing a query that returns all the instances of Pet, or the em.getTransaction().begin(); first 100 if there are more than 100 instances. Pet pet = new Pet(idNum, name, type); em.persist(pet); Listing 7 – Creating and executing a dynamic query em.getTransaction().commit(); return pet; Query q = em.createQuery(quot;SELECT p FROM Pet pquot;); } q.setMaxResults(100); List results = q.getResultList(); The complete EntityManager API is listed in Table 1, with a A named query is a query that is defined statically and then brief description of what each method does. instantiated and executed at runtime. It can be defined as an Method Description annotation on the entity class, and assigned a name that is used when the query is created. Listing 8 shows a named query void persist(Object entity) Persist an entity defined on the Pet entity. <T> T merge(T entity); Merge the state of an entity into the database Listing 8 – Defining a named query void remove(Object entity); Remove an entity from the database @NamedQuery(name=quot;Pet.findByNamequot;, <T> T find( Find and return an instance of an entity class query=quot;SELECT p FROM Pet p WHERE p.name LIKE :pnamequot;) Class<T> entityClass, @Entity Object primaryKey); public class Pet { <T> T getReference( Create a holder for the primary key of an ... Class<T> entityClass, entity } Object primaryKey); void flush(); Cause all changes to be written out to the The last identifier is prefixed with a colon (:) character to indicate database that it is a named parameter that must be bound at runtime void setFlushMode( Set the flushing mode for query execution before the query can be executed. Listing 9 shows a method FlushModeType flushMode); that executes the query by first instantiating a Query object FlushModeType getFlushMode(); Get the flushing mode for query execution using the createNamedQuery() factory method, then binding void lock( Lock an object to obtain greater isolation Object entity, consistency guarantees the pname named parameter to the name that was passed LockModeType lockMode); into the method, and finally executing the query by invoking void refresh(Object entity); Update the in-memory entity with the state getResultList(). from the database Listing 9 – Executing a named query void clear(); Make all managed entities become unmanaged public List findAllPetsByName(String petName) { boolean contains( Determine if an entity is managed Query q = em.createNamedQuery(quot;Pet.findByNamequot;); Object entity); q.setParameter(quot;pnamequot;, petName); Query createQuery( Create a dynamic query from JP QL return q.getResultList(); String JP QLString); } Query createNamedQuery( Create a named query String name); Named queries are not only more efficient than Query createNativeQuery( Create a query from SQL Hot dynamic queries but are also safer since they String sqlString); Tip Query createNativeQuery( Create a query from SQL that returns a given will often get pre-compiled by the persistence String sqlString, Class entityClass); entity type implementation at deployment time Query createNativeQuery( Create a query from SQL that uses a given String sqlString, defined mapping The entire Query API is shown in Table 2. String resultSetMapping); void joinTransaction(); Join an existing JTA transaction Query Method Description List getResultList(); Execute the query and return the results Object getDelegate(); Access the underlying EntityManager as a List implementation Object getSingleResult(); Execute a query that returns a single result void close(); Close the EntityManager int executeUpdate(); Execute an update or delete statement boolean isOpen(); Determine whether the EntityManager has been closed Query setMaxResults( Set the maximum number of results to int maxResult); retrieve EntityTransaction Access the EntityManager local transaction getTransaction(); Query setFirstResult( Set the position of the first result to int startPosition); retrieve Table 1. EntityManager method summary Query setHint( Set an implementation-specific query hint String hintName, Object value); QUEryING Query setParameter( String name, Bind an argument to a named parameter Object value); Query setParameter( Bind an instance of java.util.Date to a Dynamic queries are objects that are created from an entity String name, named parameter manager, and then executed. The query criteria are specified Date value, TemporalType temporalType); at creation time as a Java Persistence Query Language (JP QL) string. Before executing the query a number of possible Table 2. Query method summary DZone, Inc. | www.dzone.com
  4. 4. 4 Getting Started with JPa tech facts at your fingertips Querying, continued Java Persistence Query Language, continued Query Method Description Clause/Term Syntax Query setParameter( Bind an instance of java.util.Calendar to a select_clause SELECT [DISTINCT] select_exp {,select_exp}* String name, named parameter Calendar value, TemporalType temporalType); select_exp variable | state_field_exp | single_rel_exp | aggregate_exp | constructor_exp Query setParameter( Bind a parameter by position int position, aggregate_exp {{AVG | MAX | MIN | SUM} ( [DISTINCT] state_field_exp )} | Object value); COUNT ( [DISTINCT] variable | state_field_exp | single_rel_ Query setParameter( Bind an instance of java.util.Date to a exp ) int position, positional parameter Date value, TemporalType constructor_exp NEW constructor_method ( constructor_item {,constructor_ temporalType); item}* ) Query setParameter( Bind an instance of java.util.Calendar to a constructor_item single_rel_exp | aggregate_exp int position, positional parameter Calendar value, TemporalType from_clause FROM variable_decl {, {variable_decl | in_decl}}* temporalType); Query setFlushMode( Set the flush mode for the query variable_decl entityName [AS] variable {join_exp | fetch_join_exp}* FlushModeType flushMode); join_exp [LEFT [OUTER] | INNER] JOIN rel_field [AS] variable Table 2. Query method summary, continued fetch_join_exp [LEFT [OUTER] | INNER] JOIN FETCH rel_field in_decl IN ( multi_rel_exp ) [AS] variable Java PErSISTENCE QUEry LaNGUaGE where_clause WHERE conditional_exp The Java Persistence Query Language is SQL-like, but operates conditional_exp {[NOT] conditional} | {conditional_exp {AND | OR} over the entities and their mapped persistent attributes instead conditional_exp} of the SQL schema. Many of the SQL functions and even conditional comparison | between_exp | like_exp | in_exp | compare_ null_exp | compare_empty_exp | compare_member_exp | reserved words are supported in JP QL. exists_exp There are three basic types of JP QL statements, of which the comparison compare_string | compare_boolean | compare_enum | compare_datetime | compare_entity | compare_arithmetic first is monstrously the most popular and useful: selects, bulk compare_string string_exp {= | > | >= | < | <= | <>} {string_exp | all_any_ updates and bulk deletes. subquery} 1. select_clause from_clause [where_clause] [groupby_clause] compare_boolean boolean_exp {= | <>} {boolean_exp | all_any_subquery} [having_clause] [orderby_clause] compare_enum enum_exp {= | <>} {enum_exp | all_any_subquery} 2. update_clause [where_clause] compare_datetime datetime_exp {= | > | >= | < | <= | <>} {datetime_exp | 3. delete_clause [where_clause] all_any_subquery} compare_entity entity_exp {= | <>} {entity_exp | all_any_subquery} Bulk deletes are useful for doing test clean-up compare_arithmetic arithmetic_exp {= | > | >= | < | <= | <>} {arithmetic_exp | Hot and clearing all of the data from the entity tables all_any_subquery} Tip all_any_subquery {ALL | ANY | SOME} ( subquery ) without having to revert to SQL. between_exp arithmetic_exp [NOT] BETWEEN arithmetic_exp AND arithmetic_exp A simplified table of most of the supported syntax is in like_exp string_exp [NOT] LIKE pattern_value [ESCAPE escape_char] Table 4. For complete and precise grammar, consult the JPA in_exp state_field_exp [NOT] IN ( {literal | input_param} {,{literal | specification at http://jcp.org/aboutJava/communityprocess/ input_param}}* ) final/jsr220/index.html. The primitive terms are shown in Table 3. compare_null_exp {single_rel_exp | input_param} IS [NOT] NULL Term Description compare_empty_exp multi_rel_exp IS [NOT] EMPTY entityName Name of an entity (which is defaulted to the name of the compare_member_exp entity_exp [NOT] MEMBER [OF] multi_rel_exp entity class) exists_exp [NOT] EXISTS ( subquery ) variable Identifier variable following Java identifier rules state_field_exp Term that resolves to an entity field containing simple arithmetic_exp arithmetic | ( subquery ) state (e.g. if Pet is represented by variable p, then p.name or p.owner.phoneNumber) string_exp string | ( subquery ) single_rel_exp Term that resolves to an entity field containing an one-to- entity_exp variable | input_param | single_rel_exp one or many-to-one relationship (e.g. if Pet is represented by variable p, then p.owner or p.owner.address) enum_exp enum | ( subquery ) multi_rel_exp Term that resolves to an entity field containing a one- datetime_ exp datetime | ( subquery ) to-many or many-to-many relationship (e.g. if Owner is represented by variable o, then o.pets) boolean_exp boolean | ( subquery ) rel_field Term composed of a variable and one of its relationship fields, with no traversing of intermediate relationships arithmetic arithmetic_term | {arithmetic { * | / | + | - } arithmetic} (e.g. if Pet is represented by variable p, then p.owner) arithmetic_term state_field_exp | literal | input_param | aggregate_exp | constructor_method Constructor for a non-entity class (i.e. the name of the numeric_function | ( arithmetic ) class) string state_field_exp | literal | input_param | aggregate_exp | input_param Variable that represents an input parameter and must be string_function bound before the query can be executed literal A value of a particular type such as a string or integer (e.g. enum state_field_exp | literal | input_param ‘Iggy Pop’, or 42) datetime state_field_exp | input_param | aggregate_exp | datetime_ pattern_value A valid SQL pattern string (e.g. “% Smith”) function escape_char A character to be escaped boolean state_field_exp | literal | input_param Table 3. Primitive terms for JP QL grammar Table 4. Simplified JP QL Grammar DZone, Inc. | www.dzone.com
  5. 5. 5 Getting Started with JPa tech facts at your fingertips Java Persistence Query Language, continued Configuration, continued Clause/Term Syntax for that configuration unit. In a non-managed environment string_function CONCAT ( string , string ) | the target database is typically specified through the use SUBSTRING ( string , arithmetic , arithmetic) | of vendor-specific properties that describe the JDBC driver TRIM ( [[{LEADING | TRAILING | BOTH}] [trim_char] FROM] and connection properties to use. Also, in non-managed string ) | LOWER ( string ) | environments the entity classes must be enumerated in class UPPER ( string ) elements, whereas in managed containers the entity classes datetime_function CURRENT_DATE | CURRENT_TIME | CURRENT_ will be automatically detected. Examples of container and non- TIMESTAMP container persistence unit elements are indicated in Listings 10 numeric_function LENGTH ( string ) | and 11, respectively. LOCATE ( string , string [ , arithmetic] ) | ABS ( arithmetic ) | Listing 10 – Container persistence unit configuration SQRT ( arithmetic ) | MOD ( arithmetic , arithmetic ) | <persistence-unit name=quot;PetShopquot;> SIZE ( multi_rel_ exp ) <jta-data-source>jdbc/PetShopDB</jta-data-source> </persistence-unit> subquery SELECT [DISTINCT] {variable | single_rel_exp | aggregate_ exp} FROM subquery_decl {, subquery_decl}* Listing 11 – Non-container persistence unit configuration [where_clause] <persistence-unit name=quot;PetShopquot;> subquery_decl variable_decl | {single_rel_exp [AS] variable} | in_decl <class>com.acme.petshop.Pet</class> ... update_clause UPDATE entityName [[AS] variable] SET update_item <class>com.acme.petshop.Owner</class> {,{update_item}}* <properties> update_item {state_field_exp | single_rel_exp} = new_value <property name=quot;eclipselink.jdbc.driverquot; new_value variable | input_param | arithmetic | string | boolean | datetime value=quot;oracle.jdbc.OracleDriverquot;/> | enum | NULL <property name=quot;eclipselink.jdbc.urlquot; delete_clause DELETE FROM entityName [[AS] variable] value=quot;jdbc:oracle:thin:@localhost:1521:XEquot;/> <property name=quot;eclipselink.jdbc.userquot; groupby_clause GROUP BY groupby_item {, groupby_item}* value=quot;scottquot;/> groupby_item single_rel_exp | variable <property name=quot;eclipselink.jdbc.passwordquot; value=quot;tigerquot;/> having_clause HAVING conditional_exp </properties> </persistence-unit> orderby_clause ORDER BY orderby_item {, orderby_item}* orderby_item state_field_exp [ASC | DESC] A provider implementation will be found by Table 4. Simplified JP QL Grammar, continued Hot default, so avoid using the provider element and Tip binding yourself to a specific provider unless you really are dependent upon that provider. JP QL queries can return data projections over Hot entity attributes, averting instantiation of the Tip A hierarchical view of the possible XML elements in a actual entity objects persistence.xml file are shown in Figure 1. All of the elements are optional and the starred elements may be pluralized. CONfIGUraTION persistence * Without counting the mappings from the entity to the database tables, there is really only one unit of JPA configuration needed persistence-unit to get your application up and running. It is based on the • name • transaction type notion of a persistence unit, and is configured in a file called persistence.xml, which must always be placed in the META- INF directory of your deployment unit. Each persistence unit is provider a configuration closure over the settings necessary to run in the jta-data-source relevant environment. The parent element in a persistence.xml file is the persistence element and may contain one or more non-jta-data-source persistence-unit elements representing different execution * mapping-file configurations. Each one must be named using the mandatory * jar-file persistence-unit name attribute. * class There are slightly different requirements for configuring the exclude-unlisted-classes * property persistence unit, depending upon whether you are deploying • name to a managed container environment or a non-managed one. In properties • value a managed container the target database is indicated through the jta-data-source element, which is the JNDI name for the managed data source describing where the entity state is stored Figure 1. XML elements in persistence.xml file DZone, Inc. | www.dzone.com
  6. 6. 6 Getting Started with JPa tech facts at your fingertips MOvING ON rESOUrCES As you may have noticed, using JPA is not that hard, and Resource Source you win the big prizes of portability and compatibility going Glassfish Persistence Page https://glassfish.dev.java.net/javaee5/persistence/entity- persistence-support.html forward. Hopefully you are now feeling ready to cut your teeth Oracle Technology Network http://www.oracle.com/technology/products/ias/toplink/ on a JPA application of your very own. The next step is to JPA resources jpa/index.html download the open source JPA 1.0 Reference Implementation Eclipse JPA (part of Eclipse http://www.eclipse.org/eclipselink (TopLink Essentials) and start it up. It is available at https:// Persistence Services Project) glassfish.dev.java.net/downloads/persistence/JavaPersistence. Pro EJB 3: Java Persistence By Mike Keith and Merrick Schincariol API Apress, 2006 html and is trivial to install and configure. Happy persisting! books.dzone.com/books/java-persistence abOUT ThE aUThOr rECOMMENDED bOOK Mike Keith Assuming a basic knowl- Mike Keith was a co-lead of the EJB 3.0 and JPA 1.0 specifications and co- edge of Java, SQL and authored the premier JPA reference book called Pro EJB 3: Java Persistence JDBC, this book will teach API. He has 18 years of teaching, research and development experience in you the Java Persistence object-oriented and distributed systems, specializing in object persistence. He API from the ground up. currently works as an architect for Java and persistence strategies at Oracle After reading it, you will and represents Oracle on the JPA 2.0 and Java EE 6 expert groups. He has have an in-depth under- authored a host of articles and papers and is a popular speaker at numerous conferences and standing of JPA and learn events around the world. many tips for using it in Publications Current projects: your applications. n Pro EJB 3: Java Persistence API, Mike Keith and Merrick n Committer on Eclipse Persistence Project Schincariol, Apress, May 2006 n Member of JSR 317–JPA 2.0 n ACM Queue, May/June 2008, Exposing the ORM Cache n Member of JSR 316–Java EE 6 n JavaLobby, June 2008, Looking Forward to JPA 2.0–Part 2 n Member of JSR 318–EJB 3.1 n JavaLobby, April 2008, Looking Forward to JPA 2.0–Part 1 n Member of Java EE subcommittee of bUy NOW OSGi Alliance EEG books.dzone.com/books/java-persistence Want More? Download Now. Subscribe at refcardz.com Upcoming Refcardz: Available: n Core CSS: Part II Published September 2008 Published June 2008 frEE n Ruby n Getting Started with JPA n jQuerySelectors n Core CSS: Part III n Core CSS: Part I n Design Patterns Struts 2 Flexible Rails: Flex 3 on Rails 2 SOA Patterns n n n Scalability and High Published August 2008 n n Very First Steps in Flex Published May 2008 n Agile Methodologies n C# n Windows PowerShell n Spring Annotations n Groovy n Dependency Injection in EJB 3 n PHP n Core .NET n Core Java Published July 2008 Visit http://refcardz.dzone.com n NetBeans IDE 6.1 Java Editor for a complete listing of n JUnit RSS and Atom available Refcardz. n n MySQL n GlassFish Application Server n Seam n Silverlight 2 Design Patterns n IntelliJ IDEA Published June 2008 DZone, Inc. 1251 NW Maynard ISBN-13: 978-1-934238-24-0 Cary, NC 27513 ISBN-10: 1-934238-24-4 50795 888.678.0399 DZone communities deliver over 3.5 million pages per month to 919.678.0300 more than 1.5 million software developers, architects and designers. Refcardz Feedback Welcome DZone offers something for every developer, including news, refcardz@dzone.com $7.95 tutorials, blogs, cheatsheets, feature articles, source code and more. Sponsorship Opportunities 9 781934 238240 “DZone is a developer’s dream,” says PC Magazine. sales@dzone.com Copyright © 2008 DZone, Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, Version 1.0 photocopying, or otherwise, without prior written permission of the publisher. Reference: Pro EJB 3: Java Persistence API, Michael Keith and Merrick Schincariol, Apress, May 2006.

×