use*_*644 10 java hibernate jpa
我一直在使用DAO模式来提供对我一直在构建的应用程序中的持久层的访问.
我实现的一件事是围绕我的DAO实现的"包装器"以进行验证.包装器接受我的DAO实例作为构造函数参数,并实现与DAO类似的接口,但抛出的异常类型除外.
例如:
业务逻辑接口
public interface UserBLInt {
private void assignRightToUser(int userId, int rightId) throws SomeAppException;
}
Run Code Online (Sandbox Code Playgroud)
DAO接口
public interface UserDAOInt {
private void assignRightToUser(int userId, int rightId) throws SomeJPAExcption;
}
Run Code Online (Sandbox Code Playgroud)
业务逻辑实现
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这只是一个简单的例子,但我的问题是,在DAO实现中看起来"冗余"进行另一次检查以确保User在添加a之前不是null Right,但是,作为程序员,我看到了一个机会空指针.
显然我可以在实体管理器上调用find之后添加一个空检查,如果返回null则抛出异常,但这是将DAO包装在业务逻辑实现中的全部目的,事先做好所有验证工作,这样就可以了DAO代码是干净的,并且不需要在空检查或很多逻辑方面做很多事情.既然我有DAO的包装器,那么在DAO中进行空检查仍然是一个好主意吗?我在理论上知道可以在业务逻辑调用和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();
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
你的DAO 知道业务逻辑.它知道为auser分配权利是为权利列表添加权利.(似乎很明显,分配权利只是将其添加到列表中,但想象一下,将来可能会变得更加复杂,对其他用户和权利产生副作用等等.)
所以这个分配不属于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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,对于你的问题:数据库userExists() if在userDAO持久存在之间发生变化的风险仍然存在......但你有选择:
(1)锁定用户直到交易结束; 或(2)让它成为.
1:如果用户被这两个命令搞砸的风险很高(比如你的系统有很多并发用户),如果发生这个问题就很大,那么考虑锁定user整个交易; 也就是说,如果我开始使用它,其他任何事务都不会改变它.
2:另一种可能性,你会发现这是最常用的方法,如果你正在处理一个具有约束检查的SQL DB,例如UNIQUE,只是让DAO异常被吹掉.我的意思是,这将是一个如此罕见且几乎不可能发生的事情,你可以通过接受它来解决它可能发生,你的系统只会显示一个很好的消息,如"出了问题,请再试一次" - - 这只是基本成本与收益加权.
更新:
程序化事务处理可能很棘手(难怪使用声明性替代方法,例如Spring和EJB/CDI).尽管如此,我们并不总是乐于使用它们(也许你正在调整遗留系统,谁知道).所以这是一个建议:https://gist.github.com/acdcjunior/94363ea5cdbf4cae41c7
DAO 这里有太多逻辑。DAO 的作用不是为用户分配权限。这就是业务逻辑。DAO 的作用是寻找用户或寻找权限。DAO 中的代码应该位于服务中:
interface UserDAO {
User findById(int userId);
//...
}
interface RightDAO {
Right findById(int rightId);
// ...
}
public class UserBLImpl implements UserBLInt {
private UserDAO userDAO;
private RightDAO rightDAO;
// ...
@Override
public void assignRightToUser(int userId, int rightId) {
User u = userDAO.findById(userId);
Right right = rightDAO.findById(rightId);
// do some null checks and throw exception
user.addRight(right);
}
}
Run Code Online (Sandbox Code Playgroud)
这也显示了设计中的一个基本问题:事务不能在 DAO 层启动。它们必须从服务层启动。这使得 DAO 方法可以重用,并且服务方法可以在单个事务中使用多个 DAO。不这样做将导致服务贫乏,这些服务将调用包含整个业务逻辑的 DAO 方法,如代码所示。
这就是为什么像 EJB 或 Spring 这样的框架允许以声明方式划分事务:您不需要显式地启动和提交事务、处理异常和回滚异常。您所要做的就是使用注释将服务方法标记为事务性的。
| 归档时间: |
|
| 查看次数: |
10512 次 |
| 最近记录: |