JSF 控制器、服务和 DAO

JSF Controller, Service and DAO

我正在努力适应 JSF 在访问数据方面的工作方式(来自 spring 背景)

我正在创建一个维护用户列表的简单示例,我有类似的东西

<h:dataTable value="#{userListController.userList}" var="u">
    <h:column>#{u.userId}</h:column>
    <h:column>#{u.userName}</h:column>
</h:dataTable>

然后"controller"有类似

的东西
@Named(value = "userListController")
@SessionScoped
public class UserListController {
    @EJB
    private UserListService userListService;

    private List<User> userList;

    public List<User> getUserList() {
        userList = userListService.getUsers();
        return userList;
    }
}

并且 "service"(虽然它看起来更像一个 DAO)有

public class UserListService {

    @PersistenceContext
    private EntityManager em;

    public List<User> getUsers() {
        Query query = em.createQuery("SELECT u from User as u");
        return query.getResultList();
    }
}

这是正确的做事方式吗?我的术语正确吗? "service" 感觉更像一个 DAO?并且控制器感觉它正在做一些服务的工作。

它是 DAOwell actually a repository,但不要太担心这种差异,因为它使用持久性上下文访问数据库。

您应该创建一个 Service class,它包装该方法并且是调用 transactions 的地方。

有时 service class 感觉没有必要,但是当您有一个 service 方法调用许多 DAO 方法时,它们的使用更有必要。

我通常最终只创建 service,即使感觉没有必要,以确保模式保持不变并且永远不会直接注入 DAO

这增加了一个额外的抽象层,使未来的重构更加灵活。

Is this the correct way of doing things?

除了在托管 bean getter 方法中以低效的方式执行业务逻辑以及使用过于广泛的托管 bean 范围外,它看起来还不错。如果将服务调用从 getter 方法移动到 @PostConstruct 方法并使用 @RequestScoped@ViewScoped 而不是 @SessionScoped,它看起来会更好。

另请参阅:

  • Why JSF calls getters multiple times
  • How to choose the right bean scope?

Is my terminology right?

没关系。只要您与它保持一致并且代码以合理的方式可读。只是您命名 类 和变量的方式有些笨拙(不合逻辑的 and/or 重复)。比如我个人会用users代替userList,用var="user"代替var="u",用idname代替userIduserName。此外,“UserListService”听起来只能处理用户列表而不是一般用户。我宁愿使用“UserService”,这样您也可以使用它来创建、更新和删除用户。

另请参阅:

  • JSF managed bean naming conventions

The "service" feels more like a DAO?

它不完全是一个 DAO。基本上,JPA 是这里真正的 DAO。以前,当 JPA 不存在时,每个人都自己开发 DAO 接口,以便服务方法可以继续使用它们,即使底层实现(“普通旧”JDBC,或“好旧”Hibernate 等)发生变化。服务方法的真正任务是透明地管理事务。这不是 DAO 的责任。

另请参阅:

  • I found JPA, or alike, don't encourage DAO pattern
  • DAO and JDBC relation?
  • When is it necessary or convenient to use Spring or EJB3 or all of them together?

And the controller feels like it's doing some of the job of the service.

我可以想象它在这个相对简单的设置中做到了这一点。然而,控制器实际上是前端的一部分而不是后端。该服务是后端的一部分,其设计方式应使其可跨所有不同的前端重用,例如 JSF、JAX-RS、“普通”JSP+Servlet,甚至 Swing 等。此外,前端-特定的控制器(也称为“支持 bean”或“演示者”)允许您以特定于前端的方式处理成功 and/or 异常结果,例如在 JSF 的情况下,在抛出异常时显示面孔消息来自服务。

另请参阅:

  • JSF Service Layer
  • What components are MVC in JSF MVC framework?

总而言之,正确的做法如下:

<h:dataTable value="#{userBacking.users}" var="user">
    <h:column>#{user.id}</h:column>
    <h:column>#{user.name}</h:column>
</h:dataTable>
@Named
@RequestScoped // Use @ViewScoped once you bring in ajax (e.g. CRUD)
public class UserBacking {

    private List<User> users;

    @EJB
    private UserService userService;

    @PostConstruct
    public void init() {
        users = userService.listAll();
    }

    public List<User> getUsers() {
        return users;
    }

}
@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public List<User> listAll() {
        return em.createQuery("SELECT u FROM User u", User.class).getResultList();
    }

}

您可以在这里找到一个使用规范 Java EE/JSF/CDI/EJB/JPA 实践的真实世界启动项目:Java EE kickoff app.

另请参阅:

  • Creating master-detail pages for entities, how to link them and which bean scope to choose
  • Passing a JSF2 managed pojo bean into EJB or putting what is required into a transfer object
  • Filter do not initialize EntityManager
  • javax.persistence.TransactionRequiredException in small facelet application