550 likes | 568 Views
Java Persistence: EntityManager. Goals. Become familiar with the Java Persistence API and EntityManager Become familiar with how to setup a project using JPA and a provider (Hibernate). Objectives. Provide an Overview of the Java Persistence API Go through Details of the EntityManager
E N D
Java Persistence: EntityManager Java Persistence: EntityManager
Java Persistence: EntityManager Goals • Become familiar with the Java Persistence API and EntityManager • Become familiar with how to setup a project using JPA and a provider (Hibernate)
Java Persistence: EntityManager Objectives • Provide an Overview of the Java Persistence API • Go through Details of the EntityManager • Go through some implementation details associated with class projects
Java Persistence: EntityManager Overview • Earlier versions of EJB Specification defined the persistence layer • javax.ejb.EntityBean • Java EE 5 moved persistence to its own specification • Java Persistence API (JPA) version 1.0 • javax.persistence • ease of use API above JDBC • Provides • Object/Relational Mapping (ORM) Engine • Query Language (SQL-Like, based on former EJB-QL) • Java EE 6 uses JPA 2.0
Java Persistence: EntityManager javax.persistence.EntityManager • Replaces much of the EJB 2.x “Home” functionality • Handles O/R Mapping of Entities to the database • Provides APIs • inserting objects into database • getting objects from database • synchronizing objects with database • querying database • Provides caching • Coordinates with transactional services (JTA) • Tightly integrated with Java EE and EJB, but not limited to that environment
Java Persistence: EntityManager Entities • (formerly and sometimes still called Entity Beans) • are now Plain Old Java Objects (POJOs) • nothing special happens when calling new Author author = new Author(); • are not persistent until associated with an EntityManager em.persist(author);
Java Persistence: EntityManager Example Author POJO Entity Warning: Using GeneratedValue without specifying a specific strategy should only be used when you have no intention of controlling how the provider manages primary keys for this object type. This would be rare. @javax.persistence.Entity public class Author { private long id; private long version=0; private String firstName; private String lastName; private String subject; private Date publishDate; public Author() {} public Author(long id) { this.id = id; } @Id @GeneratedValue public long getId() { return id;} private void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } ... }
Java Persistence: EntityManager Creating Entity in Database Author author = new Author(); //primary key will be gen author.setFirstName("dr"); author.setLastName("seuss"); author.setSubject("children"); author.setPublishDate(new Date()); log_.info("creating author:" + author); em.persist(author); log_.info("created author:" + author); //output -creating author:id=0, fn=dr, ln=seuss, subject=children, pdate=Fri Sep 15 11:54:15 EDT 2006 -created author:id=50, fn=dr, ln=seuss, subject=children, pdate=Fri Sep 15 11:54:15 EDT 2006
Java Persistence: EntityManager Managed and Unmanaged Entities • Unmanaged state (detached) • instance not associated with an EntityManager • state changes are not tracked • can be serialized to client and returned to be synchronized with database • nothing equivalent to this state in EJB 2.1 entity beans • Managed state (attached) • instance associated with an EntityManager • state changes are tracked within a Persistence Context • EJB 2.1 entity beans were always managed • client interfaced with data through a proxy or state transferred through a Data Transfer Object
Java Persistence: EntityManager Persistence Context • A set of attached entity instances managed by an EntityManager • All entities become detached once closed • Two types • Transaction-scoped Persistence Contexts • begin/end at transaction boundaries • only made available through container managed persistence contexts • Extended Persistence Contexts • live beyond any single transaction • allow longer-lived interactions with database without lengthy transactions tying up database resources
Java Persistence: EntityManager Persistence Context Examples • Transaction-scoped (inside server container) @PersistenceContext(unitName=”jpaDemo”)EntityManager em; @TransactionAttribute(REQUIRED) public void update(long authorId, String type) { Author author = em.find(Author.class, authorId); author.setType(type); } • Extended (inside or outside server container) EntityManager em = Persistence. createEntityManagerFactory(“jpaDemo”).createEntityManager(); tx.begin(); //tx 1 begins Author author = em.find(Author.class, authorId); tx.commit(); //tx 1 ends, but author remains managed ... tx.begin(); //tx 2 begins author.setType(type); tx.commit(); //tx 2 ends, and author is still managed until close
Java Persistence: EntityManager Persistence Unit • A set of classes that are mapped to the database • defined in META-INF/persistence.xml • must have an identity • “” is a valid identity • Classes • may be named in persistence.xml file • may be automatically scanned for in the classpath • orm.xml • optionally provided to augment, provide, or replace class persistence metadata • (more on orm.xml in Core ORM topic)
Java Persistence: EntityManager Example Component Layout META-INF/ +---persistence.xml ejava + ---examples +---… +---DAOException.class +---AuthorDAO.class +---jpa | +---JPAAuthorDAO.class +--bo +---Author.class
Java Persistence: EntityManager Example persistence.xml <?xml version="1.0" encoding="UTF-8"?> <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_1_0.xsd" version="1.0"> <persistence-unit name="jpaDemo"> <jta-data-source>java:/ejavaDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create"/> <property name="hibernate.show_sql" value="true"/ </properties> </persistence-unit> </persistence> referenced by name • global JNDI name by which • provider references resource • (will be used when deployed within server) • may use properties element in Java SE environments that lack JNDI • vendor-specific way to configure persistence provider
Java Persistence: EntityManager Another Example persistence.xml <?xml version="1.0" encoding="UTF-8"?> <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_1_0.xsd" version="1.0"> <persistence-unit name="jpaDemo"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.Provider"/> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.hbm2ddl.auto" value="create"/> </properties> </persistence-unit> </persistence
Java Persistence: EntityManager Solution-specific property option: hibernate.properties $ cat hibernate.properties hibernate.dialecte=org.hibernate.dialect.HSQLDialect hibernate.connection.url=jdbc:hsqldb:hsql://localhost:9001 hibernate.connection.driver_class=org.hsqldb.jdbcDriver hibernate.connection.password= hibernate.connection.username=sa hibernate.hbm2ddl.auto=create #hibernate.show_sql=true #hibernate.format_sql=true #hibernate.jdbc.batch_size=0 • Easier to provide unit testing options that do not get propagated into production artifacts
Java Persistence: EntityManager persistence.xml elements • name – identity to reference Persistence Unit • provider – fully qualified name of javax.persistence.PersistenceProvider • not needed if provider found in classpath acceptable • mapping-file – resource path to optional mapping file • can be used to specify <class>es or specify/override @Annotation details • jta-data-source • vendor-specific reference to data source using JTA transactions • non-jta-data-source • vendor-specific reference to data source using RESOURCE_LOCAL transactions • jar-file • optional/additional jar file to scan for classes • class • specifies entity classes not automatically scanned by provider • exclude-unlisted-classes • if set, provider will not automatically scan archive for entity classes • properties • may be used to provide vendor-specific properties to configure persistence providers
Java Persistence: EntityManager Specifying an optional orm.xml file <?xml version="1.0" encoding="UTF-8"?> <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_1_0.xsd" version="1.0"> <persistence-unit name="jpaDemo"> <provider>...</provider> <mapping-file>META-INF/orm.xml</mapping-file> <properties> ... </properties> </persistence-unit> </persistence
Java Persistence: EntityManager Optional orm.xml overrides <?xml version="1.0" encoding="UTF-8"?> <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_1_0.xsd" version="1.0"> <entity class="ejava.examples.daoex.bo.Author" metadata-complete="false"> <table name="DAO_AUTHOR"/> </entity> </entity-mappings>
Java Persistence: EntityManager Entities Discovered • classes with @Entity annotation • in the persistence.xml's JAR file • contained in any JAR file listed in jar-file element • classes mapped • with META-INF/orm.xml • with custom mapping files • classes listed in persistence.xml's “<class>” element
Java Persistence: EntityManager Java SE Steps • Startup • Get EntityManagerFactory • Runtime • Create EntityManager • Start Transaction • Interact with Entity Manager • Commit Transaction • Close EntityManager • Shutdown • Close EntityManagerFactory
Java Persistence: EntityManager javax.persistence.Persistence • used to bootstrap persistence in Java SE environments public class javax.persistence.Persistence { public static java.lang.String PERSISTENCE_PROVIDER; public javax.persistence.Persistence(); public static EntityManagerFactory createEntityManagerFactory(String puName); public static EntityManagerFactory createEntityManagerFactory(String puName, Map props); } • example usage EntityManagerFactory emf = Persistence .createEntityManagerFactory(“jpaDemo”);
Java Persistence: EntityManager javax.persistence.EntityManagerFactory • used to obtain reference to EntityManager in standard Java SE environments package javax.persistence; public interface EntityManagerFactory{ EntityManager createEntityManager(); EntityManager createEntityManager(Map props); void close(); boolean isOpen(); } • Map used to supply or override properties in persistence.xml • close() and isOpen() only valid for non-injected EMFs • example usage • EntityManager em = emf.createEntityManager();
Java Persistence: EntityManager javax.persistence.EntityManager package javax.persistence; public interface EntityManager{ EntityTransaction getTransaction(); void persist(java.lang.Object); Object<T> find(Class<T>, Object pKey); Object<T> getReference(Class<T>, Object pKey); boolean contains(Object); Object merge(java.lang.Object); void refresh(java.lang.Object); void remove(java.lang.Object); Object find(java.lang.Class, java.lang.Object); void flush(); void clear(); void close(); isOpen(); --- Query createQuery(java.lang.String); Query createNamedQuery(java.lang.String); Query createNativeQuery(java.lang.String); Query createNativeQuery(java.lang.String, java.lang.Class); Query createNativeQuery(java.lang.String, java.lang.String); ...
Java Persistence: EntityManager persist() Author author = new Author(); author.setFirstName("dr"); author.setLastName("seuss"); author.setSubject("children"); author.setPublishDate(new Date()); em.persist(author); • Extended persistence contexts • queues write until associated with transaction • Transaction-scoped persistence contexts • illegal to call outside the scope of a transaction • Actual write to the database depends on FlushMode • manually controlled with flush() call
Java Persistence: EntityManager find() Author author2=null; author2 = em.find(Author.class, id); log_.info("got author author:" + author2); got author author:id=51, fn=thing, ln=one, subject=children, pdate=Fri Sep 15 11:54:15 EDT 2006 • Returns an instance of the class associated with the specified primary key value • relationships are instantiated according to lazy-loading policies • Returns null if primary key not found • Uses generics, so no casting is necessary • Ids can be autoboxed without a manual wrapper • can be called outside the scope of a transaction • will be attached to open persistence context • second find() will return same object
Java Persistence: EntityManager getReference() Author author2=null; author2 = em.getReference(Author.class, id); log_.info("got author author:" + author2); • Similar to find() • Returns an instance of the class associated with the specified primary key value • no guarantee that object state initialized • Throws EntityNotFoundException if primary key not found
Java Persistence: EntityManager createQuery() • 5 createQuery() methods Query createQuery(String ejbqlString); Query createNamedQuery(String name); Query createNativeQuery(String sqlString); Query createNativeQuery(String sqlString, Class resultClass); Query createNativeQuery(String sqlString, String resultSetMap); • example usage Query query = em.createQuery("from jpaAuthor where id=" + id); Author author = (Author)query.getSingleResult(); • use EJB-QL and native (SQL) query languages • similar to find/getReference() • returned objects attached to open persistence context
Java Persistence: EntityManager updating entities • Updates to managed entities automatically get propagated to database according to flush policy public Author update(Author author) { Author dbAuthor = em.find(Author.class,author.getId()); dbAuthor.setFirstName(author.getFirstName()); dbAuthor.setLastName(author.getLastName()); dbAuthor.setSubject(author.getSubject()); dbAuthor.setPublishDate(author.getPublishDate()); return dbAuthor; } • Note that if author passed in was already managed... • the changes have already been queued • the dbAuthor returned from the find() will be the same object as author • the sets are unnecessarily changing the values of the Author to their current values
Java Persistence: EntityManager merge() • merges state changes to detached objects back into persistent storage public Author updateByMerge(Author author) { Author managedAuthor = em.merge(author); return managedAuthor; } • Original is left detached • Object returned is managed • Returned object is added to persistence if did not already exist • Updates are made to existing object if already exists
Java Persistence: EntityManager remove() public void remove(Author author) { em.remove(author); } • removes object from database • physically performed in database according to flush policy • cascades to related objects according to cascade policy • object will be detached
Java Persistence: EntityManager refresh() and contains() • refresh() Author author = em.find(Author.class, id); em.refresh(author); • used to make sure entity is in sync with database • cascades to related entities depending on cascade policy • entity must be currently managed by entity manager instance • contains() if (em.contains(author)) { ... } • used to test if instance is being managed by entity manager instance
Java Persistence: EntityManager clear() and flush() • clear() • detaches all entities • does not commit queued changes • call flush() prior to clear() • flush() • changes not synchronized with database until entity manager flushed • persist(), merge(), remove() • occurs automatically before executing • correlated queries • permits query to reflect changes in persistence context • transaction commit • not impacted by primary key finders • find(), getReference() • FlushMode • AUTO – default and most sane • COMMIT – an optimization to only flush and end of transaction. May limit amount of database locking that occurs
Java Persistence: EntityManager lock() and getDelegate() • lock() • provides a pessimistic write lock for entity • will be covered with later during transaction topics • getDelegate() • returns vendor object that implements EntityManager interface • used to expose vendor-specific extension API
Java Persistence: EntityManager EntityTransactions • Only available for Entity Managers with an extended persistence context • Transaction-scoped persistence contexts are only available with containers that support JTA transactions • Extended persistence contexts generally pertain to Java SE applications using javax.persistence.Persistence class to get EntityManagerFactory • transaction-like API for managing transactions within the single resource • transaction context obtained from EntityManager javax.persistence.EntityTransaction tx = em.getTransaction(); tx.begin() tx.commit() tx.isActive() tx.rollback()
Java Persistence: EntityManager Example private EntityManager em = ... public void testQuery() throws Exception { Author author = new Author(); author.setFirstName("test"); author.setLastName("Query"); author.setSubject("testing"); author.setPublishDate(new Date()); em.persist(author); //need to associate em with Tx to allow query to see entity in DB try { em.getTransaction().begin(); //note that the persist does not have to be within the tx em.getTransaction().commit(); } catch (Exception ex) { em.getTransaction().rollback(); fail("" + ex); } Author author2 = null; Query query = em.createQuery( "from jpaAuthor where id=" + author.getId()); author2 = (Author)query.getSingleResult(); assertNotNull(author2); assertEquals(author.getFirstName(), author2.getFirstName()); assertEquals(author.getLastName(), author2.getLastName()); assertEquals(author.getSubject(), author2.getSubject()); assertEquals(author.getPublishDate(), author2.getPublishDate()); }
Java Persistence: EntityManager Implementation/Testing Details
Java Persistence: EntityManager Add JPA Dependencies • JPA API • JPA 1.0 <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> • JPA 2.0 <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.0.Final</version> <scope>provided</scope> </dependency> • Both available from the JBoss Maven/Nexus repository
Java Persistence: EntityManager Add Provider Dependencies • Identify the JBoss repository <repositories> <repository> <id>jboss-nexus</id> <name>JBoss Nexus Repository</name> <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url> </repository> </repositories> • Add hibernate-entitymanager • primary dependency we need to pull in hibernate • maven will pull in many other dependencies based on hibernate-entitymanager's pom.xml <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.6.0.Final</version> <scope>test</scope> </dependency>
Java Persistence: EntityManager Add Dependency Overrides • We have to pull in a library that will allow hibernate to use log4j <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> <scope>test</scope> </dependency> • Otherwise, we’ll get the following error Tests run: 12, Failures: 0, Errors: 12, Skipped: 0, Time elapsed: 0.078 sec <<< FAILURE! testCreate(ejava.examples.dao.jpa.JPAAuthorDAODemo) Time elapsed: 0.031 sec <<< ERROR! java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:60)
Java Persistence: EntityManager Add Standard DB Properties • Identical to what was needed for JDBC <profile> <!-- defines our default database --> <id>hsql</id> <activation> <property> <!-- use this property to name another db --> <name>jdbcdb</name> <value>hsql</value> </property> </activation> <properties> <jdbc.driver>org.hsqldb.jdbcDriver</jdbc.driver> <jdbc.url>jdbc:hsqldb:hsql://localhost:9001</jdbc.url> <jdbc.user>sa</jdbc.user> <jdbc.password /> <hibernate.dialect>org.hibernate.dialect.HSQLDialect </hibernate.dialect> </properties> <dependencies> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>1.8.0.4</version> <scope>test</scope> </dependency> </dependencies> </profile>
Java Persistence: EntityManager Design Mechanism to Pass DB Properties • Compile-time property filtering • default values end up in .jar file; not ideal • much less work; good for prototypes • demonstrated here within JPA examples • Runtime property passing • more realistic • more work • could delegate details to a Test Utility Class • demonstrated in JDBC examples
Java Persistence: EntityManager src/main/resources/META-INF/persistence.xml <persistence ...> <persistence-unit name="jpaDemo"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.dialect" value="${hibernate.dialect}"/> <property name="hibernate.connection.url" value="${jdbc.url}"/> <property name="hibernate.connection.driver_class" value="${jdbc.driver}"/> <property name="hibernate.connection.password" value="${jdbc.password}"/> <property name="hibernate.connection.username" value="${jdbc.user}"/> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.format_sql" value="false"/> <!–- used in special cases <property name="hibernate.hbm2ddl.auto" value="create"/> <property name="hibernate.jdbc.batch_size" value=“0"/> --> </properties> </persistence-unit> </persistence>
Java Persistence: EntityManager src/test/resources/hibernate.properties hibernate.dialecte=${hibernate.dialect} hibernate.connection.url=${jdbc.url} hibernate.connection.driver_class=${jdbc.driver} hibernate.connection.password=${jdbc.password} hibernate.connection.username=${jdbc.user} #only use auto create for quick prototypes #hibernate.hbm2ddl.auto=create hibernate.show_sql=true hibernate.format_sql=true # eliminate batching to help debug DB errors #hibernate.jdbc.batch_size=0
Java Persistence: EntityManager Add Resource Filtering Spec to pom.xml • This overrides the default behavior of resource handling <build> <!--tell the resource plugin to perform filtering on resources to fill in JDBC URL, etc. --> <!–- if $vars in src/main/resources branch --> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <!-- if $vars in src/test/resources branch <testResources> <testResource> <directory>src/test/resources</directory> <filtering>true</filtering> </testResource> </testResources> ... </build>
Java Persistence: EntityManager target/classes/META-INF/persistence.xml <persistence ...> <persistence-unit name="jpaDemo"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.hbm2ddl.auto" value="create"/> </properties> </persistence-unit> </persistence>
Java Persistence: EntityManager target/test-classes/hibernate.properties hibernate.dialecte=org.hibernate.dialect.HSQLDialect hibernate.connection.url=jdbc:hsqldb:hsql://localhost:9001 hibernate.connection.driver_class=org.hsqldb.jdbcDriver hibernate.connection.password= hibernate.connection.username=sa hibernate.hbm2ddl.auto=create hibernate.show_sql=false #hibernate.format_sql=true #hibernate.jdbc.batch_size=0
Java Persistence: EntityManager Setup JUnit TestCase import static org.junit.Assert.*; import org.junit.*; public class JPAAccountDAOTest { @BeforeClass public static void setUpClass() throws Exception { … } @Before public void setUp() throws Exception { … } @After public void tearDown() throws Exception { … } @AfterClass public static void tearDownClass() throws Exception { … } @Test public void testJPACreate() throws Exception { … } @Test public void testXXX() throws Exception { … }
Java Persistence: EntityManager Setup EntityManager within TestCase public class JPAAccountDAOTest { private static Log log = LogFactory.getLog(JPAAccountDAO.class); private EntityManagerFactory emf; private EntityManager em; private AccountDAO dao; @Before public void setUp() throws Exception { emf = Persistence.createEntityManagerFactory("eSalesBO"); em = emf.createEntityManager(); dao = new JPAAccountDAO(); ((JPAAccountDAO)dao).setEntityManager(em); cleanup(); //get env to a known state before starting tests //may want in its own transaction em.getTransaction().begin(); //optional //have transaction started prior to test }
Java Persistence: EntityManager Teardown EntityManager within TestCase @After public void tearDown() throws Exception { if (em != null) { EntityTransaction tx = em.getTransaction(); if (tx.isActive()) { if (tx.getRollbackOnly()) { tx.rollback(); } else { tx.commit(); } } em.close(); } if (emf != null) { emf.close(); } }