Java Tomcat、JAX-WS、Hibernate——缺陷和解决方案?

Java Tomcat、JAX-WS、Hibernate——缺陷和解决方案?,java,web-services,hibernate,tomcat,jaxb,Java,Web Services,Hibernate,Tomcat,Jaxb,我目前正在开发一个客户端(java/swing)服务器(tomcat/hibernate/jax-ws)应用程序,该应用程序需要许多数据库操作,并且应该能够在服务器端执行长时间运行的后台任务。我选择这种设置主要是为了更好地重用代码 然而,可能还有许多其他人也面临着一些问题,并找到了解决方案: 最大的问题之一是延迟加载与jax-ws。有一些可行的解决方案,比如覆盖jax-ws访问器()来解决这个问题,比如用null替换hibernate的代理 现在我面临着这个例子描述的新问题: 实体“客户”位于“

我目前正在开发一个客户端(java/swing)服务器(tomcat/hibernate/jax-ws)应用程序,该应用程序需要许多数据库操作,并且应该能够在服务器端执行长时间运行的后台任务。我选择这种设置主要是为了更好地重用代码

然而,可能还有许多其他人也面临着一些问题,并找到了解决方案:

最大的问题之一是延迟加载与jax-ws。有一些可行的解决方案,比如覆盖jax-ws访问器()来解决这个问题,比如用null替换hibernate的代理

现在我面临着这个例子描述的新问题:

实体“客户”位于“国家”内,因此:n:1关系

“customer”中的“country”标记为延迟加载,以避免不必要的数据库通信。当客户端UI想要列出客户(这里不需要国家)时,jax-ws访问器中的国家代理将替换为null,一切正常。 但是,在编辑客户时,(我认为)我必须加入该国,即使不查看/更改该国。否则,当通过jax-ws发送到客户机时,它的代理将被null替换,然后发送回服务器,并提交(使用null)到数据库中。此后,我的客户->国家协会丢失

可能有以下几种解决方案:

  • 将国家/地区标记为“optional=false”,当我事先忘记加入该国家/地区,然后尝试保存客户时触发异常。使用这种方法,我必须始终连接所有引用,即使它们不是编辑过程的一部分。需要“optional=true”的引用将以静默方式传递,编码错误可能会破坏数据库
  • 在jax-ws访问器中不使用null替换代理,而是使用其他一些伪类,当从客户端发送回服务器时,这些伪类将被原始代理替换。但我不确定这是否可行
  • 在客户端中使用hibernate并直接连接到数据库,使用jax ws仅用于非数据库交互
  • 编写一些代码,通过发送相应的jax-ws请求(在有人要求这样做的地方再也找不到StackOverflow链接),允许在客户机内延迟加载(必要时)。完全感觉像是重新创造冬眠
对于此类应用程序,是否有其他解决方案、建议、最佳做法和更好的设置


提前谢谢

您描述的问题并不局限于“JAX-WS到Hibernate”场景。在其他szenarios中,您也将面临这个“空值”问题

一种解决方案是“DIY合并模式”:

  • 将实体从客户端发送到服务器
  • 在服务器上,使用收到的ID调用“EntityManager.find”以查找现有实体
  • 现在我们自己复制一下这个州。如果EntityManager.find返回null,则其新->仅保留接收到的对象
  • 例如:

    Customer serverCustomer = dao.findById(receivedCustomer.getId());
    if(serverCustomer == null) {
        dao.persist(clientCustomer);
    } else {
        serverCustomer.setName(receivedCustomer.getName());
        serverCustomer.setDate(receivedCustomer.getDate());
        // ... all other fields, except "Country"
        if (receivedCustomer.getCountry() != null) {
           // Country keeps its server state if no new data
           serverCustomer.setDate(receivedCustomer.getCountry());
        }
    }
    

    与传输数据的方式相比,存储数据的方式显然有所不同。因此,不使用相同的对象实例进行传输和存储可能是有意义的

    其中一个解决方案是为DTO和实体使用不同的类。您存储实体,但传输DTO。这需要额外的努力来实现DTO和映射DTO实体,但这样可以实现清晰的层分离,并且从长远来看可能更加高效(从努力的角度来看)。您可以使用Dozer和likes在DTO和实体之间进行映射

    另一种方法是不使用不同的类,而是使用对象的不同实例进行传输和存储。这可能与@VinZ答案类似。您可以将源对象中的数据“合并”到目标对象中。不久前,我写了一篇文章来生成这种合并方法,发现这种方法在不同的用例中非常有用。
    与DTO相比,使用这种方法可以节省大量工作,但在类级别上没有层分离

    我个人会选择发展完善的实体结构和为转移而优化的额外DTO。我还尝试以某种方式自动生成合并/复制方法,以避免手动编写。可能是JAXB插件。我喜欢编写JAXB插件,所以如果可以用JAXB插件解决某些问题,我会用JAXB插件解决


    希望这有帮助。

    这是一个非常具体的解决方案,只有在我根本不需要更改参考的情况下才可行。然而,在某些情况下,我必须能够更改它,“null”甚至可能是一个有效的引用(可选引用)。“null-due:coder忘记加入”和“null-due:we really want null here”之间肯定有某种明显的区别。如果您只需要使用新数据进行更改的能力,那么您没有问题。。。见更新的Answare。事实上,如果您需要删除客户国家/地区关系的能力,那么您就会遇到问题。您需要更多信息。我建议将客户的传输对象定义为“有效负载”,并提供一些关于它的附加信息。就像一个要删除字段的列表。这是我的第二个要点。区分“空:更新”和“缺少:不更新”。我正在寻找解决方案,不必为每个实体及其用例编写多个自定义合并。无法相信大型系统依赖数千行手动合并代码。一定还有别的事。。我想仅仅使用(取消)编组代理->XYZ和XYZ->代理(而不仅仅是“null”)就足以满足我的用例。但是我不确定hibernate是否支持自己创建一个代理…区别只是因为不能在客户端延迟加载引用,因此必须将变通方法用作代理->null。根据客户实体的每个用例,使用DTO可能会产生许多不同的类。此外,我已经有了equals(),hashCo