如何将Web服务的输入合并到JPA实体

时间:2014-07-09 23:11:41

标签: java rest jpa jax-rs dto

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

这些是我迄今为止发现的有利有弊的选择。

1。 JPA merge()
我尝试的第一件事可能是最简单的。 GET操作返回JPA实体,该实体很容易序列化为JSON。在更新时,传回的对象是JSON,可用于填充分离的实体。可以使用JPA merge()方法将其保存到DB中。

赞成
简单的架构,代码重复较少(即没有DTO')

缺点
据我所知,只有你传递整个模型才有效。如果您尝试隐藏某些字段,例如可能是User实体上的密码,则合并会认为您尝试在DB中将这些字段设置为null。不好!<​​/ p>

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

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

缺点
在编写DTO时,感觉就像重复自己一样。不知何故,你必须在实体和DTO之间进行映射。我试图用推土机自动化这个但是有点令人失望。它解决了它不应该做的事情,并且要完全控制你必须编写xml。

第3。 DTO使用手动合并
第三种方法是放弃推土机,只需将属性从DTO复制到服务中的实体。每个人似乎都说反模式,但它几乎是我过去看到的每一个非平凡的应用程序都有用。

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

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

2 个答案:

答案 0 :(得分:2)

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

然后我们转向DTO方法,我们避免使用合并并依赖于手动操作。是的,这很乏味,需要知识 什么状态实际来自超脱实体,但对我们来说这是值得的。这样我们就不会触及懒惰的关系和属性而不是要改变。并只设置所需的内容。 Hibernate的自动状态检测完成了事务提交。

答案 1 :(得分: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注释,那么程序员可以避免创建自定义属性映射,这将是很好的。