Использование динамических прокси для централизации кода 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.
Он экономит много стандартного кодирования с интуитивно понятным сопоставлением с базовой структурой доступа к данным и обеспечивает очень легкое моделирование уровня доступа к данным.
Итак, я определенно "одобряю" использование прокси.