我们正在使用Java EE 7和WildFly 9来开发移动/ Web应用程序的自定义后端。后端是一个经典的3层系统,具有通信逻辑(JAX-RS),业务逻辑(会话EJB)和持久层(Hibernate)。
业务逻辑层由一组服务组成,每个服务由接口和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应仅包含帖子id
和title
。目标是避免不必要的处理,以便在不需要时加载非常大的UserDTO
对象。
天真的解决方案是向List<String> desiredFields
添加自定义getAllPosts()
参数。这并不能说服我,因为我们需要将此参数添加到几乎每个服务方法中。
这样做的最佳做法是什么?是否有针对此目的的Java EE对象?
答案 0 :(得分:0)
通过直接返回一个唯一的模型类实例,假设 Post 而不是PostDTO,结合JPA和JAXB注释,您可以从@XmlTransient和默认延迟加载中受益,以避免查询用户不需要时,持久层中的实体,以及从RESTful Web服务层隐藏此属性。
我个人用它来做很多事情,因为它通过减少层和映射(entity / dto)来简化应用程序代码。有关详细信息,请参阅these slides。
我认为它非常适合CRUD操作,它是一个纯Java EE 7解决方案。无需使用额外的库。
Post.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 ...
}
PROS:
CONS:
修改强>
有时您想要获取作者,假设您在REST操作中将示例的QueryParam fetchAuthor设置为true / false,则需要在该模型对象中添加额外的JPA @Transient属性,例如 author 并在需要时初始化它。所以它是Post java类中的额外映射,但是你保持上面提到的优点的好处。要初始化author属性,您只需使用getPersistedAuthor()返回的值设置它(这将触发查询以获取延迟加载的持久作者)。
答案 1 :(得分:0)
我的回答做了一些额外的假设:
@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
来决定什么部件应在实体的(反)序列化中处理。