1

I need to have a atomic counter with ObjectDB but the following code doesn't work as I expected:

    final EntityManagerFactory emf = Persistence.createEntityManagerFactory("test.odb");
    EntityManager em = emf.createEntityManager();
    Point p = new Point(0, 0);
    em.getTransaction().begin();
    em.persist(p);
    em.getTransaction().commit();
    em.close();
    final CountDownLatch l = new CountDownLatch(100);
    for (int i = 0; i < 100; i++) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                EntityManager em = emf.createEntityManager();
                em.getTransaction().begin();
                //Query q = em.createQuery("UPDATE Point SET x = x + 1");
                Query query = em.createQuery("UPDATE Point SET x = x + 1");
                query.executeUpdate();
                em.getTransaction().commit();
                em.close();
                l.countDown();                    
            }
        });
        t.start();
    }
    l.await();
    em = emf.createEntityManager();
    TypedQuery<Point> myquery = em.createQuery("SELECT p from Point p", Point.class);
    List<Point> results = myquery.getResultList();
    System.out.println("X coordiate is: " + results.get(0).getX());
    em.close();

It should have printed out X coordinate is 100. But in reality, it doesn't.

What is wrong with my code?

Ashot Karakhanyan
  • 2,734
  • 3
  • 21
  • 28
user161904
  • 31
  • 1

2 Answers2

0

You can fix your code by one of the following ways:

Synchronize your update queries, so they will be executed sequently, rather than concurrently:

synchronized (lock) {
    em.createQuery("UPDATE Point SET x = x + 1").executeUpdate();
}

Your lock object must be one object that is shared by all the threads.

Or, use ObjectDB / JPA locking, by setting a pessimistic locking timeout, e.g. by:

Map<String, Integer> properties =
    Collections.singletonMap("javax.persistence.lock.timeout", 1000);
EntityManagerFactory emf =
    Persistence.createEntityManagerFactory(
        "objectdb:$objectdb/db/test.tmp;drop", properties);

and then replacing the UPDATE query with a retrieval with a lock and an update:

Point point = em.find(Point.class, 1, LockModeType.PESSIMISTIC_WRITE);
point.setX(point.getX() + 1);
ObjectDB
  • 1,294
  • 7
  • 8
0

Better create a DAO class for Point and wrap the persist(), merge() ... functions with synchronized functions instead.

Alternatively an AtomicInteger with getAndIncrement() should solve your problem without the need for synchronization and locks.