当前位置: 首页 > 面试题库 >

强制事务回退Seam中的验证错误

安经纶
2023-03-14
问题内容

快速版本:我们正在寻找一种方法来强制事务回退,当在后备Bean上执行方法期间发生特定情况时,回退事务,但是我们希望进行回退而不必向用户显示通用的500错误页面。相反,我们希望用户看到她刚提交的表单以及一个FacesMessage来指示问题所在。

较长的版本:我们有一些支持bean,这些bean使用组件来执行数据库中的一些相关操作(使用JPA /
Hibernate)。在此过程中,某些数据库操作发生后可能会发生错误。这可能是出于几种不同的原因,但是出于这个问题,我们假设在发生某些DB写入之后检测到验证错误,而在写入之前无法检测到该错误。发生这种情况时,我们希望确保到目前为止所有的数据库更改都将被回滚。Seam可以处理此问题,因为如果您从当前FacesRequest中抛出RuntimeException,Seam将回滚当前事务。

问题是向用户显示了一个通用错误页面。在我们的案例中,我们实际上希望向用户显示她所在的页面,其中包含关于出了什么问题的描述性消息,并有机会纠正导致问题的错误输入。我们想出的解决方案是从组件中抛出异常,该异常会发现带有注释的验证问题:

@ApplicationException( rollback = true )

然后,我们的后备bean可以捕获此异常,并假设抛出该异常的组件已经发布了适当的FacesMessage,并且只需返回null即可将用户带回显示错误的输入页面。ApplicationException注释告诉Seam回滚事务,我们没有向用户显示通用错误页面。

这在我们第一次使用它时恰好只是在做插入操作的地方效果很好。我们尝试使用它的第二个地方,我们必须在此过程中删除某些内容。在第二种情况下,如果没有验证错误,则一切正常。如果确实发生验证错误,则会引发rollback
Exception并将该事务标记为回滚。即使没有任何数据库修改被回滚,当用户修复错误的数据并重新提交页面时,我们也会得到:

java.lang.IllegalArgumentException: Removing a detached instance

分离的实例是从另一个对象延迟加载的(存在多对一关系)。实例化后备bean时将加载该父对象。由于验证错误后事务已回滚,因此该对象现在已分离。

我们的下一步是将该页面从对话范围更改为页面范围。当我们这样做时,Seam甚至无法在验证错误后呈现页面,因为我们的页面必须命中数据库才能呈现,并且该事务已被标记为可回滚。

所以我的问题是:其他人如何干净地处理错误并同时适当地管理事务?更好的是,如果有人可以发现我做错的事情相对容易修复,那么我希望能够使用我们现在拥有的一切。

我已经阅读了统一错误页面和异常处理上的Seam
Framework文章,但这更适合您的应用程序可能遇到的更常见的错误。

更新 :这是一些伪代码和页面流的详细信息。

在这种情况下,假设我们正在编辑某些用户的信息(在这种情况下,我们实际上并未与用户打交道,但我不会发布实际的代码)。

编辑功能的edit.page.xml文件包含一个用于RESTful URL的简单重写模式和两个导航规则:

  1. 如果结果编辑成功,则将用户重定向到相应的视图页面以查看更新的信息。
  2. 如果用户单击“取消”按钮,则将用户重定向到相应的视图页面。

edit.xhtml非常基本,其中包含可以编辑的用户所有部分的字段。

支持bean具有以下注释:

@Name( "editUser" )
@Scope( ScopeType.PAGE )

有一些注入的组件,例如User:

@In
@Out( scope = ScopeType.CONVERSATION ) // outjected so the view page knows what to display
protected User user;

我们在支持bean上有一个save方法,该方法将工作委派给用户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;
}

我们的GuaranteedRollbackException看起来像:

@ApplicationException( rollback = true )
public class GuaranteedRollbackException extends RuntimeException
{
    public GuaranteedRollbackException(String message) {
        super(message);
    }
}

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 );
}

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
    ...
}

ModifyUser.modifyUser与ModifyOrg类似。

现在(您将不得不接受这一飞跃,因为这不一定听起来像是此User场景的问题,但这是针对我们正在做的事情)假定更改组织会导致ModifyUser失败,验证,但无法提前验证此失败。我们已经在当前的txn中将组织更新写入了db,但是由于用户修改未能通过验证,因此GuaranteedRollbackException将标记事务回滚。通过此实现,当我们再次呈现编辑页面以显示验证器添加的错误消息时,我们将无法在当前范围内使用数据库。渲染时,我们点击db以使某些内容显示在页面上,这是不可能的,因为Session无效:

由org.hibernate.LazyInitializationException引起,并带有消息:“无法初始化代理-没有会话”


问题答案:

我必须同意@duffymo关于在启动事务之前进行验证的信息。处理数据库异常并将其呈现给用户非常困难。

出现分离异常的原因很可能是因为您认为已向数据库中写入了某些内容,然后调用了对象的remove或refresh,然后尝试再次编写一些内容。

你需要做的,而不是什么是创建一个long-running conversationflushMode设置为MANUAL。然后,您开始保存内容,然后可以执行验证,如果可以,则再次保存。完成后,一切顺利,您致电entityManager.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";
}
}

请注意,我们现在保存人,但尚未刷新交易。但是,它将检查您在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";
}

您甚至可以在这里放入内容,try catch并且只有在没有发生异常的情况下才返回成功,这样您就不会被抛出错误页面。

更新资料

您可以尝试以下方法:

@PersistenceContext(type=EXTENDED)
EntityManager em;

这将使有状态Bean具有EJB3扩​​展的持久性上下文。只要bean存在,在查询中检索到的消息就保持受管状态,因此对有状态bean的任何后续方法调用都可以更新它们,而无需对EntityManager进行任何显式调用。这可以避免您的LazyInitializationException。您现在可以使用
em.refresh(user);



 类似资料:
  • 情景故事时间: 我“继承”了一个程序,一个用于访问数据库的相当简单的webservice。该程序在某个地方有一个缺陷:它试图更新一个没有更新授权的表。该程序只具有更新数据库队列的权限(Oracle),以保存谁访问了什么信息。这是不受欢迎的行为,现在我纠正了它。注意:这与这个问题本身无关,它只是导致我提出这个问题的原因。 该程序使用Spring+Hibernate来管理和访问数据和事务。 因为程序的

  • 我过去认为,在分层应用程序(控制器或服务)中,将验证逻辑放在何处并不重要,但最近正在开发需要事务的服务(使用spring)。Spring使用方面创建代理,Spring代码如下所示: org.springframework.transaction.interceptor.事务支持 所以从我这里看到的,spring首先打开事务,然后执行代码。考虑到验证可能会失败,并且根本不需要DB调用,这是否意味着将

  • 我们有CXF SOAP over JMS服务,并使用schema validation enabled属性启用了XSD验证。使用此配置,对于所有类型的错误,都会发生事务回滚。我们希望停止XSD验证错误的回滚,并允许运行时错误的回滚。 我们添加了OneException(ValidationException.class)。已处理(true),但对于JMS上的SOAP,当发生验证异常时,不会执行下面

  • 对于错误的响应,是否可能在响应中返回验证注释消息?我认为这是可能的,但我注意到我们的项目没有给出详细的不良请求信息。 如果请求缺少IDField,我希望返回“IDField is required”。我用的是泽西2.0。我看到的回应是...

  • 我有一个spring rest服务,它接受Person对象。Person对象具有名称、电话号码和电子邮件。当添加一个人时,电话号码是强制性的。如果存在电话号码属性,它将使用或或验证是否为空。但是如果属性不存在,验证就不起作用。请建议提供任何验证注释,以检查JSON请求中是否存在该属性。 以下是请求体测试用例 {“name”:“anu”,“phonenumber”:“”,“email”:“test@

  • 问题内容: 我有一个用于Web应用程序的ASP.net Web服务,该服务根据我调用的函数向我返回XML或JSON数据。到目前为止,这一直很好,但是我遇到了一个问题。我想在我的页面上创建一个“导出”链接,该链接将下载一个JSON文件。链接的格式非常简单: 您可能会想到,这应该导出项目2。到目前为止,很好,是吗? 问题是,由于我没有特别要求接受的内容类型是JSON,因此ASP.net绝对拒绝发回除X