Jakarta ee JPA数据访问对象-异常处理和回滚

Jakarta ee JPA数据访问对象-异常处理和回滚,jakarta-ee,jpa,exception-handling,dao,rollback,Jakarta Ee,Jpa,Exception Handling,Dao,Rollback,我想知道在数据访问对象中处理异常的最佳方法是什么,我对产品质量代码感兴趣 例如 public UserDaoImpl implements UserDao{ @PersistenceContext private EntityManager em; void save(User user){ em.persist(user); } User getById(long id){ return em.find(User.class

我想知道在数据访问对象中处理异常的最佳方法是什么,我对产品质量代码感兴趣

例如

public UserDaoImpl implements UserDao{
    @PersistenceContext
    private EntityManager em;

    void save(User user){
       em.persist(user);
    }
    User getById(long id){
       return em.find(User.class,id);
    }
}
比如说,我在某个地方有一个RegisterService,我希望在某个时候将用户保存到数据库中。每个用户都需要一封独特的电子邮件。 您如何检查这一点,以及该代码的去向? 在持久化之前,我们是否使用查询检查保存方法中是否已经有该电子邮件的用户?或者该代码会被发送到服务吗?或者我们试着抓住一些例外

但就我所知,对于例外情况,我们永远无法确定到底发生了什么,我们可以尝试捕获ConstraintViolationException,但这并不能明确告诉我们发生了什么


它在产品质量代码中的表现如何?

这与您的知识和“风格”密切相关;人们做这件事的方式不同,有些是对的,有些是错的

我来自一个从不使用异常进行流控制的世界,这意味着:我不会恢复抛出异常的情况。当流程中发生某些事情时,我总是让它进入最高级别,有时重新抛出以添加更多的价值/意义/语义信息。因此,例如,在您的
save(User-User)
中,原因/问题是什么并不重要,因为它只是失败了。我不认为有一组异常比其他异常更重要……这就是为什么我可以更进一步,并不真正需要/使用所有异常类型,因为仅表示一种行为就足够了(
exception
)-在这里,人们不同意(有时不考虑…“为什么不止一种”?)

在您的情况下,人们通常会:

void save(User user) {
  try {
    em.persist(user);
  } catch (SomeExceptionType e) {
    log.error("something here");
    // What now here, huh?
  }
}
其他人可能会说,最好有不同类型的人“知道发生了什么”,比如:

…这和用它们来控制流量没有什么不同

希望能有帮助。这可能是一件“正在进行的事情”,但如果这种方法适合你的世界,我希望你能得到这个想法

建议

使
final
成为您的
UserDaoImpl
(如果您知道没有其他人从中扩展)。您的方法应该是
private
final
以及参数。明智地使用
public
,即使是在类级别,有时我们也不会在同一个包之外使用它们

public final UserDaoImpl implements UserDao {
    // Grrrr, can't make 'em' final...that's because DI is an
    // anti-pattern but that's another story
    @PersistenceContext
    private EntityManager em;

    private void save(final User user){
      em.persist(user);
    }

    private User getById(final long id){
      return em.find(User.class, id);
    }
}
比如说,我在某个地方有一个RegisterService,我希望在某个时候将用户保存到数据库中。每个用户都需要一封独特的电子邮件。您如何检查这一点,以及该代码的去向

在插入时在同一事务中检查它,并引发触发完全回滚的自定义异常

签入同一事务将保证DB在插入期间不会导致约束冲突异常。建议您的“DAO”实际上是一个
@无状态的
EJB(基于您用[JavaEE]而不是[spring]标记问题的事实),那么客户端的每个方法调用默认情况下都算作一个事务

抛出特定于定制服务层的异常会将前端(即调用该业务服务方法的人,如JSF、JAX-RS、Spring MVC、JSP/Servlet等)与底层持久性层分离。换句话说,前端代码库完全没有
javax.persistence.*
imports/dependencies。建议您使用EJB作为服务层API,然后使用注释注释自定义异常

例如

然后,在前端,只需捕获自定义服务层特定的异常(我们现在可以确定这正是导致异常的预期意外情况),并通过向最终用户显示“嘿,你已经注册了!也许你想登录或重置密码?”消息来进行相应的处理

另见:

很抱歉,这确实是一个没有建设性的回答。那个最后的评论和抱怨的评论也毫无意义。@BalusC就像我说的,人们有他们自己的方式;你介意在“非建设性”中详细说明吗?我想知道?你的回答不是技术性的/客观的。你的回答是个人的/主观的,没有任何关于选择的可行的技术解释。换言之,答案的表达方式让人感觉你只是在黑暗中拍摄/猜测;但就像生活中的每件事一样,为了达到某个目标,有很多方法可以做同样的事情。无论如何谢谢你!就像生活中的一切一样,经验伴随时间而来;)不客气!回答得好!就像“预期的意外情况”。谢谢你的回答:)我就是这么想的。如果有更多独特的属性,并且前端希望确切地知道出了什么问题,例如用户名被占用或电子邮件已经被占用,该怎么办。我只是做了那么多自定义例外吗?并使用查询检查这些属性的可用性?我真的不喜欢那个代码。将它设置为异常的属性。至于喜欢技术需求来实现功能需求,不要武断,要务实。归根结底,您需要健壮、干净的逻辑。因此,我不应该试图捕捉一些JPA特定的异常和类似的东西?它确实可以归结为以前端(客户端)不需要这样做的方式设计您的服务层。另请参见第一个“请参见”链接,以获取关于该问题的详细问答。
public final UserDaoImpl implements UserDao {
    // Grrrr, can't make 'em' final...that's because DI is an
    // anti-pattern but that's another story
    @PersistenceContext
    private EntityManager em;

    private void save(final User user){
      em.persist(user);
    }

    private User getById(final long id){
      return em.find(User.class, id);
    }
}
@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public User findByEmail(String email) {
        List<User> users = em.createQuery("SELECT u FROM User u WHERE email = :email", User.class)
            .setParameter("email", email)
            .getResultList();
        return users.isEmpty() ? null : users.get(0);
    }

    public void register(User user) {
        if (findByEmail(user.getEmail()) != null) {
            throw new DuplicateEntityException();
        }

        em.persist(user);
    }

    // ...
}
@ApplicationException(rollback=true)
public class DuplicateEntityException extends RuntimeException {}