gpt4 book ai didi

java - 指定服务返回的字段的最佳方式

转载 作者:搜寻专家 更新时间:2023-11-01 03:48:55 24 4
gpt4 key购买 nike

我们使用 Java EE 7 和 WildFly 9 来开发移动/网络应用程序的自定义后端。后端是一个经典的 3 层系统,具有通信逻辑 (JAX-RS)、业务逻辑 (Session EJB) 和持久层 (Hibernate)。

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

public interface IPostService {
List<PostDTO> getAllPosts();
}

@Stateless
public class PostService implements IPostService {
List<PostDTO> getAllPosts(){
// retrieving my Posts through Hibernate
}

public class PostDTO {

private Long id;
private String title;
// UserDTO is a VEEERY big object
private UserDTO author;

// getters and setters
}

假设,有时客户只对帖子 id 感兴趣和 title . API 端点将收到一个查询参数,其中包含要获取的字段列表。因此,JSON 序列化的 DTO 应该只包含帖子 idtitle .目标是避免不必要的处理以加载非常大的 UserDTO。不需要时反对。

一个天真的解决方案是添加自定义 List<String> desiredFields参数 getAllPosts() .这并不能完全说服我,因为我们需要将此参数添加到几乎每个服务方法。

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

最佳答案

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

  • 使用 JPA 2.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 {
/* ... */
}

下面是加载用户的代码,有或没有角色:

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();
}

现在是 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();
}

}

因此,在持久性端(JPA、Hibernate),您可以使用延迟获取来防止加载实体的某些部分,而在 Web 层(JAX-RS、JSON),您可以使用 @JsonView 决定在实体的(反)序列化中应处理哪些部分。

关于java - 指定服务返回的字段的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34165948/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com