Chr*_*ams 6 java hibernate seam jpa transactions
快速版:我们正在寻找一种方式来强制交易时的方法上的支持bean的执行过程中出现的具体情况来回滚,但我们希望回滚,而无需向用户显示一个通用的500错误页面发生.相反,我们希望用户看到她刚刚提交的表单和一个表明问题所在的FacesMessage.
长版本:我们有一些支持bean,它们使用组件在数据库中执行一些相关的操作(使用JPA/Hibernate).在此过程中,某些数据库操作发生后可能会发生错误.这可能是出于几个不同的原因,但是对于这个问题,我们假设在发生一些DB写入之后检测到的验证错误在写入发生之前是不可检测的.发生这种情况时,我们希望确保到目前为止所有的数据库更改都将被回滚.Seam可以解决这个问题,因为如果你从当前的FacesRequest中抛出一个RuntimeException,Seam将回滚当前的事务.
这个问题是用户显示了一般错误页面.在我们的例子中,我们实际上希望用户能够显示她所在的页面,并提供有关出错的描述性消息,并有机会纠正导致问题的错误输入.我们提出的解决方案是从发现带有注释的验证问题的组件中抛出异常:
@ApplicationException( rollback = true )
Run Code Online (Sandbox Code Playgroud)
然后我们的支持bean可以捕获此异常,假设抛出它的组件已发布相应的FacesMessage,并且只需返回null以将用户带回输入页面并显示错误.ApplicationException注释告诉Seam回滚事务,我们没有向用户显示一般错误页面.
这在我们使用它的第一个地方运作良好,恰好只是插入.我们试图使用它的第二个地方,我们必须在此过程中删除一些东西.在第二种情况下,如果没有验证错误,一切正常.如果确实发生了验证错误,则抛出回滚异常并将事务标记为回滚.即使没有回滚数据库修改,当用户修复错误数据并重新提交页面时,我们得到:
java.lang.IllegalArgumentException: Removing a detached instance
Run Code Online (Sandbox Code Playgroud)
分离的实例从另一个对象延迟加载(存在多对一关系).在实例化辅助bean时加载该父对象.由于事务在验证错误后回滚,因此该对象现在已分离.
我们的下一步是将此页面从会话范围更改为页面范围.当我们这样做时,Seam甚至无法在验证错误之后呈现页面,因为我们的页面必须命中数据库才能呈现并且事务已标记为回滚.
所以我的问题是:其他人如何干净利落地处理错误并同时正确管理交易?更好的是,我希望能够使用我们现在拥有的所有东西,如果有人能发现我做错的东西,那将是相对容易修复的.
我已经阅读了有关统一错误页面和异常处理的Seam Framework文章,但这更倾向于应用程序可能遇到的更一般的错误.
更新:这里有一些psudo代码和页面流的详细信息.
在这种情况下,假设我们正在编辑一些用户的信息(在这种情况下我们实际上并没有处理用户,但我不打算发布实际的代码).
编辑功能的edit.page.xml文件包含RESTful URL的简单重写模式和两个导航规则:
edit.xhtml非常基本,可以编辑用户的所有部分的字段.
辅助bean具有以下注释:
@Name( "editUser" )
@Scope( ScopeType.PAGE )
Run Code Online (Sandbox Code Playgroud)
有一些注入的组件,如用户:
@In
@Out( scope = ScopeType.CONVERSATION ) // outjected so the view page knows what to display
protected User user;
Run Code Online (Sandbox Code Playgroud)
我们在backing bean上有一个save方法,它为用户保存委派工作:
public String save()
{
try
{
userManager.modifyUser( user, newFName, newLName, newType, newOrgName );
}
catch ( GuaranteedRollbackException grbe )
{
log.debug( "Got GuaranteedRollbackException while modifying a user." );
return null;
}
return USER_EDITED;
}
Run Code Online (Sandbox Code Playgroud)
我们的GuaranteedRollbackException如下所示:
@ApplicationException( rollback = true )
public class GuaranteedRollbackException extends RuntimeException
{
public GuaranteedRollbackException(String message) {
super(message);
}
}
Run Code Online (Sandbox Code Playgroud)
UserManager.modifyUser看起来像这样:
public void modifyUser( User user, String newFName, String newLName, String type, String newOrgName )
{
// change the user - org relationship
modifyUser.modifyOrg( user, newOrgName );
modifyUser.modifyUser( user, newFName, newLName, type );
}
Run Code Online (Sandbox Code Playgroud)
ModifyUser.modifyOrg做了类似的事情
public void modifyOrg( User user, String newOrgName )
{
if (!userValidator.validateUserOrg( user, newOrgName ))
{
// maybe the org doesn't exist something. we don't care, the validator
// will add the appropriate error message for us
throw new GauaranteedRollbackException( "couldn't validate org" );
}
// do stuff here to change the user stuff
...
}
Run Code Online (Sandbox Code Playgroud)
ModifyUser.modifyUser类似于modifyOrg.
现在,(你将不得不采取这一飞跃与我,因为它不一定听起来就像是这个用户场景的一个问题,但它是我们正在做的事情)假设改变组织造成modifyUser以失败验证,但提前验证此故障是不可能的.我们已经在当前的txn中向数据库写了组织更新,但由于用户修改无法验证,因此GuaranteedRollbackException将标记要回滚的事务.通过此实现,当我们再次呈现编辑页面以显示验证器添加的错误消息时,我们无法在当前作用域中使用DB.在渲染时,我们点击数据库以获取在页面上显示的内容,但这是不可能的,因为会话无效:
由org.hibernate.LazyInitializationException引起的消息:"无法初始化代理 - 没有会话"
我必须同意 @duffymo 关于在交易启动之前进行验证的观点。处理数据库异常并将其呈现给用户是相当困难的。
出现分离异常的原因很可能是因为您认为已向数据库写入了某些内容,然后对对象调用了删除或刷新,然后尝试再次写入内容。
您需要做的是创建一个设置long-running conversation为. 然后你开始持久化东西,然后你可以执行你的验证,如果没问题,你再次持久化。完成并一切顺利后,您可以致电。这会将所有内容保存到数据库中。flushModeMANUALentityManager.flush()
如果某件事失败了,你就不会冲水。你只是return null或"error"有一些消息。让我向您展示一些伪代码。
假设您有一个人和组织实体。现在,您需要先存储人员,然后才能将人员放入组织。
private Person person;
private Organization org;
@Begin(join=true,FlushMode=MANUAL) //yes syntax is wrong, but you get the point
public String savePerson() {
//Inside some save method, and person contains some data that user has filled through a form
//Now you want to save person if they have name filled in (yes I know this example should be done from the view, but this is only an example
try {
if("".equals(person.getName()) {
StatusMessages.instance().add("User needs name");
return "error"; //or null
}
entityManager.save(person);
return "success";
} catch(Exception ex) {
//handle error
return "failure";
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们现在保存了人员,但我们还没有刷新交易。然而,它会检查您在entitybean 上设置的约束。(@NotNull、@NotEmpty 等)。所以它只会模拟保存。
现在你可以为人保留组织。
@End(FlushMode=MANUAL) //yes syntax is wrong, but you get the point
public String saveOrganization() {
//Inside some save method, and organization contains some data that user has filled through a form, or chosen from combobox
org.setPerson(person); //Yes this is only demonstration and should have been collection (OneToMany)
//Do some constraint or validation check
entityManager.save(org);
//Simulate saving org
//if everything went ok
entityManager.flush() //Now person and organization is finally stored in the database
return "success";
}
Run Code Online (Sandbox Code Playgroud)
在这里你甚至可以放入一些东西try catch,只有在没有发生异常的情况下才返回成功,这样你就不会被抛出到错误页面。
更新
你可以试试这个:
@PersistenceContext(type=EXTENDED)
EntityManager em;
Run Code Online (Sandbox Code Playgroud)
这将使有状态 bean 具有 EJB3 扩展持久性上下文。只要 bean 存在,查询中检索到的消息就保持托管状态,因此对有状态 bean 的任何后续方法调用都可以更新它们,而无需对 EntityManager 进行任何显式调用。这可能会避免您的 LazyInitializationException。您现在可以使用
em.refresh(user);
| 归档时间: |
|
| 查看次数: |
4290 次 |
| 最近记录: |