使用动态代理集中 JPA 代码

发布于 2024-08-28 06:14:40 字数 2992 浏览 8 评论 0原文

事实上,这不是一个问题,但我真的需要你对某件事的意见...... 我把他的帖子放在这里是因为我知道你总是很活跃,所以请不要认为这是一个坏问题并与我分享你的意见。

我使用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

所以,请给我你的意见。

谢谢。

Actually, This is not a question but really I need your opinions in a matter...
I put his post here because I know you always active, so please don't consider this a bad question and share me your opinions.

I've used Java dynamic proxies to Centralize The code of JPA that I used in a standalone mode, and Here's the dynamic proxy code:

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();
        }
    }
}

And here's the link that contains more info (http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html)

So, Please give me your opinions.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

在你怀里撒娇 2024-09-04 06:14:40

因此,您已将事务划分逻辑封装在一处,并使用动态代理通过事务管理来增强现有服务并减少样板代码,对吧?

对我来说听起来还不错。实际上,当我们谈到 声明式事务划分非常相似。在实现方面,您可以使用动态代理或字节码工具,甚至使用 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 规范和动态代理异常处理的所有细节,但有一些棘手的情况)。此类代码可能隐藏细微的错误,因此值得花时间使其防弹。

So you've encapsulated the transaction demarcation logic in one place and use dynamic proxy to enhance existing services with transaction management and reduce boilerplate code, right?

The sounds a rather OK to me. Actually what containers such as Spring, or EJB do when we speak of declarative transaction demarcation is very similar. Implementation-wise, you can do it with dynamic proxy, or byte code instrumentation, or even use AspectJ. I did something very similar once for a tiny testing framework once. Here is a blog post about it.

The tricky parts that I see are:

1) Rollback only. As per JPA spec, an entity transaction can be flagged as "rollback only". Such a transaction can never commit. So I feel like you should check that between these two lines:

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

2) Re-entrancy. Most system that have declarative transaction implement a semantics in which a transaction is started only if there isn't one already active (See "Required" in this list of EJB annotations). Looks like you should maybe check with isActive that in your logic.

3) Exception handling. Be very careful with the exception propagation in dynamic proxy. The proxy is supposed to be transparent for the client as much as possible. If an exception other than DAOException leaks out of the DAO, the proxy will transform it into a RuntimeException. Doesn't sound optimal to me. Also don't confuse the exception because invoke failed, and the exception wrapped by the invocation, that I think you should re-throw as-is:

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

Conclusion: the idea to use dynamic proxy in this scenario sounds OK to me. But I suspect there are a few stuffs to double-check in your code (I don't remember all the details of the JPA specs and exception handling with dynamic proxy, but there are some tricky cases). This kind of code can hide subtle bugs, so it's worth taking time to make it bullet-proof.

谎言月老 2024-09-04 06:14:40

我过去使用过类似的东西,但编码为 hibernate API(这是 JPA 之前的版本)。大多数类型的 DAO 的数据访问由以对象类型命名的接口进行管理,例如用于管理客户实例的 CustomerPersistence。 findXXX 等方法映射到命名查询,方法中的参数名称映射到查询中的参数。

接口的实现是代理,它使用接口名称、方法名称、参数名称等来调用 hibernate API 中的适当方法。

它节省了大量的样板代码,具有到底层数据访问框架的直观映射,并且可以非常轻松地模拟数据访问层。

所以,我绝对对使用代理“竖起大拇指”。

I've used something similar in the past, but coded to the hibernate API (this was pre-JPA). Data access for most types of DAO was managed by an interface named after the object type, E.g. CustomerPersistence for managing Customer instnaces. Methods such as findXXX mapped to named queries, with parameter names in the method mapped to parameters in the query.

The implementation of the interfaces were proxies, which used the interface name, method names, parameter names etc.. to invoke appropriate methods in the hibernate API.

It saves a lot of boilerplate coding, with an intuitive mapping to the underlying data access framework, and makes for very easy mocking of the data access layer.

So, I'm definitely "thumbs up" on using proxies.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文