Search in sources :

Example 6 with Item

use of org.jpwh.model.concurrency.version.Item in project microservices by pwillhan.

the class Versioning method storeCategoriesAndItems.

public ConcurrencyTestData storeCategoriesAndItems() throws Exception {
    UserTransaction tx = TM.getUserTransaction();
    tx.begin();
    EntityManager em = JPA.createEntityManager();
    ConcurrencyTestData testData = new ConcurrencyTestData();
    testData.categories = new TestData(new Long[3]);
    testData.items = new TestData(new Long[5]);
    for (int i = 1; i <= testData.categories.identifiers.length; i++) {
        Category category = new Category();
        category.setName("Category: " + i);
        em.persist(category);
        testData.categories.identifiers[i - 1] = category.getId();
        for (int j = 1; j <= testData.categories.identifiers.length; j++) {
            Item item = new Item("Item " + j);
            item.setCategory(category);
            item.setBuyNowPrice(new BigDecimal(10 + j));
            em.persist(item);
            testData.items.identifiers[(i - 1) + (j - 1)] = item.getId();
        }
    }
    tx.commit();
    em.close();
    return testData;
}
Also used : UserTransaction(javax.transaction.UserTransaction) Item(org.jpwh.model.concurrency.version.Item) EntityManager(javax.persistence.EntityManager) Category(org.jpwh.model.concurrency.version.Category) TestData(org.jpwh.shared.util.TestData) BigDecimal(java.math.BigDecimal)

Example 7 with Item

use of org.jpwh.model.concurrency.version.Item in project microservices by pwillhan.

the class Versioning method forceIncrement.

// TODO This throws the wrong exception!
// @Test(expectedExceptions = OptimisticLockException.class)
@Test(expectedExceptions = org.hibernate.StaleObjectStateException.class)
public void forceIncrement() throws Throwable {
    final TestData testData = storeItemAndBids();
    Long ITEM_ID = testData.getFirstId();
    UserTransaction tx = TM.getUserTransaction();
    try {
        tx.begin();
        EntityManager em = JPA.createEntityManager();
        /* 
               The <code>find()</code> method accepts a <code>LockModeType</code>. The
               <code>OPTIMISTIC_FORCE_INCREMENT</code> mode tells Hibernate that the version
               of the retrieved <code>Item</code> should be incremented after loading,
               even if it's never modified in the unit of work.
             */
        Item item = em.find(Item.class, ITEM_ID, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
        Bid highestBid = queryHighestBid(em, item);
        // Now a concurrent transaction will place a bid for this item, and
        // succeed because the first commit wins!
        Executors.newSingleThreadExecutor().submit(new Callable<Object>() {

            @Override
            public Object call() throws Exception {
                UserTransaction tx = TM.getUserTransaction();
                try {
                    tx.begin();
                    EntityManager em = JPA.createEntityManager();
                    Item item = em.find(Item.class, testData.getFirstId(), LockModeType.OPTIMISTIC_FORCE_INCREMENT);
                    Bid highestBid = queryHighestBid(em, item);
                    try {
                        Bid newBid = new Bid(new BigDecimal("44.44"), item, highestBid);
                        em.persist(newBid);
                    } catch (InvalidBidException ex) {
                    // Ignore
                    }
                    tx.commit();
                    em.close();
                } catch (Exception ex) {
                    // This shouldn't happen, this commit should win!
                    TM.rollback();
                    throw new RuntimeException("Concurrent operation failure: " + ex, ex);
                }
                return null;
            }
        }).get();
        try {
            /* 
                   The code persists a new <code>Bid</code> instance; this does not affect
                   any values of the <code>Item</code> instance. A new row will be inserted
                   into the <code>BID</code> table. Hibernate would not detect concurrently
                   made bids at all without a forced version increment of the
                   <code>Item</code>. We also use a checked exception to validate the
                   new bid amount; it must be greater than the currently highest bid.
                */
            Bid newBid = new Bid(new BigDecimal("44.44"), item, highestBid);
            em.persist(newBid);
        } catch (InvalidBidException ex) {
        // Bid too low, show a validation error screen...
        }
        /* 
               When flushing the persistence context, Hibernate will execute an
               <code>INSERT</code> for the new <code>Bid</code> and force an
               <code>UPDATE</code> of the <code>Item</code> with a version check.
               If someone modified the <code>Item</code> concurrently, or placed a
               <code>Bid</code> concurrently with this procedure, Hibernate throws
               an exception.
             */
        tx.commit();
        em.close();
    } catch (Exception ex) {
        throw unwrapCauseOfType(ex, org.hibernate.StaleObjectStateException.class);
    } finally {
        TM.rollback();
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) TestData(org.jpwh.shared.util.TestData) Callable(java.util.concurrent.Callable) BigDecimal(java.math.BigDecimal) OptimisticLockException(javax.persistence.OptimisticLockException) NoResultException(javax.persistence.NoResultException) InvalidBidException(org.jpwh.model.concurrency.version.InvalidBidException) InvalidBidException(org.jpwh.model.concurrency.version.InvalidBidException) Item(org.jpwh.model.concurrency.version.Item) EntityManager(javax.persistence.EntityManager) Bid(org.jpwh.model.concurrency.version.Bid) JPATest(org.jpwh.env.JPATest) Test(org.testng.annotations.Test)

Example 8 with Item

use of org.jpwh.model.concurrency.version.Item in project microservices by pwillhan.

the class Versioning method firstCommitWins.

@Test(expectedExceptions = OptimisticLockException.class)
public void firstCommitWins() throws Throwable {
    UserTransaction tx = TM.getUserTransaction();
    try {
        tx.begin();
        EntityManager em = JPA.createEntityManager();
        Item someItem = new Item("Some Item");
        em.persist(someItem);
        tx.commit();
        em.close();
        final Long ITEM_ID = someItem.getId();
        tx.begin();
        em = JPA.createEntityManager();
        /* 
               Retrieving an entity instance by identifier loads the current version from the
               database with a <code>SELECT</code>.
             */
        Item item = em.find(Item.class, ITEM_ID);
        // select * from ITEM where ID = ?
        /* 
               The current version of the <code>Item</code> instance is 0.
             */
        assertEquals(item.getVersion(), 0);
        item.setName("New Name");
        // The concurrent second unit of work doing the same
        Executors.newSingleThreadExecutor().submit(new Callable<Object>() {

            @Override
            public Object call() throws Exception {
                UserTransaction tx = TM.getUserTransaction();
                try {
                    tx.begin();
                    EntityManager em = JPA.createEntityManager();
                    Item item = em.find(Item.class, ITEM_ID);
                    // select * from ITEM where ID = ?
                    assertEquals(item.getVersion(), 0);
                    item.setName("Other Name");
                    tx.commit();
                    // update ITEM set NAME = ?, VERSION = 1 where ID = ? and VERSION = 0
                    // This succeeds, there is a row with ID = ? and VERSION = 0 in the database!
                    em.close();
                } catch (Exception ex) {
                    // This shouldn't happen, this commit should win!
                    TM.rollback();
                    throw new RuntimeException("Concurrent operation failure: " + ex, ex);
                }
                return null;
            }
        }).get();
        /* 
               When the persistence context is flushed Hibernate will detect the dirty
               <code>Item</code> instance and increment its version to 1. The SQL
               <code>UPDATE</code> now performs the version check, storing the new version
               in the database, but only if the database version is still 0.
             */
        em.flush();
    // update ITEM set NAME = ?, VERSION = 1 where ID = ? and VERSION = 0
    } catch (Exception ex) {
        throw unwrapCauseOfType(ex, OptimisticLockException.class);
    } finally {
        TM.rollback();
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) Item(org.jpwh.model.concurrency.version.Item) EntityManager(javax.persistence.EntityManager) OptimisticLockException(javax.persistence.OptimisticLockException) Callable(java.util.concurrent.Callable) OptimisticLockException(javax.persistence.OptimisticLockException) NoResultException(javax.persistence.NoResultException) InvalidBidException(org.jpwh.model.concurrency.version.InvalidBidException) JPATest(org.jpwh.env.JPATest) Test(org.testng.annotations.Test)

Aggregations

EntityManager (javax.persistence.EntityManager)8 UserTransaction (javax.transaction.UserTransaction)8 Item (org.jpwh.model.concurrency.version.Item)8 Test (org.testng.annotations.Test)6 BigDecimal (java.math.BigDecimal)5 Callable (java.util.concurrent.Callable)4 JPATest (org.jpwh.env.JPATest)4 NoResultException (javax.persistence.NoResultException)3 OptimisticLockException (javax.persistence.OptimisticLockException)3 Category (org.jpwh.model.concurrency.version.Category)3 InvalidBidException (org.jpwh.model.concurrency.version.InvalidBidException)3 TestData (org.jpwh.shared.util.TestData)3 Bid (org.jpwh.model.concurrency.version.Bid)2 Connection (java.sql.Connection)1 SQLException (java.sql.SQLException)1 HashMap (java.util.HashMap)1 LockTimeoutException (javax.persistence.LockTimeoutException)1 PersistenceException (javax.persistence.PersistenceException)1 PessimisticLockException (javax.persistence.PessimisticLockException)1 Session (org.hibernate.Session)1