Upload
stephan-janssen
View
4.574
Download
0
Tags:
Embed Size (px)
Citation preview
KICK START JPAAlexander Snaps – Axen
GOAL OF THE PRESENTATION
give a good overview of the Java Persistence API (JPA),while still addressing more advanced issues & common pitfalls
all you need to successfully start with JPA
So... You are still not using JavaEE 5 ?
Still believe JDBC is fun ?Still stuck with a RDBMS ?
try { Connection connection = DriverManager.getConnection("jdbc:derby:Console;create=true"); Statement statement = null; try { statement = connection.createStatement(); statement.execute("SELECT first_name, last_name FROM persons"); ResultSet resultSet = null; try { resultSet = statement.getResultSet(); while(resultSet.next()) { String fName = resultSet.getString("first_name"); System.out.println(resultSet.wasNull ? "(null)" : fName); } } catch (SQLException e) { // Handle exception thrown while retrieving the result } finally { if(resultSet != null) resultSet.close(); } } catch (SQLException e) { // Handle exception thrown while trying to get to the database } finally { if(statement != null) statement.close(); connection.close(); }} catch (SQLException e) { // Handle exception thrown while trying to get a connection}
• Eliminates the need for JDBC
• CRUD & Querying
• Object identity management
• Inheritance strategies
• Class hierarchy to single or multiple tables
• Associations, Composition
• Lazy navigation
• Fetching strategies
OBJECT RELATIONAL MAPPING
INTRODUCING JPA• Vendor independent ORM solution
• Easily configurable
• Configuration directly in code using Java 5 annotations
• Configuration fine tunable, overriding annotations using XML
• Available outside JavaEE containers
• Dedicated Java Specification RequestJSR 317 as of JPA 2.0
• One last thing on ORM
”The effective use of ORM technology in all but the simplest of enterprise environments requires
understanding and configuring how the mediation between relational data and objects is performed”
Linda DeMichiel Lead Architect EJB, Sun
It should all be ... transparent!:
MAPPINGS
• Transparent yet:
• Non-final class or methods
• Constructor with no argument
• Collections typed to interfaces
• Associations aren’t managed for you
• Database identifier field
JPA – BACK TO POJOS
SIMPLEST ENTITY EXAMPLE@Entitypublic class Person { @Id
private Long ssn; private String firstName;
private String lastName; protected Person() {}
public Person(Long ssn, String firstName, String lastName) {
...
}}
SIMPLE STILL, BUT VERBOSE@Entity(name = “Humans”)@Table(name = “persons”, schema = “hr”)public class Person {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Basic private Long ssn; private String firstName;
@Column(name = "NAME", unique = false, nullable = false, length = 255)
private String lastName;
protected Person() {}
public Person(Long ssn, String firstName, String lastName) {
...
}}
SIMPLE TYPES• Primitives & wrapper classes !
• java.lang.String !
• java.math.BigInteger & BigDecimal
• Byte & Character arrays
• Java & JDBC Temporal types
• Enumeration
• Serializable types
CLASS HIERARCHIES
• Entities support
• Inheritance
• polymorphic associations
• Concrete and abstract can be mapped
• @Entity
• @MappedSuperclass
POLYMORPHISM
• Three mapping strategies
• One table per class hierarchy
• Joined subclass
• One table per concrete class (optional)
MANY-TO-ONE ASSOCIATIONS
@Entitypublic class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long ssn; private String firstName; private String lastName;
private Company company; protected Person() {}
public Person(Long ssn, String firstName, String lastName) {
...
}}
@JoinColumn(name = “ID_COMP”) @ManyToOne
MANY-TO-ONE ASSOCIATIONS
Person
- id: Long
- ssn: Long
- firstName: String
- lastName: String
Company
- id: Long
- name: String1
FK1
ssn
firstname
lname
company_id
PK id
person
name
PK id
company
ONE-TO-ONE ASSOCIATIONS
@Entitypublic class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long ssn; private String firstName; private String lastName;
@OneToOne private AccessPass accessPass; protected Person() {}
public Person(Long ssn, String firstName, String lastName) {
...
}}
ONE-TO-ONE ASSOCIATIONS
Person
- id: Long
- ssn: Long
- firstName: String
- lastName: String
AccessPass
- id: Long
- validUntil: Date0..1
FK1
ssn
firstname
lname
access_id
PK id
person
until
PK id
access
ONE-TO-ONEBI-DIRECTIONAL
@Entitypublic class AccessPass { @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Temporal(TemporalType.DATE) private Date validUntil; @OneToOne(mappedBy=”accessPass”) private Person owner;
protected AccessPass() {}
public Person(Person person, Date validUntil) {
this.person = person; this.validUntil = (Date) validUntil.clone(); }}
ONE-TO-MANYBI-DIRECTIONAL
@Entitypublic class Person {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = ”resident”) private Set<Address> addresses = new HashSet<Address>();
protected Person() {} }
ONE-TO-MANYBI-DIRECTIONAL
@Entitypublic class Address {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; @ManyToOne @JoinColumn(name = “resident_id”) private Person resident;
public Address() {}
public void setResident(Person resident) { if(this.resident != resident) { if(this.resident != null) resident.removeAddress(this); this.resident = resident;
if(resident != null) resident.addAddress(this); } } }
ONE-TO-MANYBI-DIRECTIONAL
@Entitypublic class Person { ...
@OneToMany(mappedBy = ”resident”) private Set<Address> addresses = new HashSet<Address>();
public void addAddress(Address address) { if(!this.addresses.contains(address)) { this.addresses.add(address); address.setResident(this); } }
public void removeAddress(Address address) { if(this.addresses.contains(address)) { this.addresses.remove(address); address.setResident(null); } } }
ONE-TO-MANYUNI-DIRECTIONAL
• Same as the bi-directional only without the mappedBy
• Without a owning side with cardinality of one, a join table is required!
FK1
FK2
person_id
address_id
person_address
...
PK id
address
...
PK id
person
• With a unique constraint on FK2
• Many-to-Many is equal to One-to-Many uni-directional, but without the unique constraint
*-TO-MANY EXAMPLES
@Entitypublic class Person { ... @OneToMany private Set<Address> addresses = new HashSet<Address>();
@ManyToMany(cascade = CascadeType.ALL) private Set<Project> projects = new HashSet<Project>();
... @OneToMany(mappedBy = “owner”) @MapKey(name = “type”) private Map<String, PhoneNumber> phones = new HashMap<String, PhoneNumber>();}
USING JPA ENTITIES
MANAGING PERSISTENCE
• javax.persistence.Persistence creates an EntityManagerFactory based on a persistence unit name
• With the EntityManagerFactory instance,you create EntityManager instances
• EntityManager to handle the persistence of your entities
• EntityTransaction used for transaction demarcation
• Query to... query(!) them back from the database
SETTING UPTHE PERSISTENCE UNIT
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="myPU">
<jta-data-source> jdbc/myIncredibleDS </jta-data-source>
</persistence-unit>
</persistence>
SETTING UPTHE PERSISTENCE UNIT
<persistence-unit name="myPU" transaction-type="RESOURCE_LOCAL">
<provider> oracle.toplink.essentials.PersistenceProvider </provider> <class>some.domain.Class</class> <properties> <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/> <property name="toplink.jdbc.url" value="jdbc:derby:myData;create=true"/> <property name="toplink.ddl-generation" value="create-tables"/> </properties>
DEPENDENCY INJECTION@Statelesspublic class EmployeeDaoBean implements EmployeeDao {
@PersistenceContext(unitName = “myPU”) private EntityManager em;
public Person getEmployee(Long ssn) { return em.find(Person.class, ssn); }}
THE ENTITYMANAGER
• EntityManager.remove(Object): void
• EntityManager.find(Class<T>, Object): T
• EntityManager.persist(Object): void
• Manages CRUD operations
• Now how do I update ?
• You DO NOT!
• Manages object identity
ENTITY LIFE-CYCLE
THE LIFE-CYCLE• An entity can be described to be in four states
• Removed: the entity has a persistent identity & is associated to a persistence context, but scheduled for removal.
• Detached: the entity has a persistent identity, but is not (anymore) associated to a persistence context;
• Managed: the entity instance is associated to a persistence context;
• New: the entity instance is new and not yet associated to a persistence context;
PERSISTING AN ENTITY
• The operation EntityManager.persist(Object) will have a new entity instance to become managed;
• Other entities referenced by the entity, whose association are to be cascaded when persisting, will also be persisted;
• If already managed, the operation will be ignored, but might be cascaded to other entities referenced by it.
• Yet, this might not happen as you call persist on the entity!
REMOVING AN ENTITY
• You schedule the removal of an entity by calling EntityManager.remove(Object) operation
• If the entity is new or already scheduled for removal, the operation is ignored
• Again the operation might be cascaded
• Actual delete can be the result of the transaction committing or an explicit flush() of the persistence context
MANAGED ENTITIES• A managed entity will have its state automatically synchronized
with the database.
• This operation, called flushing, depends on the flush mode
• Auto, or• Commit
• In the FlushModeType.COMMIT, the entity’s state will only be synchronized when the transaction actually commits
• This is not the default behavior of a PersistenceContext
AUTO FLUSH MODE
• In the FlushModeType.AUTO, the entity’s state will still be synchronized to the database at the latest when the transaction commits or when ...
• A transaction is active and
• Either you flush with EntityManager.flush();
• or an operation requires the state to be synched.
SOME EXAMPLE
public void methodWithTxDemarcation() {
// loads Company named Apple Computers Company company = em.find(Company.class, "AAPL"); company.setName("Apple Inc.");
}
Query query = em.createQuery("select c from Companies"); List list = query.getResultList();
Company newCompany = new Company("Sun Microsystems", "JAVA"); em.persist(newCompany);
@PersistenceContextprivate EntityManager em;
@Stateful@TransactionAttribute(NOT_SUPPORTED)public class StatefulBean {
public void createTheSun() {
}
public List<Company> query() {
return list; }
@TransactionAttribute(REQUIRED)@Removepublic void done() {}
}
Query query = em.createQuery("select c from Companies"); List list = query.getResultList();
Company newCompany = new Company("Sun Microsystems", "JAVA"); em.persist(newCompany);
@PersistenceContextprivate EntityManager em;
(type = EXTENDED)
DETACHED ENTITIES• An entity is detached when
• its persistence context is closed;
• In a JavaEE environment, defaults when the transaction commits;
• In a JavaSE environment,you manage the persistence context’s life-cycle.
• the entity is serialized;
• an exception occurred !
MERGINGDETACH ENTITIES BACK
• You can merge an entity back with a persistent context using EntityManager.merge(Object): Object
• If entity is detached, a new managed instance of the entity is returned, with the detached copied into it;
• If the entity is new, a new managed entity is returned, after have the state copied into it;
• Again, cascading applies;
• If the entity is scheduled for removal, an Exception is thrown.
OPTIMISTIC LOCKING• To enable optimistic locking on an entity,
simply annotate an entity field with @Version
• The version attribute will be updated by the EntityManager every time the entity’s state is written to the database
• Field can be of type : int, Integer, short, Short, long, Long, Timestamp
• Apply it consistently to graphs
• Do not modify it...
• Throws OptimisticLockException
QUERIES & BULK OPERATIONS
JPA QUERY LANGUAGE• The Query API
• is used for
• named queries • dynamic queries
• supports
• polymorphism• named parameters• pagination
DYNAMIC QUERIES
em.createQuery( "SELECT c FROM Customer c WHERE c.name" + " LIKE :custName") .setParameter("custName", name) .setFirstResult(10) .setMaxResults(10) .getResultList();
STATIC QUERIES
@NamedQuery( name ="findAllCustomersWithName", query="SELECT c FROM Customer c WHERE c.name LIKE :custName")
BULK OPERATIONS• You can bulk update & delete operations
with the Query API
• Delete example:DELETE FROM Customer c WHERE c.status = ‘inactive’
• Update example:UPDATE customer c SET c.status = ‘outstanding’ WHERE c.balance < 10000 AND 1000 > (SELECT COUNT(o) FROM customer cust JOIN cust.order o)
BULK OPERATIONS
Caution !Bulk operation will not affect the EntityManager !!!
OBJECT IDENTITYVS.
DATABASE IDENTITY
OBJECT IDENTITY• How do you deal with object equality ?
• Introduce the database identifier as part of it ?
• Quick reminder from java.lang.Object#equals(Object)
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
OBJECT IDENTITY • So that if the database identifier is in it,
you always have to have it assigned before using it in a Collection for instance
Company company = new Company(“JoGoSlow”);
em.persist(company);
em.flush();
group.addCompany(company);
OBJECT IDENTITY• Other possible solutions
• Have real business key
• Have some sort of GUID / UUID
• Do not override equals(Object) nor hashCode()except if you real need to & know what you are doing !
• Yet that isn’t okay if your object is to be used as composite identifier
JPA IN A JAVA EE ENVIRONMENT
GETTING AN ENTITYMANAGER
• In JavaEE
• Using dependency injection:@PersistenceContext EntityManager em;
• Within a SFSB@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager orderEM;
• Getting the EntityManagerFactory@PersistenceUnit EntityManagerFactory emf;
EXTENDEDPERSISTENCE CONTEXT
• To be used within a Stateful SessionBean
• The persistence context will be created,when the SFSB is himself created
• And will be closed when the SFSB and all other SFSB that inherited the Persistence Context have been removed
• If the SFSB uses container-managed transaction demarcation, the persistence context will join the tx
JPA 2.0aka. JSR 317
MORE FLEXIBLE MODELING AND MAPPING
• Collections of basic types
• Improved support for embeddable classes
• Ordered lists
• Generalized maps
• Expanded relationship mapping options
• More flexible use of access types
CRITERIA APICriteria crit = new Criteria(User.class) .project(Name.class) .property("firstName") .property("lastName") .orderBy() .property("lastName").asc() .property("firstName").asc() .property("age").desc() .restrict() .property("name").eq("Gavin") .and() .property("age").gt(18) .fetch("dependents").leftJoin() .join("address") .restrict() .property("country").in("USA", "AUSTRALIA");
em.createQuery(crit).getResultList();
WELL ...
SUMMARY
• JPA makes dealing with RDBMS simpler
• ... once you’ve understood how it works!
• It is available in JavaEE and JavaSE
• Multiple vendors
• On going dedicated JSR with lots of improvement on its way
• Great tool support
CONCLUDING STATEMENTSo you think you want to forget about JDBC ?
The Java Persistence API looks cool ?It all can be yours today... and with great tooling !
& answers?Questions
THANKS FOR YOUR ATTENTION!