Использование динамических прокси для централизации кода JPA

На самом деле, это не вопрос, но на самом деле мне нужно ваше мнение по этому вопросу... Я разместил его пост здесь, потому что я знаю, что вы всегда активны, поэтому, пожалуйста, не считайте это плохим вопросом и поделитесь со мной своим мнением.

Я использовал динамические прокси Java для централизации кода JPA, который я использовал в автономном режиме, и вот код динамического прокси:

package com.forat.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import com.forat.service.exceptions.DAOException;

/**
 * Example of usage :
 * <pre>
 * OnlineFromService onfromService = 
 *            (OnlineFromService) DAOProxy.newInstance(new OnlineFormServiceImpl());
 *        try {
 *            Student s = new Student();
 *            s.setName("Mohammed");
 *            s.setNationalNumber("123456");
 *            onfromService.addStudent(s);    
 *        }catch (Exception ex) {
 *            System.out.println(ex.getMessage());
 *        }
 *</pre>
 * @author mohammed hewedy
 *
 */
public class DAOProxy implements InvocationHandler{

    private Object object;
    private Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    private DAOProxy(Object object) {
        this.object = object;
    }

    public static Object newInstance(Object object) {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), 
                    object.getClass().getInterfaces(), new DAOProxy(object));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        EntityManagerFactory emf = null;
        EntityManager em = null;
        EntityTransaction et = null;
        Object result = null;
        try {
            emf = Persistence.createEntityManagerFactory(Constants.UNIT_NAME);
            em = emf.createEntityManager();;
            Method entityManagerSetter = object.getClass().
                getDeclaredMethod(Constants.ENTITY_MANAGER_SETTER_METHOD, EntityManager.class);
            entityManagerSetter.invoke(object, em);
            et = em.getTransaction();
            et.begin();
            result = method.invoke(object, args);
            et.commit();
            return result;
        }catch (Exception ex) {
            et.rollback();
            Throwable cause = ex.getCause();
            logger.log(Level.SEVERE, cause.getMessage());
            if (cause instanceof DAOException)
                throw new DAOException(cause.getMessage(), cause);
            else
                throw new RuntimeException(cause.getMessage(), cause);
        }finally {
            em.close();
            emf.close();
        }
    }
}

А вот ссылка, которая содержит больше информации ( http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html)

Поэтому, пожалуйста, дайте мне свое мнение.

Благодарю.

2 ответа

Решение

Итак, вы инкапсулировали логику разграничения транзакций в одном месте и используете динамический прокси-сервер для улучшения существующих сервисов с помощью управления транзакциями и сокращения стандартного кода, верно?

Звучит довольно хорошо для меня. На самом деле то, что делают контейнеры, такие как Spring или EJB, когда мы говорим о декларативном разграничении транзакций, очень похоже. С точки зрения реализации вы можете сделать это с помощью динамического прокси или инструментария байтового кода, или даже использовать AspectJ. Однажды я сделал нечто очень похожее для крошечной среды тестирования. Вот сообщение в блоге об этом.

Хитрые части, которые я вижу:

1) Откат только. Согласно спецификации JPA, транзакция объекта может быть помечена как " только откат ". Такая транзакция никогда не может совершить. Поэтому я чувствую, что вы должны проверить это между этими двумя строками:

result = method.invoke(object, args);
et.commit();

2) Повторный вход. Большинство систем с декларативной транзакцией реализуют семантику, в которой транзакция запускается, только если она еще не активна (см. "Обязательный" в этом списке аннотаций EJB). Похоже, вы должны проверить с isActive это в твоей логике.

3) Обработка исключений. Будьте очень осторожны с распространением исключений в динамическом прокси. Предполагается, что прокси должен быть максимально прозрачным для клиента. Если исключение, кроме DAOException утечка из DAO, прокси превратит его в RuntimeException, Не кажется оптимальным для меня. Также не путайте исключение, потому что invoke произошел сбой, и исключение обернуто вызовом, который, я думаю, вы должны перебросить как есть:

catch ( InvocationTargetException e )
{
     Throwable nested = e.getTargetException();
     throw nested;
}

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

Я использовал что-то подобное в прошлом, но закодировал в Hibernate API (это было до JPA). Доступ к данным для большинства типов DAO осуществлялся через интерфейс, названный в честь типа объекта, например CustomerPersistence, для управления экземплярами Customer. Такие методы, как findXXX, сопоставляются с именованными запросами, а имена параметров в методе сопоставляются с параметрами в запросе.

Реализация интерфейсов представляла собой прокси-серверы, которые использовали имя интерфейса, имена методов, имена параметров и т. Д. Для вызова соответствующих методов в hibernate API.

Он экономит много стандартного кодирования с интуитивно понятным сопоставлением с базовой структурой доступа к данным и обеспечивает очень легкое моделирование уровня доступа к данным.

Итак, я определенно "одобряю" использование прокси.

Другие вопросы по тегам