Java 指定服务返回的字段的最佳方法
我们使用JavaEE7和WildFly 9为移动/web应用程序开发定制后端。后端是一个典型的三层系统,具有通信逻辑(JAX-RS)、业务逻辑(会话EJB)和持久层(Hibernate) 业务逻辑层由一组服务组成,每个服务由接口和EJB实现定义。让我们假设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端点将收到一个查询参数,其中
public interface IPostService {
List<PostDTO> getAllPosts();
}
让我们假设,有时候,客户只对postid
和title
感兴趣。API端点将收到一个查询参数,其中包含要获取的字段列表。因此,JSON序列化DTO应该只包含postid
和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提供程序,您可以使用
将对象呈现为JSON@JsonView
@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();
}
}