简体   繁体   English

如何使用RMI远程方法自动化Hibernate样板

[英]How to automate Hibernate boilerplate in RMI remote methods

I have an RMI class that accepts remote calls from clients. 我有一个RMI类,可以接受来自客户端的远程调用。

This class uses Hibernate to load entities and perform some business logic, in general read-only. 此类使用Hibernate加载实体并执行一些业务逻辑(通常为只读)。

Currently most of the remote methods bodies look like that : 当前,大多数远程方法主体如下所示:

try {
    HibernateUtil.currentSession().beginTransaction();

    //load entities, do some business logic...

} catch (HibernateException e) {
   logger.error("Hibernate problem...", e);
   throw e;
} catch (other exceptions...) {
   logger.error("other problem happened...", e);
   throw e;
} finally {
   HibernateUtil.currentSession().getTransaction().rollback(); //this because it's read-only, we make sure we don't commit anything
   HibernateUtil.currentSession().close();
}

I would like to know if there is some pattern that I could (relatively easily) implement in order to automatically have this "try to open session/catch hibernate exception/finally close hibernate resources" behavior without having to code it in every method. 我想知道是否可以(相对容易)实现某种模式,以便自动具有这种“尝试打开会话/捕获休眠异常/最终关闭休眠资源”的行为,而不必在每种方法中都进行编码。

Something similar to "open session in view" that is used in webapps, but that could be applied to remotr RMI method calls instead of HTTP requests. 与webapp中使用的“视图中打开会话”类似,但可用于远程RMI方法调用而不是HTTP请求。

Ideally I would like to be able to still call the methods directly, not to use some reflexion passing method names as strings. 理想情况下,我希望仍然能够直接调用方法,而不是使用一些反射将方法名称作为字符串传递。

I would suggest you to use spring+hibernate stack. 我建议您使用spring + hibernate堆栈。 This saves us a lot of repeatable code which I guess you are looking for. 这为我们节省了很多我认为您正在寻找的可重复代码。 Please check this link . 请检查此链接 Its actually an example of web application but same can be use for a standalone application as well. 它实际上是Web应用程序的示例,但也可以用于独立应用程序

All i wanted was a "quick and clean" solution, if possible, so no new framework for now (I might use Spring+Hibernate stack later on though). 如果可能的话,我想要的只是一个“快速且干净”的解决方案,因此暂时没有新的框架(不过以后我可能会使用Spring + Hibernate堆栈)。

So I ended up using a "quick-and-not-so-dirty" solution involving a variant of the "Command" pattern, where the hibernate calls are encapsulated inside anonymous inner classes implementing my generic Command interface, and the command executer wraps the call with the Hibernate session and exception handling. 因此,我最终使用了涉及“命令”模式的变体的“快速而又不那么肮脏”的解决方案,其中,休眠调用封装在实现我的通用Command接口的匿名内部类中,而命令执行程序包装了使用Hibernate会话和异常处理进行调用。 The generic bit is in order to have different return value types for the execute method. 通用位是为了使execute方法具有不同的返回值类型。

I am not 100% satisfied with this solution since it still implies some boilerplate code wrapped around my business logic (I am especially unhappy about the explicit casting needed for the return value) and it makes it slightly more complicated to understand and debug. 我对这个解决方案并不满意,因为它仍然隐含着围绕我的业务逻辑包装的一些样板代码(我对返回值所需的显式转换特别不满意),并使它的理解和调试稍微复杂一些。

However the gain in repetitive code is still significant (from about 10 lines to 3-4 lines per method), and more importantly the Hibernate handling logic is concentrated in one class, so it can be changed easily there if needed and it's less error-prone. 但是,重复代码的收益仍然很可观(每种方法从大约10行到3-4行),更重要的是,Hibernate处理逻辑集中在一个类中,因此可以在需要时轻松地对其进行更改,并且错误更少-易于。

Here is some of the code : 这是一些代码:

The Command interface : 命令界面:

public interface HibernateCommand<T> {
    public T execute(Object... args) throws Exception; 
}

The Executer : 执行者:

public class HibernateCommandExecuter {

    private static final Logger logger = Logger.getLogger(HibernateCommandExecuter.class);

    public static Object executeCommand(HibernateCommand<?> command, boolean commit, Object... args) throws RemoteException{
        try {
            HibernateUtil.currentSession().beginTransaction();

            return command.execute(args);

        } catch (HibernateException e) {
            logger.error("Hibernate problem : ", e);
            throw new RemoteException(e.getMessage());
        }catch(Exception e){
            throw new RemoteException(e.getMessage(), e);
        }
        finally {
            try{
                if(commit){
                    HibernateUtil.currentSession().getTransaction().commit();
                }else{
                    HibernateUtil.currentSession().getTransaction().rollback();
                }
                HibernateUtil.currentSession().close();
            }catch(HibernateException e){
                logger.error("Error while trying to clean up Hibernate context :", e);
            }
        }
    }   
}

Sample use in a remotely called method (but it could be used locally also) : 在远程调用的方法中使用示例(但也可以在本地使用):

@Override
    public AbstractTicketingClientDTO doSomethingRemotely(final Client client) throws RemoteException {
        return (MyDTO) HibernateCommandExecuter.executeCommand(new HibernateCommand<MyDTO>() {
            public AbstractTicketingClientDTO execute(Object...args) throws Exception{
                MyDTO dto = someService.someBusinessmethod(client);
                return dto;
            }
        },false);
    }

Note how the client argument is declared final, so it can be referrenced inside the inner class. 请注意如何将client参数声明为final,以便可以在内部类中引用它。 If not possible to declare final, it could be passed as parameter to the executeCommand method. 如果无法声明final,则可以将其作为参数传递给executeCommand方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM