Hibernate envers. Подмена ID пользователя совершившего изменение

image

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

Об аудировании в Hibernate написано уже немало. Я хочу рассказать о решении не совсем стандартной задачи — записи в таблицу ревизий ID любого пользователя, назначаемого непосредственно перед операцией записи сущности в базу данных. Стандартное решение, предложенное в официальной документации — использование ID пользователя, сохраненного в сессионном компоненте. Но возможна ситуация, когда ID пользователя необходимо подменить. Пример: пользователь совершает операции через взаимодействие с сервером телефонии посредством DTMF сигналов. В данном случае сессию создавать вообще не нужно. Я долго искал решение в интернете, но так ничего и не нашёл, поэтому предлагаю вашему вниманию свою версию. Возможно кому-то из новичков, вроде меня, она окажется полезной.

Почитав документацию, я понял, что аудирование в Hibernate основано на перехватчиках. Это означает, что поток, совершающий обновление сущности в базе данных, отвечает за аудирование — это уже кое-что.

Попробуем извлечь из этого пользу. Создаем stateless компонент, в котором будет храниться статическая map c парами: ID потока — ID пользователя. Метод start добавляет в map ID пользователя (переданного параметром) и ID текущего потока, затем запускает в новой транзакции метод, выполняющий необходимые действия, дожидается окончания метода (транзакции) и удаляет ID потока и пользователя из map.

@Stateless @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public class FakeOwnerTransaction {          @Inject     private Provider<FakeOwnerTransaction> providerFakeOwnerNewTransaction;          private static ConcurrentHashMap<Long, Long> threadToPersonID = new ConcurrentHashMap<Long, Long>();          @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)     public void newCMT(Runnable action) {         action.run();     }          public void start(Long personID, Runnable action) {         threadToStaffID.put(Thread.currentThread().getId(), personID);         try {             providerFakeOwnerNewTransaction.get().newCMT(action);         } finally {             threadToStaffID.remove(Thread.currentThread().getId());         }     }          public static Long getFakeChanger() {         return threadToStaffID.get(Thread.currentThread().getId());     } } 

Теперь посмотрим, как будет выглядеть реализация RevisionListener. Если текущий поток ассоциирован с ID пользователя, используем этот ID, иначе берем ID пользователя из сессионного компонента UserManager.

public class Audition implements RevisionListener {          @Override     public void newRevision(Revinfo revinfo) {         Long personID = FakeOwnerTransaction.getFakeChanger();                  if (personID == null) {             UserManager userManager = SystemUtils.lookup(JNDI_NAME_PREFIX, UserManager.class);             personID = userManager.getPersonID();         }         revinfo.setPersonID(personID);     } } 

Ну и напоследок попробуем сделать изменение в базе данных, используя ID указанного пользователя.

FakeOwnerTransaction fakeOwnerTransaction = SystemUtils.lookup(JNDI_NAME_PREFIX, FakeOwnerTransaction.class); fakeOwnerTransaction.start(getPersonID(), new Runnable() {                          @Override             public void run() {                 Dao dao = SystemUtils.lookup(JNDI_NAME_PREFIX, Dao.class);                 dao.add(new Person(“Smirmov”));             }         }); 

На этом у меня всё. Если у кого-то есть более интересные решения или критика — добро пожаловать в комментарии.

ссылка на оригинал статьи http://habrahabr.ru/post/275431/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *