These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: Hibernate custom validator fails on one to many collection
PostPosted: Tue Jul 28, 2009 9:37 am 
Newbie

Joined: Tue Jul 28, 2009 9:01 am
Posts: 3
Hi there,

I am using Hibernate via JPA, in other words EntityManager. I have written a custom validator annotation that checks a OneToMany relationship for the size of the Set:

Code:
...

public class AssertOptionsValidator implements Validator<AssertOptions>,
      PropertyConstraint {

   @Override
   public void initialize(AssertOptions arg0) {}

   @SuppressWarnings("unchecked")
   @Override
   public boolean isValid(Object arg0) {
      if (!(arg0 instanceof Set)) return false;
      Set<Option> options = (Set<Option>) arg0;
      if (options.size() < 2) return false;
      int correctCount = 0;
      Iterator iter = options.iterator();
      while (iter.hasNext()) {
         Option option = (Option) iter.next();
         if (option.isCorrect()) correctCount++;
      }
      return (correctCount == 1);
   }

   @Override
   public void apply(Property arg0) {}
}


My model is something like this:

Quiz-<Question-<Option

Where a quiz has many questions and a question has many options. If I create the relevant objects and call em.persist(quiz) from within a transactional context, eg:

Code:
...

@Repository
@Transactional
public class QuizDaoImpl extends AbstractBaseDao implements QuizDao {

   protected EntityManager em;

   @PersistenceContext
   public void setEntityManager(EntityManager em) {
      this.em = em;
   }

   @Override
   public void addQuiz(Quiz quiz) {
      em.persist(quiz);
   }

   @Override
   public void updateQuiz(Quiz quiz) {
      em.merge(quiz);
   }
}


Everything works fine. However, if I create the quiz, persist that, then add questions and options to the quiz after the initial transaction, then attempt to merge it, the arg passed to validator.isValid has a zero length, even though I have added Options to the Question:

Code:
...
Quiz quiz = new Quiz();
quizDao.addQuiz(quiz);
Question question = new Question();
question.addOption(new Option());
question.addOption(new Option());
quiz.addQuestion(question);
quizDao.updateQuiz(quiz);


I've checked in the debugger and the length of the Set is clearly zero even though I have added Options to Question.

I have set all relevant OneToMany mappings to CascadeType=ALL. All of the fetchTypes for those mappings are eager.

Does anyone have any idea why the validator should work for persisting a new Quiz object but should fail for merging an existing Quiz object with new Questions and Options?

Thanks a lot,


Matt


Top
 Profile  
 
 Post subject: Re: Hibernate custom validator fails on one to many collection
PostPosted: Tue Jul 28, 2009 10:18 am 
Newbie

Joined: Tue Jul 28, 2009 9:01 am
Posts: 3
I've reproduced this with a simplified test case:

Code:
...
@Entity
@Table(name="test")
public class Test extends BaseEntity {

   private Set<Unit> units = new HashSet<Unit>();
   
   public Test() {}
   
   @OneToMany(mappedBy="test", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
   @AssertTest
   public Set<Unit> getUnits() {
      return units;
   }

   public void setUnits(Set<Unit> units) {
      this.units = units;
   }
   
   public void addUnit(Unit unit) {
      unit.setTest(this);
      units.add(unit);
   }
}


Code:
...
@Entity
@Table(name="unit")
public class Unit extends BaseEntity {

   private Test test;
   
   public Unit() {}
   
   @ManyToOne(optional=false)
   public Test getTest() {
      return test;
   }

   public void setTest(Test test) {
      this.test = test;
   }
}


Code:
...
@Repository
@Transactional
public class TestDaoImpl implements TestDao {

   protected EntityManager em;

   @PersistenceContext
   public void setEntityManager(EntityManager em) {
      this.em = em;
   }

   @Override
   public void addTest(Test test) {
      em.persist(test);
   }

   @Override
   public void updateTest(Test test) {
      em.merge(test);
   }
}


Code:
...
@ValidatorClass(AssertTestValidator.class)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AssertTest {
   String message() default "Test must have at least 1 unit";
}


Code:
...
public class AssertTestValidator implements Validator<AssertTest>,
      PropertyConstraint {

   @Override
   public void initialize(AssertTest arg0) {}

   @SuppressWarnings("unchecked")
   @Override
   public boolean isValid(Object arg0) {
      if (!(arg0 instanceof Set)) return false;
      Set<Unit> units = (Set<Unit>) arg0;
      return (units.size() > 0);
   }

   @Override
   public void apply(Property arg0) {}
}


Code:
Test test = new Test();
testDao.addTest(test);
test.addUnit(new Unit());
test.addUnit(new Unit());
test.addUnit(new Unit());
test.addUnit(new Unit());
testDao.updateTest(test);


The above throws an Exception. Stack trace is:

Code:
org.hibernate.validator.InvalidStateException: validation failed for: Test
   at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:148)
   at org.hibernate.validator.event.ValidateEventListener.onPreInsert(ValidateEventListener.java:172)
   at org.hibernate.action.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:122)
   at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:45)
   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
   at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:298)
   at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
   at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
   at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
   at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:38)
   at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:618)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:592)
   at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:596)
   at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:213)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:198)
   at $Proxy34.persist(Unknown Source)
   at TestDaoImpl.addTest(TestDaoImpl.java:9)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
   at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
   at $Proxy45.addTest(Unknown Source)
   at testValidator(ValidatorTest.java:79)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at junit.framework.TestCase.runTest(TestCase.java:168)
   at junit.framework.TestCase.runBare(TestCase.java:134)
   at junit.framework.TestResult$1.protect(TestResult.java:110)
   at junit.framework.TestResult.runProtected(TestResult.java:128)
   at junit.framework.TestResult.run(TestResult.java:113)
   at junit.framework.TestCase.run(TestCase.java:124)
   at junit.framework.TestSuite.runTest(TestSuite.java:232)
   at junit.framework.TestSuite.run(TestSuite.java:227)
   at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:91)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)



Toggling a breakpoint in AssertTestValidator.isValid shows that the Set has zero items in it.

Matt


Top
 Profile  
 
 Post subject: Re: Hibernate custom validator fails on one to many collection
PostPosted: Tue Jul 28, 2009 10:22 am 
Newbie

Joined: Tue Jul 28, 2009 9:01 am
Posts: 3
And finally, to follow up:

Code:
Test test = new Test();
test.addUnit(new Unit());
test.addUnit(new Unit());
test.addUnit(new Unit());
test.addUnit(new Unit());
testDao.addTest(test);


Persists all of the objects correctly, and:

Code:
Test test = new Test();
testDao.addTest(test);


As expected throws a validation failure.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.