gpt4 book ai didi

java - DAO 实现的最佳实践

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:55:37 36 4
gpt4 key购买 nike

我一直在使用 DAO 模式来访问我一直在构建的应用程序中的持久层。

我已经实现的其中一件事是围绕我的 DAO 实现进行“包装”以进行验证。包装器将我的 DAO 实例作为构造函数参数,并实现与 DAO 类似的接口(interface),除了抛出的异常类型。

例如:

业务逻辑接口(interface)

public interface UserBLInt {

private void assignRightToUser(int userId, int rightId) throws SomeAppException;

}

DAO 接口(interface)

public interface UserDAOInt {

private void assignRightToUser(int userId, int rightId) throws SomeJPAExcption;

}

业务逻辑实现

public class UserBLImpl implements  UserBLInt {

private UserDAOInt userDAO;

public UserBLImpl(UserDAOInt userDAO){
this.userDAO = userDAO;
}

@Override
private void assignRightToUser(int userId, int rightId) throws SomeAppException{
if(!userExists(userId){
//throw an exception
}
try{
userDAO.assignRightToUser(userId, rightId);
} catch(SomeJpAException e){
throw new SomeAppException(some message);
}
}

}

DAO 实现

public class UserDAOImpl implements UserDAOInt {
//......
@Override
public void assignRightToUser(int userId, int rightId){
em.getTransaction().begin();
User userToAssignRightTo = em.find(User.class, userId);
userToAssignRightTo.getRights().add(em.find(Right.class, rightId));
em.getTransaction().commit();
}
}

这只是一个简单的示例,但我的问题是,在 DAO 实现中进行另一项检查以确保在添加 >对,但是,作为一名程序员,我看到了空指针的机会。

显然,我可以在实体管理器上调用 find 之后添加空检查,如果返回空则抛出异常,但这就是将 DAO 包装在业务逻辑实现中的全部目的,以便预先完成所有验证工作,因此 DAO 代码是干净的,并且根本不需要在空检查或逻辑方面做太多事情。因为我有 DAO 的包装器,所以在 DAO 中进行 null 检查仍然是个好主意吗?我知道理论上可以在业务逻辑调用和 dao 调用之间删除对象,这不太可能,而且检查 null 似乎是重复的工作。对于这种情况,最佳做法是什么?

编辑:

这看起来像是一个合适的 DAO 修改吗?

public EntityManager beginTransaction(){
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
return entityManager;
}

public void rollback(EntityManager entityManager){
entityManager.getTransaction().rollback();
entityManager.close();
}

public void commit(EntityManager entityManager){
entityManager.getTransaction().commit();
entityManager.close();
}

最佳答案

DAO,虽然现在有点通用和过度使用的术语,但(通常)意在抽象数据层(因此除其他好处外,它可以在不接触应用程序其余部分的情况下进行更改)。

不过,看起来您的 DAO 实际上所做的不仅仅是抽象数据层。在:

public class UserDAOImpl implements UserDAOInt {
......
@Override
public void assignRightToUser(int userId, int rightId){
em.getTransaction().begin();
User userToAssignRightTo = em.find(User.class, userId);
userToAssignRightTo.getRights().add(em.find(Right.class, rightId));
em.getTransaction().commit();
}
}

您的 DAO 知道业务逻辑。它知道向用户分配权限就是在权限列表中添加权限。 (似乎很明显,分配权限只是将其添加到列表中,但想象一下,这在未来可能会变得更加复杂,并对其他用户和权限等产生副作用。)

所以这个赋值不属于DAO。应该在业务层。您的 DAO 应该只有类似 userDAO.save(user) 的东西,业务层在完成设置权限和内容后会调用它。


另一个问题:您的交易过于本地化。这几乎不是交易。

请记住事务是一个业务单元,您在其中执行原子(“批处理”)业务工作,而不仅仅是因为 EntityManager 使您打开的事务。

我的意思是,从代码的角度来说,应该是业务层主动开启事务,而不是DAO(实际上,DAO应该有“开启事务”的服务——方法——会被调用)。

然后考虑在业务层中打开事务:

public class UserBLImpl implements  UserBLInt {
...
@Override
private void assignRightToUser(int userId, int rightId) throws SomeAppException{
userDAO.beginTransaction(); // or .beginUnitOfWork(), if you wanna be fancy

if(!userExists(userId){
//throw an exception
// DON'T FORGET TO ROLLBACK!!! before throwing the exception
}
try{
userDAO.assignRightToUser(userId, rightId);
} catch(SomeJpAException e){
throw new SomeAppException(some message);
}

userDAO.commit();
}
}

现在,对于您的问题:数据库在 userExists() ifuserDAO 之间变化的风险仍然存在。 .. 但你有选择:

(1)锁定用户,直到交易结束;或 (2) 顺其自然。

1:如果用户在这两个命令之间被搞砸的风险很高(比如你的系统有很多并发用户)并且如果这个问题发生了那将是一件大事,那么考虑锁定 整个交易的用户;也就是说,如果我开始使用它,则没有其他事务可以更改它。

  • 如果您的系统有大量并发用户,另一个(更好的)解决方案是“设计问题”,即重新设计您的设计,以便业务交易中的更改具有更严格(更小)的范围- 你的例子的范围足够小,所以这个建议可能没有多大意义,但只要考虑你的业务交易做的更多(然后让它做的少得多,每个依次,可能是解决方案)。这本身就是一个完整的主题,所以我不会在这里详细介绍,请您保持开放的心态。

2:另一种可能性,你会认为这是最常见的方法,如果你正在处理具有约束检查的 SQL DB,例如 UNIQUE,只是让 DAO 异常吹走。我的意思是,这将是一件非常罕见且几乎不可能发生的事情,您不妨通过接受它可能发生来处理它,您的系统只会显示一条好消息,例如“出了点问题,请重试”- - 这只是基本成本与 yield 的比重。


更新:

程序化事务处理可能很棘手(难怪使用声明式替代方案,例如 Spring 和 EJB/CDI)。尽管如此,我们并不总是有机会使用它们(也许您正在调整遗留系统,谁知道呢)。所以这里有一个建议:https://gist.github.com/acdcjunior/94363ea5cdbf4cae41c7

关于java - DAO 实现的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29525312/

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