Java 指定服务返回的字段的最佳方法

Java 指定服务返回的字段的最佳方法,java,jakarta-ee,jboss,wildfly,wildfly-9,Java,Jakarta Ee,Jboss,Wildfly,Wildfly 9,我们使用JavaEE7和WildFly 9为移动/web应用程序开发定制后端。后端是一个典型的三层系统,具有通信逻辑(JAX-RS)、业务逻辑(会话EJB)和持久层(Hibernate) 业务逻辑层由一组服务组成,每个服务由接口和EJB实现定义。让我们假设 public interface IPostService { List<PostDTO> getAllPosts(); } 让我们假设,有时候,客户只对postid和title感兴趣。API端点将收到一个查询参数,其中

我们使用JavaEE7和WildFly 9为移动/web应用程序开发定制后端。后端是一个典型的三层系统,具有通信逻辑(JAX-RS)、业务逻辑(会话EJB)和持久层(Hibernate)

业务逻辑层由一组服务组成,每个服务由接口和EJB实现定义。让我们假设

public interface IPostService {
    List<PostDTO> getAllPosts();
}
让我们假设,有时候,客户只对post
id
title
感兴趣。API端点将收到一个查询参数,其中包含要获取的字段列表。因此,JSON序列化DTO应该只包含post
id
title
。目标是避免不必要的处理,以便在不需要时将非常大的
UserDTO
对象加载到该对象

一个简单的解决方案是在
getAllPosts()
中添加一个自定义的
List desiredFields
参数。这并不能完全说服我,因为我们需要将这个参数添加到几乎所有的服务方法中


这样做的最佳实践是什么?是否有Java EE对象用于此目的?

通过直接返回唯一的模型类实例,比如说Post而不是PostDTO,结合JPA和JAXB注释,您可以受益于默认延迟加载,以避免在不必要时查询持久层中的用户实体,并对RESTfulWeb服务层隐藏此属性

我个人经常这样做,因为它通过减少层和映射(实体/dto)简化了应用程序代码。有关详细信息,请参阅

我认为它非常适合CRUD操作,它是一个纯JavaEE7解决方案。不需要使用额外的lib

java将如下所示:

import javax.persistence.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @XmlTransient
    @ManyToOne
    private User persistedAuthor;

    @Transient
    private User author;

    // + Getters and Setters ...

}
优点:

  • 没有用于测试、编码和维护的DTO/实体映射
  • 只有一个模型层,您可以在其中放置持久性/业务/验证规则
  • JavaEE7解决方案,无需使用额外的库
缺点:

  • 您的模型与JPA绑定。所以,若您更改了持久性解决方案,并且该解决方案不允许管理多个不同的持久性层,则需要对其进行修改
编辑


由于有时需要获取作者,假设在REST操作中将sample的QueryParam fetchAuthor设置为true/false,则需要在该模型对象中添加额外的JPA属性,如author,并在需要时对其进行初始化。所以这是后java类中的一个额外映射,但是您保留了上述优点的优点。要初始化author属性,您只需使用getPersistedAuthor()返回值对其进行设置(这将触发一个查询,以通过延迟加载获取持久化的author)。

我的回答提出了一些额外的假设:

  • 使用JPA2.1,您可以使用实体图有条件地获取实体的部分,或者是惰性的,或者是急切的
  • 使用JAX-RS和Jackson JSON提供程序,您可以使用
    @JsonView
    将对象呈现为JSON
考虑以下示例:用户有一组角色。默认情况下,角色是惰性获取的,不需要在JSON中呈现。但在某些情况下,您希望立即获取它们,因为您希望它们以JSON呈现

@Entity
@NamedQueries({
    @NamedQuery(name = "User.byName", query = "SELECT u FROM User u WHERE u.name = :name"),
    @NamedQuery(name = "Users.all", query = "SELECT u FROM User u ORDER BY u.name ASC")
})
@NamedEntityGraph(name = "User.withRoles", attributeNodes = {
    @NamedAttributeNode("roles") // make them fetched eager
})
public class User implements Serializable {

    public static interface WithoutRoles {}
    public static interface WithRoles extends WithoutRoles {}

    @Id
    private Long id;

    @Column(unique = true, updatable = false)
    private String name;

    @ManyToMany // fetched lazy by default
    @JoinTable(/* ... */)
    private Set<Role> roles;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // include in JSON only when "@JsonView(WithRoles.class)" is used:
    @JsonView(WithRoles.class)
    public Set<Role> getRoles() {
        if (roles == null)
            roles = new HashSet<>();
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

}

@Entity
public class Role implements Serializable {
    /* ... */
}
@实体
@命名查询({
@NamedQuery(name=“User.byName”,query=“从用户u中选择u,其中u.name=:name”),
@NamedQuery(name=“Users.all”,query=“按u.name ASC从用户u订单中选择u”)
})
@NamedEntityGraph(name=“User.withRoles”,attributeNodes={
@NamedAttributeNode(“角色”)//让他们抓取
})
公共类用户实现可序列化{
没有角色{}的公共静态接口
带角色的公共静态接口扩展为不带角色{}
@身份证
私人长id;
@列(unique=true,updateable=false)
私有字符串名称;
@默认情况下,ManyToMany//获取为惰性
@可联接(/*…*/)
私人设定角色;
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
//仅当使用“@JsonView(WithRoles.class)”时才包含在JSON中:
@JsonView(WithRoles.class)
公共集getRoles(){
如果(角色==null)
roles=newhashset();
返回角色;
}
公共无效集合角色(集合角色){
this.roles=角色;
}
}
@实体
公共类角色实现可序列化{
/* ... */
}
下面是加载用户的代码,无论是否有角色:

public User getUser(String name, boolean withRoles) {
    TypedQuery<User> query = entityManager.createNamedQuery("User.byName", User.class)
        .setParameter("name", name);

    if (withRoles) {
        EntityGraph<User> graph = (EntityGraph<User>) entityManager.createEntityGraph("User.withRoles");
        query.setHint("javax.persistence.loadgraph", graph);
    }

    try {
        return query.getSingleResult();
    } catch (NoResultException e) {
        return null;
    }
}

public List<User> getAllUsers() {
    return entityManager.createNamedQuery("Users.all", User.class)
        .getResultList();
}
public User getUser(字符串名,带角色的布尔值){
TypedQuery query=entityManager.createNamedQuery(“User.byName”,User.class)
.setParameter(“名称”,名称);
如果(带角色){
EntityGraph=(EntityGraph)entityManager.createEntityGraph(“User.withRoles”);
setHint(“javax.persistence.loadgraph”,graph);
}
试一试{
返回query.getSingleResult();
}捕获(noresulte异常){
返回null;
}
}
公共列表getAllUsers(){
返回entityManager.createNamedQuery(“Users.all”,User.class)
.getResultList();
}
现在是REST资源:

@RequestScoped @Path("users")
public class UserResource {

    private @Inject UserService userService;

    // user list - without roles
    @GET @Produces(MediaType.APPLICATION_JSON)
    @JsonView(User.WithoutRoles.class)
    public Response getUserList() {
        List<User> users = userService.getAllUsers();
        return Response.ok(users).build();
    }

    // get one user - with roles
    @GET @Path("{name}") @Produces(MediaType.APPLICATION_JSON)
    @JsonView(User.WithRoles.class)
    public Response getUser(@PathParam("name") String name) {
        User user = userService.getUser(name, true);
        if (user == null)
            throw new NotFoundException();

        return Response.ok(user).build();
    }

}
@RequestScoped@Path(“用户”)
公共类用户资源{
private@injectuserservice用户服务;
//用户列表-无角色
@GET@products(MediaType.APPLICATION_JSON)
@JsonView(User.WithoutRoles.class)
公共响应getUserList(){
List users=userService.getAllUsers();
返回Response.ok(users.build();
}
//获取一个具有角色的用户
@获取@Path(“{name}”)@products(MediaType.APPLICATION_JSON)
@JsonView(User.WithRoles.class)
公共响应getUser(@PathParam(“name”)字符串名){
User=userService.getUser(name,true);
if(user==null)
抛出新的NotFoundException();
返回Response.ok(user.build();
}
}
public User getUser(String name, boolean withRoles) {
    TypedQuery<User> query = entityManager.createNamedQuery("User.byName", User.class)
        .setParameter("name", name);

    if (withRoles) {
        EntityGraph<User> graph = (EntityGraph<User>) entityManager.createEntityGraph("User.withRoles");
        query.setHint("javax.persistence.loadgraph", graph);
    }

    try {
        return query.getSingleResult();
    } catch (NoResultException e) {
        return null;
    }
}

public List<User> getAllUsers() {
    return entityManager.createNamedQuery("Users.all", User.class)
        .getResultList();
}
@RequestScoped @Path("users")
public class UserResource {

    private @Inject UserService userService;

    // user list - without roles
    @GET @Produces(MediaType.APPLICATION_JSON)
    @JsonView(User.WithoutRoles.class)
    public Response getUserList() {
        List<User> users = userService.getAllUsers();
        return Response.ok(users).build();
    }

    // get one user - with roles
    @GET @Path("{name}") @Produces(MediaType.APPLICATION_JSON)
    @JsonView(User.WithRoles.class)
    public Response getUser(@PathParam("name") String name) {
        User user = userService.getUser(name, true);
        if (user == null)
            throw new NotFoundException();

        return Response.ok(user).build();
    }

}