Search in sources :

Example 1 with Category

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

the class Locking method findLock.

@Test
public void findLock() throws Exception {
    final ConcurrencyTestData testData = storeCategoriesAndItems();
    Long CATEGORY_ID = testData.categories.getFirstId();
    UserTransaction tx = TM.getUserTransaction();
    try {
        tx.begin();
        EntityManager em = JPA.createEntityManager();
        Map<String, Object> hints = new HashMap<String, Object>();
        hints.put("javax.persistence.lock.timeout", 5000);
        // Executes a SELECT .. FOR UPDATE WAIT 5000 if supported by dialect
        Category category = em.find(Category.class, CATEGORY_ID, LockModeType.PESSIMISTIC_WRITE, hints);
        category.setName("New Name");
        tx.commit();
        em.close();
    } finally {
        TM.rollback();
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) EntityManager(javax.persistence.EntityManager) Category(org.jpwh.model.concurrency.version.Category) HashMap(java.util.HashMap) Test(org.testng.annotations.Test)

Example 2 with Category

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

the class Versioning method manualVersionChecking.

// TODO This throws the wrong exception!
// @Test(expectedExceptions = OptimisticLockException.class)
@Test(expectedExceptions = org.hibernate.OptimisticLockException.class)
public void manualVersionChecking() throws Throwable {
    final ConcurrencyTestData testData = storeCategoriesAndItems();
    Long[] CATEGORIES = testData.categories.identifiers;
    UserTransaction tx = TM.getUserTransaction();
    try {
        tx.begin();
        EntityManager em = JPA.createEntityManager();
        BigDecimal totalPrice = new BigDecimal(0);
        for (Long categoryId : CATEGORIES) {
            /* 
                   For each <code>Category</code>, query all <code>Item</code> instances with
                   an <code>OPTIMISTIC</code> lock mode. Hibernate now knows it has to
                   check each <code>Item</code> at flush time.
                 */
            List<Item> items = em.createQuery("select i from Item i where i.category.id = :catId").setLockMode(LockModeType.OPTIMISTIC).setParameter("catId", categoryId).getResultList();
            for (Item item : items) totalPrice = totalPrice.add(item.getBuyNowPrice());
            // Now a concurrent transaction will move an item to another category
            if (categoryId.equals(testData.categories.getFirstId())) {
                Executors.newSingleThreadExecutor().submit(new Callable<Object>() {

                    @Override
                    public Object call() throws Exception {
                        UserTransaction tx = TM.getUserTransaction();
                        try {
                            tx.begin();
                            EntityManager em = JPA.createEntityManager();
                            // Moving the first item from the first category into the last category
                            List<Item> items = em.createQuery("select i from Item i where i.category.id = :catId").setParameter("catId", testData.categories.getFirstId()).getResultList();
                            Category lastCategory = em.getReference(Category.class, testData.categories.getLastId());
                            items.iterator().next().setCategory(lastCategory);
                            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();
            }
        }
        /* 
               For each <code>Item</code> loaded earlier with the locking query, Hibernate will
               now execute a <code>SELECT</code> during flushing. It checks if the database
               version of each <code>ITEM</code> row is still the same as when it was loaded
               earlier. If any <code>ITEM</code> row has a different version, or the row doesn't
               exist anymore, an <code>OptimisticLockException</code> will be thrown.
             */
        tx.commit();
        em.close();
        assertEquals(totalPrice.toString(), "108.00");
    } catch (Exception ex) {
        throw unwrapCauseOfType(ex, org.hibernate.OptimisticLockException.class);
    } finally {
        TM.rollback();
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) Category(org.jpwh.model.concurrency.version.Category) OptimisticLockException(javax.persistence.OptimisticLockException) BigDecimal(java.math.BigDecimal) Callable(java.util.concurrent.Callable) OptimisticLockException(javax.persistence.OptimisticLockException) NoResultException(javax.persistence.NoResultException) InvalidBidException(org.jpwh.model.concurrency.version.InvalidBidException) Item(org.jpwh.model.concurrency.version.Item) EntityManager(javax.persistence.EntityManager) JPATest(org.jpwh.env.JPATest) Test(org.testng.annotations.Test)

Example 3 with Category

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

the class Locking method pessimisticReadWrite.

@Test
public void pessimisticReadWrite() throws Exception {
    final ConcurrencyTestData testData = storeCategoriesAndItems();
    Long[] CATEGORIES = testData.categories.identifiers;
    UserTransaction tx = TM.getUserTransaction();
    try {
        tx.begin();
        EntityManager em = JPA.createEntityManager();
        BigDecimal totalPrice = new BigDecimal(0);
        for (Long categoryId : CATEGORIES) {
            /* 
                   For each <code>Category</code>, query all <code>Item</code> instances in
                   <code>PESSIMISTIC_READ</code> lock mode. Hibernate will lock the rows in
                   the database with the SQL query. If possible, wait for 5 seconds if some
                   other transaction already holds a conflicting lock. If the lock can't
                   be obtained, the query throws an exception.
                 */
            List<Item> items = em.createQuery("select i from Item i where i.category.id = :catId").setLockMode(LockModeType.PESSIMISTIC_READ).setHint("javax.persistence.lock.timeout", 5000).setParameter("catId", categoryId).getResultList();
            /* 
                   If the query returns successfully, you know that you hold an exclusive lock
                   on the data and no other transaction can access it with an exclusive lock or
                   modify it until this transaction commits.
                 */
            for (Item item : items) totalPrice = totalPrice.add(item.getBuyNowPrice());
            // read or write locks, only exclusive locks.
            if (categoryId.equals(testData.categories.getFirstId())) {
                Executors.newSingleThreadExecutor().submit(new Callable<Object>() {

                    @Override
                    public Object call() throws Exception {
                        UserTransaction tx = TM.getUserTransaction();
                        try {
                            tx.begin();
                            EntityManager em = JPA.createEntityManager();
                            // connection/session.
                            if (TM.databaseProduct.equals(DatabaseProduct.POSTGRESQL)) {
                                em.unwrap(Session.class).doWork(new Work() {

                                    @Override
                                    public void execute(Connection connection) throws SQLException {
                                        connection.createStatement().execute("set statement_timeout = 5000");
                                    }
                                });
                            }
                            // can set a timeout for the whole connection/session.
                            if (TM.databaseProduct.equals(DatabaseProduct.MYSQL)) {
                                em.unwrap(Session.class).doWork(new Work() {

                                    @Override
                                    public void execute(Connection connection) throws SQLException {
                                        connection.createStatement().execute("set innodb_lock_wait_timeout = 5;");
                                    }
                                });
                            }
                            // Moving the first item from the first category into the last category
                            // This query should fail as someone else holds a lock on the rows.
                            List<Item> items = em.createQuery("select i from Item i where i.category.id = :catId").setParameter("catId", testData.categories.getFirstId()).setLockMode(// Prevent concurrent access
                            LockModeType.PESSIMISTIC_WRITE).setHint("javax.persistence.lock.timeout", // Only works on Oracle...
                            5000).getResultList();
                            Category lastCategory = em.getReference(Category.class, testData.categories.getLastId());
                            items.iterator().next().setCategory(lastCategory);
                            tx.commit();
                            em.close();
                        } catch (Exception ex) {
                            // This should fail, as the data is already locked!
                            TM.rollback();
                            if (TM.databaseProduct.equals(DatabaseProduct.POSTGRESQL)) {
                                // A statement timeout on PostgreSQL doesn't produce a specific exception
                                assertTrue(ex instanceof PersistenceException);
                            } else if (TM.databaseProduct.equals(DatabaseProduct.MYSQL)) {
                                // On MySQL we get a LockTimeoutException
                                assertTrue(ex instanceof LockTimeoutException);
                            } else {
                                // On H2 and Oracle we get a PessimisticLockException
                                assertTrue(ex instanceof PessimisticLockException);
                            }
                        }
                        return null;
                    }
                }).get();
            }
        }
        /* 
               Our locks will be released after commit, when the transaction completes.
             */
        tx.commit();
        em.close();
        assertEquals(totalPrice.compareTo(new BigDecimal("108")), 0);
    } finally {
        TM.rollback();
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) Category(org.jpwh.model.concurrency.version.Category) SQLException(java.sql.SQLException) Connection(java.sql.Connection) BigDecimal(java.math.BigDecimal) Callable(java.util.concurrent.Callable) LockTimeoutException(javax.persistence.LockTimeoutException) PessimisticLockException(javax.persistence.PessimisticLockException) SQLException(java.sql.SQLException) PersistenceException(javax.persistence.PersistenceException) PessimisticLockException(javax.persistence.PessimisticLockException) Item(org.jpwh.model.concurrency.version.Item) EntityManager(javax.persistence.EntityManager) Work(org.hibernate.jdbc.Work) PersistenceException(javax.persistence.PersistenceException) LockTimeoutException(javax.persistence.LockTimeoutException) Session(org.hibernate.Session) Test(org.testng.annotations.Test)

Example 4 with Category

use of org.jpwh.model.concurrency.version.Category 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)

Aggregations

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