Получить значение столбца БД после его обновления - PullRequest
0 голосов
/ 05 июня 2018

Извините заранее за длинный пост.Я работаю с Java WebApplication, который использует Spring (2.0, я знаю ...) и Jpa с Hibernateimplementation (используя hibernate 4.1 и hibernate-jpa-2.0.jar).У меня возникают проблемы при получении значения столбца из таблицы БД (MySql 5) после его обновления.Это моя ситуация (упрощенно, но в этом ее суть):

Таблица KcUser :

    Id:Long (primary key)
    Name:String
    .
    .
    .
    Contract_Id: Long (foreign key, references KcContract.Id)

Таблица KcContract :

    Id: Long (primary Key)
    ColA
    .
    .
    ColX

На моем сервере у меня есть что-то вроде этого:

MyController {
    myService.doSomething();    
}


MyService {

    private EntityManager myEntityManager;

    @Transactional(readOnly=true)
    public void doSomething() {
        List<Long> IDs = firstFetch();   // retrieves some users IDs querying the KcContract table
        doUpdate(IDs);                   // updates a column on KcUser rows that matches the IDs retrieved by the previous query
        secondFecth(IDs);                // finally retrieves KcUser rows <-- here the returned rows contains the old value and not the new one i updated in the previous method
    }

    @Transactional(readOnly=true)
    private List<Long> firstFetch() {
        List<Long> userIDs = myEntityManager.createQuery("select c.id from KcContract c" ).getResultList();   // this is not the actual query, there are some conditions in the where clause but you get the idea

        return userIDs;
    }

    @Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW)
    private void doUpdate(List<Long> IDs) {
        Query hql =  myEntityManager().createQuery("update KcUser t set t.name='newValue' WHERE t.contract.id IN (:list)").setParameter("list", IDs);   
        int howMany = hql.executeUpdate();
        System.out.println("HOW MANY: "+howMany); // howMany is correct, with the number of updated rows in DB

        Query select = getEntityManager().createQuery("select t from KcUser t WHERE t.contract.id IN (:list)" ).setParameter("list", activeContractIDs);
        List<KcUser> users = select.getResultList();
        System.out.println("users: "+users.get(0).getName()); //correct, newValue!
    }

    private void secondFetch(List<Long> IDs) {          
        List<KcUser> users = myEntityManager.createQuery("from KcUser t WHERE t.contract.id IN (:list)").setParameter("list", IDs).getResultList()

        for(KcUser u : users) {
            myEntityManager.refresh(u);
            String name = u.getName(); // still oldValue!
        }
    }
}

Странно то, что если я прокомментирую вызов первого метода (myService.firstFetch()) и вызову два других методас постоянным списком идентификаторов, я получаю правильное новое значение KcUser.name в secondFetch() метод.

Я не очень опытен с Jpa и Hibernate, но я подумал, что это может быть проблема с кешем, поэтому я попытался:

  • , используя myEntityManager.flush () после обновления
  • очистка кэша с помощью myEntityManager.clear () и myEntityManager.getEntityManagerFactory (). EvictAll ();
  • очистка кэша с помощью hibernate Session.clear ()
  • с использованием myEntityManager.обновить сущности KcUser
  • с использованием собственных запросов (myEntityManager.createNativeQuery ("")), которые, на мой взгляд, не должны включать в себя кеш

Ничего из этого не сработало, и я всегда получал возвратстарое значение KcUser.name в методе secondFetch().

До сих пор работали только:

  • , делающие метод firstFetch() общедоступным и перемещая его вызовза пределами myService.doSomething (), делая что-то подобное в MyController:

    List<Long> IDs = myService.firstFetch(); myService.doSomething(IDs);

  • с использованием нового EntityManager в secondFetch(), делая что-токак это:

EntityManager newEntityManager = myEntityManager.getEntityManagerFactory().createEntityManager();

и использование его для выполнения последующего запроса для извлечения пользователей из БД

Используя любой из двух последних методов, второй выбор работает нормально, и я получаю пользователейс обновленным значением в столбце «имя».Но я хотел бы знать, что на самом деле происходит и почему ничего другого не сработало: если это на самом деле проблема с кешем, просто .clear() или .refresh() должны были бы работать, я думаю.Или, может быть, я совершенно не прав, и это вообще не связано с кешем, но потом я немного теряюсь в том, что на самом деле может происходить.

Боюсь, может быть что-то не так в том, как мы используем hibernate / jpa, что может укусить нас в будущем.Любая идея, пожалуйста?Скажите, если вам нужно больше деталей и спасибо за вашу помощь.

1 Ответ

0 голосов
/ 05 июня 2018

Действия выполняются в следующем порядке:

  1. Открывается только для чтения транзакция А.
  2. Первая выборка (транзакция A)
  3. Открывается не доступная только для чтения транзакция B
  4. Обновление (транзакция B)
  5. Транзакция B закрывается
  6. Вторая выборка (транзакция A)
  7. Транзакция A закрывается

Транзакция A доступна только для чтения.Все последующие запросы в этой транзакции видят только изменения, которые были зафиксированы до начала транзакции - ваше обновление было выполнено после нее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...