【问题标题】:How to merge input from a web service to a JPA entity如何将来自 Web 服务的输入合并到 JPA 实体
【发布时间】:2014-08-31 03:41:04
【问题描述】:

我正在尝试找出在安静的 Web 服务上下文中使用 JPA 的最佳方式。输入以 JSON 形式出现,我可以使用 Jackson/JAX-RS 将其转换为 POJO。这被传递给我需要以某种方式合并到 JPA 实体中的服务。

这些是我目前发现的各有利弊的选项。

1. JPA 合并()
我尝试的第一件事可能是最简单的。 GET 操作返回可轻松序列化为 JSON 的 JPA 实体。在更新时,传递回的对象是 JSON,可用于填充分离的实体。这可以使用 JPA merge() 方法保存到数据库中。

优点
具有较少代码重复的简单架构(即没有 DTO)

缺点
据我所知,这仅在您传递整个模型时才有效。如果您尝试隐藏某些字段,例如用户实体上的密码,那么合并会认为您正在尝试在数据库中将这些字段设置为 null。不好!

2。 DTO 使用 JPA find() 和推土机
接下来我想我会考虑使用数据传输对象。显然是一种反模式,但值得一看。该服务现在基于实体创建一个 DTO 实例,并且正是这个 DTO 被序列化为 JSON。然后更新使用 find() 方法从数据库中获取实体,并且需要将值从 DTO 复制到实体。我尝试使用推土机框架自动执行此映射。

优点
您不必返回整个模型。如果您有某些不想更新的字段,您可以将它们从 DTO 中删除,并且它们不能被错误地复制到实体中。使用推土机意味着您不必手动将属性从 dto 复制到实体,反之亦然。

缺点
在编写 DTO 时感觉就像在重复自己。不知何故,您必须在实体和 DTO 之间进行映射。我试图用推土机自动化这个,但有点令人失望。它消除了不应该的东西,并且要获得完全控制,您必须编写 xml。

3. DTO 使用手动合并
第三种方法是放弃推土机,只需将属性从 DTO 复制到服务中的实体。似乎每个人都在说反模式,但这几乎就是我过去看到的每个重要应用程序的工作方式。

总结
这似乎是在为开发人员保持简单但无法控制输入/输出或制作更强大的 Web 服务但必须在过程中使用反模式之间做出决定......

我错过了什么吗?也许有一个难以捉摸的选择?

【问题讨论】:

    标签: java rest jpa jax-rs dto


    【解决方案1】:

    这是我正在使用的方法:

    • 使用 XmlTransient 注释抑制某些字段的序列化
    • 从客户端更新记录时,从数据库中获取实体并使用 ModelMapper 和自定义属性映射来复制更新的值,而不更改 JSON 表示中不存在的字段。

    例如:

    public class User {
        @Id
        private long id;
    
        private String email;
    
        @XmlTransient
        private String password;
        ...
    }
    
    public class UserService {
        ...
        public User updateUser(User dto) {
            User entity = em.find(User.class, dto.getId());
            ModelMapper modelMapper = new ModelMapper();
            modelMapper.addMappings(new UserMap());
            modelMapper.map(userDto, user);
            return user;
        }
    }
    
    public class UserMap extends PropertyMap<User, User> {
        protected void configure() {
            skip().setPassword(null);
        }
    }
    

    BeanUtils 是 ModelMapper 的替代品。

    如果这些库能够识别 XmlTransient 注释,那就太好了,这样程序员就可以避免创建自定义属性映射。

    【讨论】:

      【解决方案2】:

      使用 JPA merge 看起来是最简单、最干净且省力的方法,但正确发现会在将分离实体属性设置为 null 时产生问题。 在我的经验中,另一个问题是,如果您依赖 JPA 合并操作,您也必须使用 Cascade 功能。 对于简单且嵌套较少的关系,这工作得相当好,但对于深度嵌套的域对象和大量关系,这会对性能产生很大影响。原因是 ORM 工具(根据我的经验是 Hibernate)预先缓存 SQL 以加载合并实体(Hibernate 用语中的“合并路径”),如果嵌套太深,级联映射,SQL 中的连接就会变得太大。标记 realtions Lazy 在这里没有帮助,因为合并路径是由关系中的级联决定的。随着模型的发展,这个问题会慢慢变得明显。加上愤怒的 DBA 在我们脸上挥舞着巨大的连接查询的前景促使我们做一些不同的事情:-) 在 Hibernate JIRA 中有一个有趣的 issue 与 Hibernate 相关,关于 Lazy 关系的合并仍未解决(实际上被拒绝,但阅读讨论非常愉快)。

      然后我们转向 DTO 方法,在这种方法中我们避免使用合并,而是依靠手动进行。是的,这很乏味,需要了解 什么状态实际上来自分离的实体,但对我们来说这是值得的。这样我们就不会触及 Lazy 关系和不打算改变的属性。并仅设置所需的内容。 Hibernate 的自动状态检测在事务提交时完成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-05
        • 1970-01-01
        • 2012-12-26
        • 2016-09-17
        • 2020-01-20
        • 2011-06-29
        相关资源
        最近更新 更多