CS636 Class 15 

Setting  up the pizza2 JPA Project : see JPA2Notes for coverage

Need to reload the databases for pizza2, because there’s a new table

Also new to database directory : 3 versions of persistence.xml, one for each DB

ant config-oradb, then ant sysTest  to run SystemTest with ant, Oracle

  --after this, see Oracle info in bin/META-INF/persistence.xml, read by JPA at initialization

ant config-hsqldb, then ant sysTest to run SystemTest with ant, HSQLDB

  --after this, see HSQLDB info in bin/META-INF/persistence.xml

Since persistence.xml specifies the DB, we can use one target, sysTest, to run SystemTest on any DB.

After ant-config-hsqldb, can use runTakeOrder.cmd/runShopAdmin.cmd, since they use HSQLDB.

Refreshing the project directs eclipse to reread all the files from the file system.  This is needed after “ant config-oradb” to use the right persistence.xml in eclipse runs. Eclipse has its own cached data that it works from normally, and this includes files in bin.

So switching from HSQLDB to Oracle when using eclipse to run things :

JPA

Entity Manager – manages the runtime environment (named “persistence context”), set up by JPA

It’s an object that has information on your POJOs and also has refs to your POJOs being managed

A specific POJO (with @Entity annotation) becomes a “managed object” when

Before we call em.persist(obj), the obj is “new” object, not managed

em.close() shuts down object management, so then the objects are “detached”

The em.close() happens just after the transaction commit, right at the end of the service call.

Thus all objects received by presentation code from the service layer are detached.  The transaction that pulled their data from the database has finished already.

When we pass detached objects back to the service layer, we have to be careful. Example discussed later with makeOrder. 

Basic steps of working with JPA:

At config time, get an emf, an EntityManagerFactory.

At this time the persistence.xml is read, so the username, password, and database URL are known to the system

Example persistence.xml on pg. 427.

We have 3 of these, one for each database. The ant task config-xxx copies one of these to src and bin, in META-INF dir, to make them active in the project.

Using the emf, create an EntityManager em.  In pizza2, this is done at transaction start. Then the em lives through the transaction and gets closed just after the commit or rollback.

Then the em is used to do database operations

em.persist(o)

o = em.find(class, id)

em.createQuery(“select …”)

em.getTransaction().begin()

em.getTransaction().commit()

no update method for em: just commit persistent object, em figures out what to do

em.close()  makes all managed objects “detached”

Entity Life Cycle diagram (simplified from slide 29 of Chapter13slides)

Note this is just what we’ve covered so far, but it’s enough to do real projects.

Just after we create o in Java, the object is not "managed". It is "new".

                                               <new> 
                                                 |
                                                 | em.persist()
                                                 |
                                                 v
 <detached> <---em.commit/rollback/close()--- <managed> <---access DB with em.find or queries---

Important idea of a “managed object”

Detached objects work fine as POJOs. They hold data that was previously read from the database, so it may be a little stale.

More discussion on detached objects in Presentation

Since the em is closed at commit/rollback time, any domain objects returned to the presentation code are all detached.  This is true for all service methods. Thus any domain object you see in presentation code is detached, and that’s one reason we don’t allow changes in (persistent) domain objects in the presentation layer: they can’t be properly tracked.  It’s not a good idea for other reasons too, like securing information. Note that Cart is a domain object but it's not persistent, so it doesn't count here.

In fact, we are only allowing a few domain objects to ever travel into the presentation layer. In pizza, we have PizzaOrderData objects used to transport PizzaOrder information into presentation, with only strings for size and toppings, not domain objects.

In music, we have InvoiceData, UserData, and DownloadData objects for the same purpose. We do allow Product and Track objects to travel into the presentation layer, but the excuse is that they are fully invariant. They never change (in this app), and none are added to the system during operation.

The harder case to argue is the LineItem, since it is officially persistent (has @Entity annotation), yet travels to the presentation layer attached to the Cart, as the user decides what should be in the Cart.  However it turns out that LineItem objects have two life phases:

  1.  As details of the Cart, a user-private variable. These LineItems are new objects to JPA, not managed.
  2.  As details of the Invoice created in the checkout. These LineItems are persisted, thus managed, then the transaction is committed, and no LineItems are returned to presentation.
Thus at no time are managed or even detached LineItem objects are returned to presentation.  Of course we could have two classes for LineItems, one for each phase, to avoid the possible confusion this double-life causes.

Generated IDs

Here we see how the new table pizza_id_gen works to generate a new order number. It also does this for new pizza_topping id, Pizza_size id.
@Entity
@Table(name="PIZZA_TOPPINGS")
   
public class PizzaTopping implements Serializable, Comparable<PizzaTopping> {
    private static final long serialVersionUID = 1L;

    @Id
    @TableGenerator(name="ToppingIdGen",
            table = "PIZZA_ID_GEN",
            pkColumnName = "GEN_NAME",
            valueColumnName = "GEN_VAL",
            pkColumnValue = "ToppingId_Gen")
               
    @GeneratedValue(generator="ToppingIdGen")
    @Column(unique=true, nullable=false)
    private int id;


     Near end of createdb.sql:

-- for generated ids specific to the pizza project
-- pizza_id_gen has one row for each table that needs ids, i.e. each entity table
-- gen_val start at 0, so first generated id for each entity is 1
CREATE TABLE PIZZA_ID_GEN (GEN_NAME VARCHAR(50) NOT NULL, GEN_VAL INTEGER, PRIMARY KEY (GEN_NAME));
INSERT INTO PIZZA_ID_GEN (GEN_NAME, GEN_VAL) values ('Ordno_Gen', 0);
INSERT INTO PIZZA_ID_GEN (GEN_NAME, GEN_VAL) values ('SizeId_Gen', 0);
INSERT INTO PIZZA_ID_GEN (GEN_NAME, GEN_VAL) values ('ToppingId_Gen', 0);
INSERT INTO PIZZA_ID_GEN (GEN_NAME, GEN_VAL) values ('MenuSizeId_Gen', 0);
INSERT INTO PIZZA_ID_GEN (GEN_NAME, GEN_VAL) values ('MenuToppingId_Gen', 0);


Part of sysTest run: at presentation level, one "so" command, at service level, have 3 calls here: getToppingNames(), getPizzaSizeNames(), and makeOrder. At DAO level, 3 startTransaction, commitTransaction pairs, and within find MenuToppings(), findMenuSizes(), and in makeOrder, findMenuSize(), findMenuTopping() findCurrentDay, and insertOrder().

*************so 5***************

[EL Fine]: sql: Connection(1894338251)--SELECT ID, TOPPING_NAME FROM MENU_TOPPINGS
[EL Fine]: sql: Connection(1894338251)--SELECT ID, SIZE_NAME FROM MENU_SIZES
in findPizzaSize
[EL Fine]: sql: Connection(1894338251)--SELECT ID, SIZE_NAME FROM MENU_SIZES WHERE (SIZE_NAME = ?)
    bind => [small]
cking topping Pepperoni
[EL Fine]: sql: Connection(1894338251)--SELECT ID, TOPPING_NAME FROM MENU_TOPPINGS WHERE (TOPPING_NAME = ?)
    bind => [Pepperoni]
[EL Fine]: sql: Connection(1894338251)--select current_day from pizza_sys_tab
[EL Fine]: sql: Connection(1894338251)--UPDATE PIZZA_ID_GEN SET GEN_VAL = GEN_VAL + ? WHERE GEN_NAME = ?
    bind => [50, Ordno_Gen]
[EL Fine]: sql: Connection(1894338251)--SELECT GEN_VAL FROM PIZZA_ID_GEN WHERE GEN_NAME = ?
    bind => [Ordno_Gen]
[EL Fine]: sql: Connection(1894338251)--UPDATE PIZZA_ID_GEN SET GEN_VAL = GEN_VAL + ? WHERE GEN_NAME = ?
    bind => [50, ToppingId_Gen]
[EL Fine]: sql: Connection(1894338251)--SELECT GEN_VAL FROM PIZZA_ID_GEN WHERE GEN_NAME = ?
    bind => [ToppingId_Gen]
[EL Fine]: sql: Connection(1894338251)--UPDATE PIZZA_ID_GEN SET GEN_VAL = GEN_VAL + ? WHERE GEN_NAME = ?
    bind => [50, SizeId_Gen]
[EL Fine]: sql: Connection(1894338251)--SELECT GEN_VAL FROM PIZZA_ID_GEN WHERE GEN_NAME = ?
    bind => [SizeId_Gen]
[EL Fine]: sql: Connection(1894338251)--INSERT INTO PIZZA_SIZES (ID, SIZE_NAME) VALUES (?, ?)
    bind => [1, small]
[EL Fine]: sql: Connection(1894338251)--INSERT INTO PIZZA_ORDERS (ID, DAY, room_number, STATUS, SIZE_ID) VALUES (?, ?, ?, ?, ?)
    bind => [1, 1, 5, 1, 1]
[EL Fine]: sql: Connection(1894338251)--INSERT INTO PIZZA_TOPPINGS (ID, TOPPING_NAME, ORDER_ID) VALUES (?, ?, ?)
    bind => [1, Pepperoni, 1]
----OK

Look at pizza2 files:

Same idea of top-level build.xml, database dir with own build.xml, src, lib, bin directories

Same old file names in database: createdb.sql, dropdb.sql, showdb.sql, but edits to createdb.sql.

Reload DBs for pizza2: need default value for s_status and t_status, new id-gen table

Find persistence.xml versions under database: see db urls, username, pw's.

New top-level config commands: ant config-oradb, config-mysqldb, config-hsqldb:  these put the persistance.xml for the current db in the right place, that is, under META-INF/persistence.xml in the .class file tree.

Running with Oracle, with tunnel working to dbs2.cs.umb.edu as before

      
      cd database
      ant drop-oradb
      ant load-oradb
      cd ..
      ant config-oradb
      ant sysTest
      

--after this, see Oracle info in bin/META-INF/persistence.xml, read by JPA at initialization

Since persistence.xml specifies the DB, we can use one target, sysTest, to run SystemTest on any DB, but need the config-xxxdb command to switch databases.

To run with mysql:

     
      cd database
      ant drop-mysqldb
      ant load-mysqldb
      cd ..
      ant config-mysqldb
      ant sysTest
 

--after this, see mysql info in bin/META-INF/persistence.xml, read by JPA at initialization

See instructions to create the pizza2 eclipse project in JPA2Notes

For simple execution and source mods, can treat it as a Java project: all the needed libraries are in lib.

Refreshing the project directs eclipse to reread all the files from the file system.  This is needed after “ant config-oradb” to use the right persistence.xml in eclipse runs. Eclipse has its own cached data that it works from normally, and this includes files in bin.

So switching from HSQLDB to Oracle when using eclipse to run things :

Eclipse and pizza2:

For JPA tools accessible via the eclipse GUI, need to set it up as a JPA project--this is trickier.

JPA tools:

I should demo these at some point.

Next: look at service methods of pizza2.