JavaEE:在服务器和客户端之间共享实体类

JavaEE:在服务器和客户端之间共享实体类,java,inheritance,jaxb,jpa-2.0,Java,Inheritance,Jaxb,Jpa 2.0,我负责一个JavaEE应用程序,它为许多客户机提供后端功能。有些客户机也是用Java编写的,所以我将实体提取到一个单独的jar中,服务器和客户机共享该jar 服务器使用JPA2实现持久性,JAX-RS实现与客户端的通信,JAXB实现与XML和JSON之间的串行化。因此,(共享)类文件同时包含JPA和JAXB注释 显然,同一对象在服务器(它是托管JPA实体)和客户端(它是反序列化POJO)上的行为不同,尤其是在一对多关系方面 问题:有时,我希望根据执行的位置,各个方法调用的行为有所不同。我可以通过

我负责一个JavaEE应用程序,它为许多客户机提供后端功能。有些客户机也是用Java编写的,所以我将实体提取到一个单独的jar中,服务器和客户机共享该jar

服务器使用JPA2实现持久性,JAX-RS实现与客户端的通信,JAXB实现与XML和JSON之间的串行化。因此,(共享)类文件同时包含JPA和JAXB注释

显然,同一对象在服务器(它是托管JPA实体)和客户端(它是反序列化POJO)上的行为不同,尤其是在一对多关系方面

问题:有时,我希望根据执行的位置,各个方法调用的行为有所不同。我可以通过继承来解决这个问题,这样我就不必手动维护同一类的两个实现了吗

例如:

  • 一个队有许多队员。玩家有名字
  • 请求被映射到
    GET/team/
    以获取团队,以及
    GET/team/
    以获取团队的特定玩家
  • 对于编组和序列化,
    团队
    应保持“扁平”(不包括球员)。但是,在序列化中包括他们的名字,以便客户知道他们可以详细检索哪些玩家
在服务器端,我会这样构建它:

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Team {

  /* some other fields, belonging to the Team */

  @OneToMany(fetch = FetchType.LAZY, mappedBy="team")
  @XmlTransient  // don't marshall the players
  List<Player> players;

  /* getters and setters as necessary */

  @XmlElement
  public List<String> getPlayerNames() {
    List<String> names = new ArrayList<String>();
    for (Player p : getPlayers()) {
      names.add(p.getName());
    }
    return names;
  }
}
@实体
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
公开课小组{
/*其他一些属于团队的字段*/
@OneToMany(fetch=FetchType.LAZY,mappedBy=“team”)
@xmltransive//不要马歇尔球员
列出参与者名单;
/*如有必要,可以使用getter和setter*/
@XmlElement
公共列表getPlayerNames(){
列表名称=新的ArrayList();
for(Player p:getPlayers()){
name.add(p.getName());
}
返回姓名;
}
}
在客户端,我将其映射到:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Team {

  /* some other fields, belonging to the Team */

  List<String> playerNames;

  public List<String> getPlayerNames() {
    return playerNames;
  }

  public void setPlayerNames(List<String> playerNames) {
    this.playerNames = playerNames;
  }

  /* getters and setters as necessary */

}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
公开课小组{
/*其他一些属于团队的字段*/
列出玩家名称;
公共列表getPlayerNames(){
返回游戏时间;
}
公共无效集合播放名称(列表播放名称){
this.playerNames=playerNames;
}
/*如有必要,可以使用getter和setter*/
}
这样,playernames在服务器端被编组(通过
@xmlement
-注释
getPlayerNames()
)。当客户端收到它时,它会正确地解组列表。大家都很高兴


然而,现在我必须维护两个基本相同的类,其中只会出现微小的差异。。。正确执行此操作的最佳方法是什么?

直接序列化实体并通过线路传输它们可能会带来问题,例如,如果您有循环引用,或者如果您希望包含反序列化可能需要的其他信息,特别是在JSON中。另一个问题可能是分离的实体(如果您将它们发送到客户端,实体管理器将失去对实体的控制,当它们返回时您必须重新连接它们)或延迟加载(您不能在客户端上延迟加载)。因此,我建议在通过导线传输实体之前,将其转换为数据传输对象。有关详细信息和动机,请参阅福勒的《企业应用程序架构模式》(第401页,本章大部分内容可通过谷歌图书获得)


在客户机和服务器上使用相同的类也可能有问题,因为它们的行为不同,将来可能会进一步分化。通过在客户端和服务器上提交相同的代码,您可能会限制自己太多,或者最终导致一团乱麻。

直接序列化实体并通过有线传输可能会造成问题,例如,如果您有循环引用,或者希望包含反序列化可能需要的其他信息,特别是在JSON中。另一个问题可能是分离的实体(如果您将它们发送到客户端,实体管理器将失去对实体的控制,当它们返回时您必须重新连接它们)或延迟加载(您不能在客户端上延迟加载)。因此,我建议在通过导线传输实体之前,将其转换为数据传输对象。有关详细信息和动机,请参阅福勒的《企业应用程序架构模式》(第401页,本章大部分内容可通过谷歌图书获得)


在客户机和服务器上使用相同的类也可能有问题,因为它们的行为不同,将来可能会进一步分化。通过在客户端和服务器上使用相同的代码库,您可能会限制自己太多,或者最终导致一团混乱。

谢谢。当然,实体是分离的,只交换表示是JAX-RS的本质。因此,实体已经是DTO了。是的,同一个类在客户端和服务器上的行为在少数情况下是不同的——我问题的核心是如何更好地组织类。你能提供一个建议吗?我回答的第一段是指出我反对使用实体作为DTO。我会使用单独的类。但这只是一个旁注,以防我不够清楚。关于类组织,如果您想在客户端和服务器上共享一些常见行为:在包a中创建基类,在包a.S中为服务器扩展它,在包a.C中为客户端扩展它。然后为服务器将A+A.S捆绑在同一个JAR中,为客户端将A+A.C捆绑在同一个JAR中。Maven多模块项目(一个模块用于A,一个模块用于A.C,一个模块用于A.S,一个模块用于A.S,一个模块用于A.S,一个模块用于A.S),可能会有所帮助。感谢@aha提出Maven多模块项目的想法。。。这是几个星期来我一直在寻找的关键词。谢谢。当然,实体是分离的,只交换表示是JAX-RS的本质。因此,实体已经是DTO了。而且,是的,同一个类在