JSF与Bean验证相结合:ConstraintViolationException

JSF与Bean验证相结合:ConstraintViolationException,jsf,bean-validation,jsf-2.2,Jsf,Bean Validation,Jsf 2.2,我尝试将JSF与Bean验证结合使用。基本上,一切正常,验证工作如预期,我得到了正确的消息,但我的Glassfish控制台上有一个例外: Warnung: EJB5184:A system exception occurred during an invocation on EJB MyEntityFacade, method: public void com.mycompany.testbv.AbstractFacade.create(java.lang.Object) Warnung:

我尝试将JSF与Bean验证结合使用。基本上,一切正常,验证工作如预期,我得到了正确的消息,但我的Glassfish控制台上有一个例外:

Warnung:   EJB5184:A system exception occurred during an invocation on EJB MyEntityFacade, method: public void com.mycompany.testbv.AbstractFacade.create(java.lang.Object)
Warnung:   javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
....
....
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.
如果使用自定义约束和预定义约束,则会发生此异常

这是我的示例代码

样本实体:

@Entity
@ValidEntity
public class MyEntity implements Serializable {

    private static final long serialVersionUID = 3104398374500914142L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Size(min = 2)
    private String name;

    public MyEntity(String name) {
        this.name = name;
    }

    public MyEntity() {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
自定义约束:

@Constraint(validatedBy = MyValidator.class)
@Target({FIELD, METHOD, TYPE})
@Retention(RUNTIME)
public @interface ValidEntity {
    String message() default "fail";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
@约束(validatedBy=MyValidator.class)
@目标({字段、方法、类型})
@保留(运行时)
公共@接口有效性{
字符串消息()默认为“失败”;
类[]组()默认值{};
Class>constraintViolations=((ConstraintViolationException)t).getConstraintViolations();
for(ConstraintViolation ConstraintViolation:constraintViolations){
FacesMessage FacesMessage=新的FacesMessage(constraintViolation.getMessage());
facesMessage.setSeverity(facesMessage.SEVERITY_错误);
addMessage(null,facesMessage);
}
}
t=t.getCause();
}
}
}
}
示例jsf页面:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head></h:head>
    <h:body>
        <h:form>
            <h:messages id="messages" />
            <h:inputText value="#{myController.text}" />
            <h:commandButton value="Save" action="#{myController.saveNewEntity()}" />
        </h:form>
    </h:body>
</html>

MyEntityFacade仅从实体管理器中持久化调用

如前所述,应用程序运行良好,正确的消息是shwon,但我希望在Glassfish控制台中避免此异常

如前所述,将persistence.xml中的验证模式设置为NONE是不可取的,因为我需要验证

我在2.2版中使用JSF,实现是Mojarra。Bean验证的版本是1.1,实现是Hibernate Validator。
应用服务器是Glassfish 4.0。

类级约束不适用于JSF。看一看。当您按下“保存”按钮时,JSF只检查名称是否至少有2个字符,并且不考虑有效性约束。另一方面,JPA抱怨bean无效并抛出异常

更新

1) @Size约束在
MyEntity.name
属性上,而在facelet中,您有
MyController.text
属性。在JSF透视图中,没有什么需要验证的。它根本不知道我的存在


2) ValidEntity总是无效的,因此JPA总是会抛出异常(除非您禁用验证),即使您在facelet中正确设置了
MyEntity.name

非常感谢您的快速回答。那么异常是由JPA引发的?但是为什么不可能正确地捕捉到它呢?正如我在问题中提到的,如果仅违反@Size约束,也会引发异常。请参阅更新。我没有看到在你的控制器中没有对MyEntityAh的引用,你在facelet中的text属性是完全正确的。是的,这只是一个测试代码,自定义验证器应该总是失败,并且应该抛出一个异常,但是我的问题,或者更确切地说,我的问题是:为什么这个异常没有完全被MyController类捕获?有些东西被捕获了,因为消息“fail”被打印到JSF消息组件,但是在GF控制台上,stacktrace(参见我的初始问题)仍然被打印。这让我有点困惑。这只是一个日志记录的问题。JPA抛出异常,Glassfish EJB容器捕获它,将其包装在EJBException中并将其写入日志文件。然而,关键是您根本没有JSF验证。JSF在调用操作之前验证数据。如果数据有效,则会调用操作,否则会自动通知用户。您无需在saveNewEntity()方法中捕获异常,请尝试直接在facelet中绑定到MyEntity.name。如果在文本输入中只键入一个字符,则不会再看到任何Glassfish错误,因为JSF将在调用facade之前处理验证。
@Named
@SessionScoped
public class MyController implements Serializable {

    private static final long serialVersionUID = -6739023629679382999L;

    @Inject
    MyEntityFacade myEntityFacade;
    String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public void saveNewEntity() {
        try {
            myEntityFacade.create(new MyEntity(text));
        } catch (Exception e) {
            Throwable t = e;
            while (t != null) {
                if (t instanceof ConstraintViolationException) {
                    FacesContext context = FacesContext.getCurrentInstance();
                    Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) t).getConstraintViolations();
                    for (ConstraintViolation<?> constraintViolation : constraintViolations) {
                        FacesMessage facesMessage = new FacesMessage(constraintViolation.getMessage());
                        facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
                        context.addMessage(null, facesMessage);
                    }
                }
                t = t.getCause();
            }
        }
    }
}
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head></h:head>
    <h:body>
        <h:form>
            <h:messages id="messages" />
            <h:inputText value="#{myController.text}" />
            <h:commandButton value="Save" action="#{myController.saveNewEntity()}" />
        </h:form>
    </h:body>
</html>