我的任务是为自定义验证创建注释。这是由于handling database constraint violations nicely.的一些问题,我做了什么回应这是相对简单。我为需要它的一个域类创建了一个类级CustomConstraint。我得到了我的当前结果如下:自定义验证注解引入ConcurrentModificationException
@UniqueLocation译注:
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = UniqueLocationValidator.class)
@Documented
public @interface UniqueLocation {
String message() default "must be unique!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
这不是壮观,实际上它被复制几乎逐字从hibernate documentation.
我继续创建我的UniqueLocationValidator
,并遇到了在那里使用持久化上下文的问题。我想运行一个防御选择,因此试图注入我的应用程序范围广泛的@Produces @PersistenceContext EntityManager
。
为此我包括JBoss Seam的使用它的InjectingConstraintValidatorFactory
配置我的validation.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">
<constraint-validator-factory>
org.jboss.seam.validation.InjectingConstraintValidatorFactory
</constraint-validator-factory>
</validation-config>
运行到一些问题,Creating Constraint Violations后,这是我的验证实际效果的:
@ManagedBean
public class UniqueLocationValidator implements
ConstraintValidator<UniqueLocation, Location> {
// must not return a result for name-equality on the same Id
private final String QUERY_STRING = "SELECT * FROM Location WHERE locationName = :value AND id <> :id";
@Inject
EntityManager entityManager;
private String constraintViolationMessage;
@Override
public void initialize(final UniqueLocation annotation) {
constraintViolationMessage = annotation.message();
}
@Override
public boolean isValid(final Location instance,
final ConstraintValidatorContext context) {
if (instance == null) {
// Recommended, instead use explicit @NotNull Annotation for
// validating non-nullable instances
return true;
}
if (duplicateLocationExists(instance)) {
createConstraintViolations(context);
return false;
} else {
return true;
}
}
private void createConstraintViolations(
final ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(constraintViolationMessage)
.addNode("locationName").addConstraintViolation();
}
private boolean duplicateLocationExists(final Location location) {
final String checkedValue = location.getLocationName();
final long id = location.getId();
Query defensiveSelect = entityManager.createNativeQuery(QUERY_STRING)
.setParameter("value", checkedValue).setParameter("id", id);
return !defensiveSelect.getResultList().isEmpty();
}
}
对于我目前的配置,现在到了真正的牛肉,这个问题:
当我运行下面的代码后r从用户那里获取一个动作,这个东西奇妙而正确地将一个重复的位置名称标记为无效。当位置名称不重复时,持久化工作也很好。
public long add(@Valid final Location location) {
entityManager.persist(location);
return location.getId();
}
记住,这里的entityManager
和entityManager
在UniqueLocationValidator都通过焊接CDI注入从上述@PersistenceContext EntityManager的。
什么不工作如下:
public long update(@Valid final Location location){
entityManager.merge(location);
return location.getId();
}
调用此代码,我得到一个相对较短的堆栈跟踪,具有ConcurrentModificationException
的根源。
我既不明白为什么会这样,也不知道如何解决这个问题。我没有试图明确地多线程化我的应用程序,所以这应该由我作为应用程序服务器使用的JBoss 7.1.1-Final来管理。