JavaJEEJPA

From Wiki RB4

Introduction[edit]

The Java Persistence API (JPA) originated as part of the work of the JSR 220 Expert Group. JPA 2.0 is the work of the JSR 317 Expert Group. The final release date of the JPA 1.0 specification was 11 May 2006. The JPA 2.0 specification was released 10 Dec, 2009. JPA is a replacement for the much criticized EJB 2.0 and EJB 2.1 entity beans.

The easiest way is to use annotations. To search for entities there is JPQL. The API defines the interface and is no persistence provider. This is for example JBoss or Glassfish.

Implementations[edit]

Hibernate Versions 3.2 and later provide an implementation for the Java Persistence API.

Entity Manager[edit]

The central instance of the JPA is the entity manager (persistence manager) (interface javax.persistence.EntityManager), which is provided by the persistence provider. Each EntityManager instance is associated with a persistence context: a set of managed entity instances that exist in a particular data store. A persistence context is more or less a cache of objects. The entitiy manager is similar to a persistence context.

The entity manager is responsible for

  • first persist of an object
  • merge of an object state with the state in the database, detached objects werden wieder vom entity manager verwaltet
  • remove an object from the database
  • find an enitity by primary key or query by JPQL
  • lock an entity
  • flush the persistence context
  • close the entity manager.

The entity manager is accessed via JNDI or in an EJB container by injection (javax.persistence.PeristenceContext).

EntityManagerFactory emf = Persistence.createEntityManagerFactory(<PersistenceUnitName>);
EntityManager em = emf.createEntityManager();
...
em.close();

Entities[edit]

An entity can have different states:

  • transient (created by new(), no associated row in database)
  • persistent (has an database identity, are associated with a persistence context)
  • removed (by remove())
  • detached (lived on over a transaction)

Entity Identity[edit]

Using an ORM introduces additional constraints on object identity. Defining the properties that make up an entity’s natural identity can be tricky, but is very important. Using the object’s identity, or the synthetic identity (database generated primary key) identity can introduce unexpected bugs into your application, so you should always ensure you use a natural identity. The identity is defined by @Id annotation. The type should not be native type, but a class, because a null value is by convention used for not stored objects. For instance it could be of type Long.

For composite keys you have three options:

  1. encapsulate the identifier properties in a separate class and mark it as @Embeddable. Include an attribute of this class in the entity class an mark it with @Id
  2. encapsulale the identifier properties in a separate class without annotation, include an attribute of this class in the entity class and mark it with @EmbeddedId
  3. @IdClass

Entity Implementation[edit]

with Spring Boot and Lombok:

  1. @Entity annotation at class level
  2. extend my BaseEntity class (documentation in java file)
  3. decide if field or property access (by placing the annotations at the fields or at the getters). This a philosophical discussion in the community, but field access seems to be the easiest and appropriate way.
  4. public or protected no-arg default ctor (e.g. Lombok @NoArgsConstructor)
  5. implements Serializable If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface
  6. if not using Lombok annotation implement hashCode() and equals() if possible on a business or natural key and not on a generated id. Best solution is a generated UUID (search the web)
  7. create DB table - iff you let the JPA provider create the DB tables for the entity there is a default mapping of Java types to DB columns. This could be a problem because for example the default mapping of String is VARCHAR(255), so strings are truncated to 255 chars. In most cases it is better to create the DB tables by a script. Topics to consider are:
    1. NOT NULL
    2. AUTOINCREMENT
    3. initial value
  8. attribute names are mapped to db columns
    1. no capital letters
    2. capital letters in a string a prefixed with a underscore e.g. targetDate => target_date, isDone => is_done

Default Mapping for Java types[edit]

  • Date -> DATETIME
  • Long -> BIGINT(20)
  • String -> VARCHAR(255)
  • @LOB String -> LONGTEXT

Manipulating Entities[edit]

Merge[edit]

Merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again):

  • Find an attached object with the same id and update it.
  • If exists update and return the already attached object.
  • If doesn't exist insert the new register to the database

Persist[edit]

Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates to the entity will be tracked):

  • Insert a new register to the database
  • Attach the object to the entity manager.

Remove[edit]

To remove an entity object from the database, make a call to EntityManager.remove(entityObject) method. The entity object that is passed to the remove() must be a managed entity, else the operation will fail.

Relationships[edit]

Multiplicities are of the following types: one-to-one, one-to-many, many-to-one, and many-to-many. The direction of a relationship can be either bidirectional or unidirectional. A bidirectional relationship can have an owning side and an inverse side, otherwise a mapping table will be created because it is treated as two unidirectional relationships (another explanation is that can be interpreted as a bag which is a bag. A bag is a collection which can have duplicates but is unordered).

  • The owning side of the relation tracked by Hibernate is the side of the relation that owns the foreign key in the database (this represents a foreign key on the n-side of the relationship, see here).
  • The inverse (non-owning) side must use the mappedBy element.

There is a god description here.

Bidirectional relationships must follow these rules.

  • The inverse side of a bidirectional relationship must refer to its owning side by using the mappedBy element of the @OneToOne, @OneToMany, or @ManyToOne annotation.
  • For many-to-many bidirectional relationships, either side may be the owning side.
  • On the one side there should be a addXXX() and removeXXX() method which calls the parent-setting of the added/removed XXX object
  • The implementation of the relationship setting is also depending on the cascade setting (see here).
  • If an ordered many relationship is needed the @OrderColumn annotation is needed.
  • the @JoinColumn is optional, if it is not there Hibernate uses a default naming strategy. In uni-directional relationships it seems that it avoids an additional association table.

Unidirectional is a relation where one side does not know about the relation. A unidirectional relationship has only an owning side. A unidirectional relationship is represented as a join table because the n-side could be referenced by multiple objects on the 1-side of the relationship.

Entity relationships often depend on the existence of another entity. When we delete a parent entity, the child entities should also get deleted. Cascading is the way to achieve this. When we perform some action on the target entity, the same action will be applied to the associated entity.

Orphan removal means that if an entity is deleted from a relationsship and orphan removal is true, than the orphan will be deleted.

One-To-Many, Many-To-One[edit]

  • the one side is sometimes called parent, the many side child or children
  • child table contains a foreign key to the primary key of the parent table, hence the many side is always the owning side of the relationship.
  • bidirectional @OneToMany needs a @ManyToOne association on the other side.
  • The many side of many-to-one bidirectional relationships must not define the mappedBy element.
  • You should always provide methods parent.addChild() and parent.removeChild() which are used to set the child site whenever you are working with a bidirectional association.
void addChild(child) {
  children.add(child);
  child.setparent(this);
}
  • It’s good practice to override equals and hashCode for the child entity in a bidirectional association.

Fetching[edit]

The default depends on the cardinality of the relationship. All to-one relationships use FetchType.EAGER and all to-many relationships FetchType.LAZY.

Persistance Unit[edit]

A persistence unit is the group of classes which is managed by one entity manager and references one database. It also indicates which transaction type is to be used when the database operations occur. The persistence unit consists of three main parts.

  • entity metadata
  • persistence unit descriptor
    • persistence provider
    • database connection
    • transaction type
  • persistence manager factory

A persistence unit is defined in persistence.xml.

Configuration[edit]

persistence.xml[edit]

  • in case of WAR file WEB-INF/classes/META-INF, in case of EAR file META-INF directory of the beans jar file
<persistence>
  <persistence-unit name="<Name>" [transaction-type= > // s. annotation PersistenceContext
    <jta-data-source>java:/<Name></jta-data-source> // points to a name of a data source configuration e.g. JBoss Database configuration or 
                                                    // to a file <Name>-ds.xml in the WEB-INF folder of an application
    <properties>
      <property name="<NAME>" value="<VALUE>" />
    </properties>
    <provider>
      <property> </property>
    </provider>
  </persistence-unit>
</persistence>

For hibernate properties start with hibernate.hbm2ddl.* which can be used to automatic create or update DB tables or to automatically import data (only if DB scheme is updated or created) from /WEB-INF/classes/import.sql (or a specific file given as property hibernate.hbm2ddl.import_files).

orm.xml[edit]

If annotations are not used then the object relational mapping is declared in META-INF/orm.xml.

Annotations[edit]

The location of the annotations e.g. before the attributes/fields or before the methods is important.

  1. Access to attributes: JPA allows for two types of access to the data of a persistent class, field/property access or method access. What access type it will be used is decided by where you put the @Id annotation (on the id field or the getId() method). Which type to use depends on: performance (reflection is faster?), logic inside the getter and/or setter, validation, independent refactoring, exception handling (see. Java Persistance with Hibernate, 3.25).
  2. Names of the db columns: Field access which means that it maps the instance variables (fields) to columns in the database and Property access which means that is uses the getters to determine the property names that will be mapped to the db.

javax.persistence.AttributesOverrides[edit]

@EmbeddedId
@AttributeOverrides( {
  @AttributeOverride(name = "kategorieId", column = @Column(name = "kategorie_id", nullable = false)),
  @AttributeOverride(name = "sprachId", column = @Column(name = "sprach_id", nullable = false)) })

Only make sense with the Embedded Annotations to override specifications for the embedded class.


javax.persistence.Basic[edit]

@javax.persistence.Basic([fetch=(FetchType.EAGER|FetchType.LAZY)])
<method declaration>

The Basic annotation maps the java type to a database type. It is standard and has to be used seldomly. It can be used to specify the fetch strategy. Normally all attributes are fetched while loading the entity. Lazy delays it to the first use.

javax.persistence.Column[edit]

@javax.persistence.Column([name="<Name>"], [length=<Number>])
<attribute declaration>

The length parameter is important for maximum string length.

javax.persistence.Entity[edit]

@javax.persistence.Entity[([name="<Name>"])]
  • if name is not specified the entity name e.g. used in JPA queries is the classname
  • if no table specified, tablename is classname
  • all members are persistent, unless speficied with javax.persistence.Transient

java.persistence.GeneratedValue[edit]

java.persistence.GeneratedValue([strategy=GenerationType.(AUTO|IDENTITY|SEQUENCE|TABLE)])
  • AUTO is the default. The implementation is depending of the type of the ID attribute. For nummeric it is a sequence or table generator.
  • strategy = GenerationType.IDENTITY means the IDs are not generated in Java code, but instead assigned by the underlying database. It can be used for MySQL database which have an auto increment procedure on id columns

javax.persistence.Id[edit]

Simple ids are mapped using @Id to a single property of one of these types: Java primitive and primitive wrapper types, String, Date, BigDecimal, BigInteger.

A primary key:

  • should never be null
  • should unique
  • should never change.

See also @GeneratedValue annotations.

javax.persistence.Lob[edit]

@javax.persistence.Lob
<method declaration>
  • large object for large data
  • the concrete type is derived from the java datatype

javax.persistence.ManyToOne[edit]

@ManyToOne([fetch = <FetchTypes>])
@JoinColumn(name = "<ColumnName>"[, nullable = <Boolean>] [, updatable = true] [, insertable = true])

The @JoinColumn is optional (to specify the column name) according to here.

javax.persistence.MappedSuperClass[edit]

  • no seperate table

javax.persistence.OneToMany[edit]

@javax.persistence.OneToMany[([mappedBy="<AttributName>"][,fetch = FetchType.<FetchType>][,cascade = {<CascadeTypes>}][,ophanRemoval= <Boolean>])]
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
@OrderColumn[(name = "<column in the table of many side>")]
  • MappedBy attribute names references the name of the attribute in the reference many class.
  • FetchType eager loads all referenced objects at once e.g. if the complete object has to be given or send outside the persistence management system. Casscade types are Cascade.ALL, ....
  • Orphan removal = true is important for correct merge.
  • The order column is necessary for sorted collections like lists. If no column is specified it will be created by the JPA implementation.

???Unfortunately Hibernate does not correctly interpret these annotations (see here). The pattern for solving this issues is described here. The values in the column fields have to be managed by the application.

javax.persistence.OneToOne[edit]

@javax.persistence.OneToOne[([cascade = <CascadeTypes>][,fetch = FetchType.<FetchType>])]

  • default cascade type is empty
  • default fetch type is EAGER

javax.persistence.OrderColumn[edit]

see here

javax.persistence.PersistenceContext[edit]

works if the client is running inside a persistance provider.

@javax.persistence.PersistenceContext[([type=(TRANSACTION|EXTENDED)][,][unitName="<Name>"])]
private javax.persistence.EntityManager <attribute name>;

injection via

@javax.persistence.PersistenceContext(unitName="testejbapplication") // s. persistence.xml <persistence-unit>
private javax.persistence.EntityManager em;

The annotations can not be used everywhere e.g. using it in the static method of the HibernateUtil class of application uweheuer. The error is 'the annotation is disallowed at this location'.

javax.persistance.Table[edit]

@javax.persistance.Table[([name="<Name>"])]
<class declaration>

javax.persistance.Temporal[edit]

@javax.persistance.Temporal[(TemporalType.DATE|TemporalType.TIME|TemporalType.TIMESTAMP)]
<method declaration>

Specifies the database type for the java type java.util.Date or java.util.Calendar.

javax.persistance.Transient[edit]

  • mark fields as not mapped to DB

Java Persistence Query Language (JPQL)[edit]

JPQL is an extension of EJBQL, which was introduced as part of the EJB 2.0 specification. The query language allows you to write portable queries that work regardless of the underlying data store. Queries in JPQL are not like SQL queries. Where a SQL query deals with tables and columns, a JPQL query deals with objects and attributes. Keywords of JPQL are case-insensitve, entities, identifier, attributes are case-sensitive. Operators are:

  • OR
  • AND
  • <>
  • NOT
  • BETWEEN
  • LIKE
  • IN
  • EMPTY
  • IS NULL
  • WHERE

Examples[edit]

Query q = em.createQuery(<QUERYSTRING>);
Query q = em.createNamedQuery(<QUERYNAME>); // see @NamedQueries annotation
List<Type> l = q.getResultList();


// Query Strings
FROM Kategorien // Kategorien is the classname or entity name if the name attribute of the entity annotation is used
SELECT k FROM Kategorien 
SELECT k FROM Kategorien ORDER BY name // name is attribute of class Kategorien
SELECT k FROM Kategorien k WHERE k.id.kategorieId = 1006 // id is a composite key
SELECT x FROM Magazine x WHERE x.y.z = 'abc' OR x.y IS NULL
SELECT x FROM Magazine x, IN(x.articles) y WHERE y.author = 'Mister B'
SELECT kadisId, productsOptionsValuesName FROM ballaballa.dbxt.ProductsOptionsValues p
Query q = emXT.createQuery("SELECT MAX(pov.id.productsOptionsValuesId) + 1 FROM ballaballa.dbxt.ProductsOptionsValues pov");
Number newPovIdNumber = (Number)q.getSingleResult();
Query q = emXT.createQuery("DELETE pa FROM ballaballa.dbxt.Produktattribute pa WHERE pa.id.hauptartikelnummer < " + BallaballaProperties.getHandmadeProductsMinKadisID());
q.executeUpdate();

q = emXT.createNativeQuery("INSERT INTO xt_sport_boeckmann.produktattribute " + " (`hauptartikelnummer`, `bestand`, `farbe_id`, `groesse_id`, `variantennummer`, `sortier_position`, `veroeffentlichungsdatum`,`verfuegbar_ab`) SELECT `hauptartikelnummer`,`bestand`,`farbe_id`,`groesse_id`,`variantennummer`,`sortier_position`,`veroeffentlichungsdatum`,`verfuegbar_ab` FROM " + " sp_kadis_db.produktattribute");
q.executeUpdate();

Type-safe Querying[edit]

For type safe querying metamodel classes have to be generated (see here). It is quite complex so skipped for the time being.

CriteriaQuery<T> c = qb.createQuery(T.class);

CriteriaQuerys are the nodes that are assembled in a tree to specify a CriteriaQuery. The generic type argument declares the type of result this CriteriaQuery will return upon execution.

Root<T> p = c.from(T.class);

Sets CriteriaQuery to query from T and returns the root of the query expression. The ctor means evaluate the expression across all instances of T. It is like the FROM clause of an SQL statement.

Predicate condition = qb.eq(....);
c.where(condition);

Predicate is another common form of query expression that evaluates to either true or false. A predicate is constructed by the CriteriaBuilder, which is the factory not only for CriteriaQuery, but also for query expressions.

Performance[edit]

  1. s. tips here

Dynamics[edit]

Make an entity persistent[edit]

Item i = new Item();
i.set(a);
transaction = entityManager.getTransaction();
transaction.begin();	
entityManager.persist(i);
transaction.commit();
entityManager.close();

Find an entity by its id[edit]

Item i = em.find(Item.class, new Long(1111));

Saving entities[edit]

JPA keeps an eye on the changes of an persistent object. An explicit save() is not neccessary. The so called flushing happens whenever a commit() is called. However, JPA implementations are allowed to synchronize at other times. E.g. Hibernate synchronize additionally (this is configurable):

  • before a query is called
  • when em.flush() is called.

Implementation[edit]

Creating JPA Entities[edit]

  1. with eclipse

Resources[edit]