Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Jpa 如何让Eclipselink在映射的超类中激发JSR303约束?_Jpa_Eclipselink_Bean Validation - Fatal编程技术网

Jpa 如何让Eclipselink在映射的超类中激发JSR303约束?

Jpa 如何让Eclipselink在映射的超类中激发JSR303约束?,jpa,eclipselink,bean-validation,Jpa,Eclipselink,Bean Validation,EclipsLink在persist()操作期间似乎不会在作为实体的映射超类的基类中检测或激发JSR303注释约束 例如: public Base { @NotNull private Integer id; private String recordName; //other stuff (getters etc) } 然后 public class MyObject extends Base { //stuff... } 然后:

EclipsLink在persist()操作期间似乎不会在作为实体的映射超类的基类中检测或激发JSR303注释约束

例如:

public Base
{
    @NotNull
    private Integer id; 

    private String recordName; 

    //other stuff (getters etc) 
}
然后

public class MyObject
    extends Base
{
     //stuff...
}
然后:

<mapped-superclass class="Base">
    <attributes>
         <basic name="recordName">
             <column name = "NAME" />
         </basic>
    </attributes>
</mapped-superclass> 


)更多细节

在我看来,eclipse link 2.6.X到2.6.4在维护触发JSR 303 bean验证的契约方面似乎有一个巨大的缺陷。 现在,eclipselink 2.6.4只在您的子实体被直接标记为约束时触发这些验证

我有在JEE6库版本(例如eclipselink 2.4.x)下完美工作的集成测试

当我将库升级到JEE 7版本时,在ecliselink的特殊情况下,这意味着版本:2.6.1到2.6.4,它们都表现出相同的错误

到目前为止,我所分析的中断单元测试正在验证必须触发ConstraintViolationException(如NOTNULL)

因此,如果您使用一个扩展了抽象实体B的实体A,而抽象实体B是一个@MappedSuperClass。 如果在抽象实体B上发现@NotNull或任何其他此类约束,您将遇到问题。。。 在这种情况下,事情不会进展顺利

eclipselink不会触发任何约束冲突。 相反,如果您在测试中发出commit()或flush(),则DB会阻止您。 Eclipse链接将在db异常时回滚

但是,只要转到实体A并向其中注入一个虚拟字段: @NotNull 私有字符串虚拟

这足以调用验证程序(例如hibernate验证程序)

在这种情况下,我的测试仍然失败,因为现在我得到两个@NotNull约束验证,而不是一个

在下面的代码片段中,我演示了eclipselink 2.6.1上的堆栈跟踪的相关chucnk

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.
    at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.validateOnCallbackEvent(BeanValidationListener.java:108)
    at org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.prePersist(BeanValidationListener.java:77)
    at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyListener(DescriptorEventManager.java:748)
    at org.eclipse.persistence.descriptors.DescriptorEventManager.notifyEJB30Listeners(DescriptorEventManager.java:691)
    at org.eclipse.persistence.descriptors.DescriptorEventManager.executeEvent(DescriptorEventManager.java:229)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectClone(UnitOfWorkImpl.java:4314)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNotRegisteredNewObjectForPersist(UnitOfWorkImpl.java:4291)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.registerNotRegisteredNewObjectForPersist(RepeatableWriteUnitOfWork.java:521)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectForPersist(UnitOfWorkImpl.java:4233)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:507)
    at TEST_THAT_IS_PROBLEMATIC
    ... 25 more
在上面的堆栈跟踪中,您让单元测试在实体A上执行em.persist(),而本例中的实体A具有dummy@NotNull字段。因此将调用验证

eclipselink中的错误似乎是当BeanValidationListener询问BeanValidationHelper类是否受约束时:

Eclipselink的代码如下:

private void validateOnCallbackEvent(DescriptorEvent event, String callbackEventName, Class[] validationGroup) {
        Object source = event.getSource();
        boolean noOptimization = "true".equalsIgnoreCase((String) event.getSession().getProperty(PersistenceUnitProperties.BEAN_VALIDATION_NO_OPTIMISATION));
        boolean shouldValidate = noOptimization || beanValidationHelper.isConstrained(source.getClass());
        if (shouldValidate) {
            Set<ConstraintViolation<Object>> constraintViolations = getValidator(event).validate(source, validationGroup);
            if (constraintViolations.size() > 0) {
                // There were errors while call to validate above.
                // Throw a ConstrainViolationException as required by the spec.
                // The transaction would be rolled back automatically
                // TODO need to I18N this.
                throw new ConstraintViolationException(
                        "Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'" +
                                callbackEventName + "'. Please refer to embedded ConstraintViolations for details.",
                        (Set<ConstraintViolation<?>>) (Object) constraintViolations); /* Do not remove the explicit
                        cast. This issue is related to capture#a not being instance of capture#b. */
            }
        }
    }
 private Boolean detectConstraints(Class<?> clazz) {
        for (Field f : ReflectionUtils.getDeclaredFields(clazz)) {
            for (Annotation a : f.getDeclaredAnnotations()) {
                final Class<? extends Annotation> type = a.annotationType();
                if (KNOWN_CONSTRAINTS.contains(type.getName())){
                    return true;
                }
                // Check for custom annotations on the field (+ check inheritance on class annotations).
                // Custom bean validation annotation is defined by having @Constraint annotation on its class.
                for (Annotation typesClassAnnotation : type.getAnnotations()) {
                    final Class<? extends Annotation> classAnnotationType = typesClassAnnotation.annotationType();
                    if (Constraint.class == classAnnotationType) {
                        KNOWN_CONSTRAINTS.add(type.getName());
                        return true;
                    }
                }
            }
        }
返回false,这是完全错误的

最后,如果检查BeanValidationHelper的实现,代码的初始部分如下所示:

private void validateOnCallbackEvent(DescriptorEvent event, String callbackEventName, Class[] validationGroup) {
        Object source = event.getSource();
        boolean noOptimization = "true".equalsIgnoreCase((String) event.getSession().getProperty(PersistenceUnitProperties.BEAN_VALIDATION_NO_OPTIMISATION));
        boolean shouldValidate = noOptimization || beanValidationHelper.isConstrained(source.getClass());
        if (shouldValidate) {
            Set<ConstraintViolation<Object>> constraintViolations = getValidator(event).validate(source, validationGroup);
            if (constraintViolations.size() > 0) {
                // There were errors while call to validate above.
                // Throw a ConstrainViolationException as required by the spec.
                // The transaction would be rolled back automatically
                // TODO need to I18N this.
                throw new ConstraintViolationException(
                        "Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'" +
                                callbackEventName + "'. Please refer to embedded ConstraintViolations for details.",
                        (Set<ConstraintViolation<?>>) (Object) constraintViolations); /* Do not remove the explicit
                        cast. This issue is related to capture#a not being instance of capture#b. */
            }
        }
    }
 private Boolean detectConstraints(Class<?> clazz) {
        for (Field f : ReflectionUtils.getDeclaredFields(clazz)) {
            for (Annotation a : f.getDeclaredAnnotations()) {
                final Class<? extends Annotation> type = a.annotationType();
                if (KNOWN_CONSTRAINTS.contains(type.getName())){
                    return true;
                }
                // Check for custom annotations on the field (+ check inheritance on class annotations).
                // Custom bean validation annotation is defined by having @Constraint annotation on its class.
                for (Annotation typesClassAnnotation : type.getAnnotations()) {
                    final Class<? extends Annotation> classAnnotationType = typesClassAnnotation.annotationType();
                    if (Constraint.class == classAnnotationType) {
                        KNOWN_CONSTRAINTS.add(type.getName());
                        return true;
                    }
                }
            }
        }
仅返回当前类的字段,而不返回父类的字段

同时,我强迫自己做的是,在BeanValidationHelper中设置此解决方案,以冻结已损坏的算法,从而将类检测为需要验证的类:

 @Transient
    @NotNull
    private final char waitForEclipseLinkToFixTheVersion264 = 'a';
通过如上所述的操作,您的代码已经清楚地标记了一个块,您可以在将来删除该块。 由于磁场是瞬态的。。。嘿,它不会改变你的数据库

同时请注意,eclipselink论坛现在有更多的信息。 这个bug比不正确跟踪BeanValidationListner.class中何时需要“beanValidation”更深入。 这个bug有第二个深度。 随eclipse链接提供的BeanValidationListner.class也未注册以下内容的任何实现: PreWriteEvent和用于描述符ReventManager.PreInsertEvent

因此,当“DeferredCachedDetectionPolocy.class”为calculateInChanges()时,如果实体A具有JSR字段,但仍然没有获得JSR 303验证。 这很可能发生在您身上,因为您的enitya是: T0:持久化并通过ok验证 T1:在同一事务中修改peristed实体,并且当calculateChanges调用事件Litenter时。 BeanValidationListner.class不关心预插入事件。 它只是假设验证是由prePersist完成的,并且根本不调用验证

这方面的工作,我还不确定。 我将研究如何在PreInserPase期间注册事件列表器,这与BeanValidationListner的功能相同。 或者,我将在本地修补BeanValidationListner.class以订阅PreINsert事件

我已经修改了其他人维护的库的代码,所以我将首先使用我们自己的eventListner作为这个bug的临时解决方案

添加允许验证两个bug的存储库。

对于bug 2,在EclipseLink2.6.4修复其bean验证编排逻辑之前,以下EventListner服务器可以临时解决

package jpa.eclipselink.test.bug2workaround;

import java.util.Map;

import javax.validation.Validation;
import javax.validation.ValidatorFactory;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy;
import org.eclipse.persistence.internal.jpa.deployment.BeanValidationInitializationHelper;
import org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener;

/**
 * Temporary work-around for JSR 303 bean validation flow in eclipselink.
 *
 * <P>
 * Problem: <br>
 * The
 * {@link DeferredChangeDetectionPolicy#calculateChanges(Object, Object, boolean, org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, org.eclipse.persistence.descriptors.ClassDescriptor, boolean)}
 * during a flush will do one of the following: <br>
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
 * or <br>
 *
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreUpdateEvent, writeQuery)); }
 *
 * <P>
 * WHe it does
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
 * the {@link BeanValidationListener} will not do anything. We want it to do bean validation.
 */
public class ForceBeanManagerValidationOnPreInsert extends DescriptorEventAdapter {

    private static final Class[] DUMMY_GROUP_PARAMETER = null;

    /**
     * This is is the EJB validator that eclipselink uses to do JSR 303 validations during pre-update, pre-delete,
     * pre-persist, but not pre-insert.
     *
     * Do not access this field directly. Use the {@link #getBeanValidationListener(DescriptorEvent)} api to get it, as
     * this api will initialize the tool if necessary.
     */
    BeanValidationListener beanValidationListener = null;

    final Object beanValidationListenerLock = new Object();

    /**
     *
     */
    public ForceBeanManagerValidationOnPreInsert() {
        super();

    }

    /**
     * As a work-around we want to do bean validation that the container is currently not doing.
     */
    @Override
    public void preInsert(DescriptorEvent event) {
        // (a) get for ourselves an instances of the eclipse link " Step 4 - Notify internal listeners."
        // that knows how to run JSR 303 validations on beans associated to descriptor events
        BeanValidationListener eclipseLinkBeanValidationListenerTool = getBeanValidationListener(event);

        // (b) let the validation listener run its pre-update logic on a preInsert it serves our purpose
        eclipseLinkBeanValidationListenerTool.preUpdate(event);

    }

    /**
     * Returns the BeanValidationListener that knows how to do JSR 303 validation. Creates a new instance if needed,
     * otherwise return the already created listener.
     *
     * <P>
     * We can only initialize our {@link BeanValidationListener} during runtime, to get access to the JPA persistence
     * unit properties. (e.g. to the validation factory).
     *
     * @param event
     *            This event describes an ongoing insert, updetae, delete event on an entity and for which we may want
     *            to force eclipselink to kill the transaction if a JSR bean validation fails.
     * @return the BeanValidationListener that knows how to do JSR 303 validation.
     */
    protected BeanValidationListener getBeanValidationListener(DescriptorEvent event) {
        synchronized (beanValidationListenerLock) {
            // (a) initializae our BeanValidationListener if needed
            boolean initializationNeeded = beanValidationListener == null;
            if (initializationNeeded) {
                beanValidationListener = createBeanValidationListener(event);
            }

            // (b) return the validation listerner that is normally used by eclipse link
            // for pre-persist, pre-update and pre-delete so that we can force it run on pre-insert
            return beanValidationListener;
        }

    }

    /**
     * Creates a new instance of the {@link BeanValidationListener} that comes with eclipse link.
     *
     * @param event
     *            the ongoing db event (e.g. pre-insert) where we want to trigger JSR 303 bean validation.
     *
     * @return A new a new instance of the {@link BeanValidationListener} .
     */
    protected BeanValidationListener createBeanValidationListener(DescriptorEvent event) {
        Map peristenceUnitProperties = event.getSession().getProperties();
        ValidatorFactory validatorFactory = getValidatorFactory(peristenceUnitProperties);
        return new BeanValidationListener(validatorFactory, DUMMY_GROUP_PARAMETER, DUMMY_GROUP_PARAMETER,
                DUMMY_GROUP_PARAMETER);
    }

    /**
     * Snippet of code taken out of {@link BeanValidationInitializationHelper}
     *
     * @param puProperties
     *            the persistence unit properties that may be specifying the JSR 303 validation factory.
     * @return the validation factory that can check if a bean is violating business rules. Almost everyone uses
     *         hirbernate JSR 303 validation.
     */
    protected ValidatorFactory getValidatorFactory(Map puProperties) {
        ValidatorFactory validatorFactory = (ValidatorFactory) puProperties
                .get(PersistenceUnitProperties.VALIDATOR_FACTORY);

        if (validatorFactory == null) {
            validatorFactory = Validation.buildDefaultValidatorFactory();
        }
        return validatorFactory;
    }

}

亲切问候。

我觉得org.eclipse.persistence.internal.jpa.metadata.beanvalidation.BeanValidationHelper中有一个bug。您是否检查了他们的bug追踪器或报告了一个bug。Bean验证和jpa是相互依赖的,所以可以使用jpa注释,但只使用Bean验证注释。您的示例在imo中应该有效。看起来像EclipseLink 2中的问题。6.x.有关更多详细信息,请参见此处()。
package jpa.eclipselink.test.bug2workaround;

import java.util.Map;

import javax.validation.Validation;
import javax.validation.ValidatorFactory;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy;
import org.eclipse.persistence.internal.jpa.deployment.BeanValidationInitializationHelper;
import org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener;

/**
 * Temporary work-around for JSR 303 bean validation flow in eclipselink.
 *
 * <P>
 * Problem: <br>
 * The
 * {@link DeferredChangeDetectionPolicy#calculateChanges(Object, Object, boolean, org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet, org.eclipse.persistence.internal.sessions.UnitOfWorkImpl, org.eclipse.persistence.descriptors.ClassDescriptor, boolean)}
 * during a flush will do one of the following: <br>
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
 * or <br>
 *
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreUpdateEvent, writeQuery)); }
 *
 * <P>
 * WHe it does
 * {@code descriptor.getEventManager().executeEvent(new DescriptorEvent(DescriptorEventManager.PreInsertEvent, writeQuery)); }
 * the {@link BeanValidationListener} will not do anything. We want it to do bean validation.
 */
public class ForceBeanManagerValidationOnPreInsert extends DescriptorEventAdapter {

    private static final Class[] DUMMY_GROUP_PARAMETER = null;

    /**
     * This is is the EJB validator that eclipselink uses to do JSR 303 validations during pre-update, pre-delete,
     * pre-persist, but not pre-insert.
     *
     * Do not access this field directly. Use the {@link #getBeanValidationListener(DescriptorEvent)} api to get it, as
     * this api will initialize the tool if necessary.
     */
    BeanValidationListener beanValidationListener = null;

    final Object beanValidationListenerLock = new Object();

    /**
     *
     */
    public ForceBeanManagerValidationOnPreInsert() {
        super();

    }

    /**
     * As a work-around we want to do bean validation that the container is currently not doing.
     */
    @Override
    public void preInsert(DescriptorEvent event) {
        // (a) get for ourselves an instances of the eclipse link " Step 4 - Notify internal listeners."
        // that knows how to run JSR 303 validations on beans associated to descriptor events
        BeanValidationListener eclipseLinkBeanValidationListenerTool = getBeanValidationListener(event);

        // (b) let the validation listener run its pre-update logic on a preInsert it serves our purpose
        eclipseLinkBeanValidationListenerTool.preUpdate(event);

    }

    /**
     * Returns the BeanValidationListener that knows how to do JSR 303 validation. Creates a new instance if needed,
     * otherwise return the already created listener.
     *
     * <P>
     * We can only initialize our {@link BeanValidationListener} during runtime, to get access to the JPA persistence
     * unit properties. (e.g. to the validation factory).
     *
     * @param event
     *            This event describes an ongoing insert, updetae, delete event on an entity and for which we may want
     *            to force eclipselink to kill the transaction if a JSR bean validation fails.
     * @return the BeanValidationListener that knows how to do JSR 303 validation.
     */
    protected BeanValidationListener getBeanValidationListener(DescriptorEvent event) {
        synchronized (beanValidationListenerLock) {
            // (a) initializae our BeanValidationListener if needed
            boolean initializationNeeded = beanValidationListener == null;
            if (initializationNeeded) {
                beanValidationListener = createBeanValidationListener(event);
            }

            // (b) return the validation listerner that is normally used by eclipse link
            // for pre-persist, pre-update and pre-delete so that we can force it run on pre-insert
            return beanValidationListener;
        }

    }

    /**
     * Creates a new instance of the {@link BeanValidationListener} that comes with eclipse link.
     *
     * @param event
     *            the ongoing db event (e.g. pre-insert) where we want to trigger JSR 303 bean validation.
     *
     * @return A new a new instance of the {@link BeanValidationListener} .
     */
    protected BeanValidationListener createBeanValidationListener(DescriptorEvent event) {
        Map peristenceUnitProperties = event.getSession().getProperties();
        ValidatorFactory validatorFactory = getValidatorFactory(peristenceUnitProperties);
        return new BeanValidationListener(validatorFactory, DUMMY_GROUP_PARAMETER, DUMMY_GROUP_PARAMETER,
                DUMMY_GROUP_PARAMETER);
    }

    /**
     * Snippet of code taken out of {@link BeanValidationInitializationHelper}
     *
     * @param puProperties
     *            the persistence unit properties that may be specifying the JSR 303 validation factory.
     * @return the validation factory that can check if a bean is violating business rules. Almost everyone uses
     *         hirbernate JSR 303 validation.
     */
    protected ValidatorFactory getValidatorFactory(Map puProperties) {
        ValidatorFactory validatorFactory = (ValidatorFactory) puProperties
                .get(PersistenceUnitProperties.VALIDATOR_FACTORY);

        if (validatorFactory == null) {
            validatorFactory = Validation.buildDefaultValidatorFactory();
        }
        return validatorFactory;
    }

}
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DESCRIMINATOR", length = 32)
@DiscriminatorValue("Bug2WorkAround")
@Entity
@EntityListeners({ ForceBeanManagerValidationOnPreInsert.class })
public class Bug2Entity2WithWorkAround extends GenericEntity {