[hibernate-issues] [Hibernate-JIRA] Reopened: (HV-246) BeanValidationEventListener has a bug (technically a problem with hibernate-annotations 3.5.0beta1... but I couldn't find that project)

Hardy Ferentschik (JIRA) noreply at atlassian.com
Mon Mar 8 16:21:49 EST 2010


     [ http://opensource.atlassian.com/projects/hibernate/browse/HV-246?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Hardy Ferentschik reopened HV-246:
----------------------------------


> BeanValidationEventListener has a bug (technically a problem with hibernate-annotations 3.5.0beta1... but I couldn't find that project)
> ---------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HV-246
>                 URL: http://opensource.atlassian.com/projects/hibernate/browse/HV-246
>             Project: Hibernate Validator
>          Issue Type: Bug
>    Affects Versions: 4.0.0.CR1
>         Environment: hibernate 3.3.2ga, hibernate validator 4.0cr1, hibernate-annotations 3.5.0beta1
>            Reporter: Ken Egervari
>            Priority: Blocker
>             Fix For: 4.1.0
>
>
> The interaction with a hibernate session and sessions created within a validator do not work together.
> For example, here is a test case:
> {code}
> 	@Test
> 	public void jobSeekersCanSaveAPreviousResumeWithTheSameName() {
> 		JobSeekerResume resume = ( JobSeekerResume ) resumeDao.find( 1 );
> 		resume.setDescription( "new desc." );
> 		resumeDao.save( resume );
> 		flush();
> 		jdbcMap = simpleJdbcTemplate.queryForMap(
> 			"select * from resume where resume_id = ?", resume.getId() );
> 		assertEquals( resume.getDescription(), jdbcMap.get( "DESCRIPTION" ) );
> 	}
> {code}
> the flush() method creates an exception:
> {code}
> ERROR AssertionFailure:45 - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
> org.hibernate.AssertionFailure: collection [jawbs.domain.jobseeker.JobSeeker.categories] was not processed by flush()
> 	at org.hibernate.engine.CollectionEntry.postFlush(CollectionEntry.java:228)
> 	at org.hibernate.event.def.AbstractFlushingEventListener.postFlush(AbstractFlushingEventListener.java:356)
> 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
> 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1028)
> 	at jawbs.DatabaseTests.flush(DatabaseTests.java:113)
> {code}
> Obviously hibernate core is saying this could be a bug, or it could be unsafe.
> Now, the reason this happens is because JobSeekerResume has a contraint that accesses the hibernate session. Now, I have many, many constraints that use this constraint and they work fine. This is the only test that is causing problems for me, and I don't have a clue why.
> Nonetheless, I'm going to give you a lot of code. Here is the constraint annotation on JobSeekerResume:
> {code}
> @QueryConstraint(
> 	hql = "select count(*) from JobSeekerResume where name = :name and id != :id and jobSeeker.id = :jobSeeker.id",
> 	message = "{jobSeekerResume.name.unique}",
> 	enabled = true
> )
> {code}
> And here is the code for the validator implementation:
> {code}
> public class QueryConstraintValidator extends ValidatorSupport<QueryConstraint,DomainObject> {
> 	/* Fields */
> 	private String hql;
> 	private boolean enabled;
> 	/* Services */
> 	public void initialize( QueryConstraint queryConstraint ) {
> 		this.hql = queryConstraint.hql();
> 		this.enabled = queryConstraint.enabled();
> 	}
> 	public boolean isValid( DomainObject domainObject ) {
> 		return isValid( domainObject, null );
> 	}
> 	@Override
> 	protected boolean preValidate() {
> 		return !enabled;
> 	}
> 	protected boolean testQuery( Session session, DomainObject domainObject ) {
> 		logger.debug( "Enabled - Validating constraint with: " );
> 		logger.debug( hql );
> 		Query query = session.createQuery(
> 			HqlParser.removePeriodsFromParameterNames( hql )
> 		);
> 		BeanWrapper beanWrapper = new BeanWrapperImpl( domainObject );
> 		for( String parameterName : HqlParser.getParameterNames( hql ) ) {
> 			query.setParameter(
> 				HqlParser.removePeriodsFromParameterName( parameterName ),
> 				beanWrapper.getPropertyValue( parameterName )
> 			);
> 		}
> 		boolean result = (Long) query.uniqueResult() == 0;
> 		logger.debug( "isValid is returning: " + result );
> 		return result;
> 	}
> ...
> }
> {code}
> The support constraint (the important one most likely) is as follows:
> {code}
> public abstract class ValidatorSupport<T extends Annotation,U> implements ConstraintValidator<T,U> {
> 	/* Fields */
> 	protected static Logger logger =
> 		LoggerFactory.getLogger( QueryConstraintValidator.class );
> 	/* Services */
> 	public boolean isValid( U object, ConstraintValidatorContext constraintValidatorContext ) {
> 		if( preValidate() ) {
> 			return true;
> 		}
> 		SessionFactory sessionFactory =
> 			( SessionFactory ) ApplicationContextProvider.getBean(
> 				"sessionFactory" );
> 		if( sessionFactory != null ) {
> 			Session session = sessionFactory.openSession();
> 			Transaction tx = session.beginTransaction();
> 			boolean result = testQuery( session, object );
> 			tx.commit();
> 			session.close();
> 			return result;
> 		}
> 		return true;
> 	}
> 	protected boolean preValidate() {
> 		return false;
> 	}
> 	protected abstract boolean testQuery( Session session, U object );
> }
> {code}
> Lastly, here is the mapping of the JobSeeker subclass:
> {code}
>         <subclass name="jawbs.domain.jobseeker.JobSeeker" discriminator-value="ROLE_JOBSEEKER">
>             <property name="isPrivate" column="is_private" />
>             <bag name="categories" table="job_seeker_to_category" cascade="all-delete-orphan" inverse="true">
>                 <key column="job_seeker_id"/>
>                 <many-to-many class="jawbs.domain.Category" column="category_id"/>
>             </bag>
>             <bag name="watchedJobs" table="job_seeker_to_watched_job" cascade="all-delete-orphan" inverse="true">
>                 <key column="job_seeker_id"/>
>                 <many-to-many class="jawbs.domain.job.Job" column="watched_job_id"/>
>             </bag>
>             <bag name="applications" table="candidate" cascade="all-delete-orphan" inverse="true">
>                 <key column="job_seeker_id"/>
>                 <many-to-many class="jawbs.domain.candidate.Candidate" column="candidate_id"/>
>             </bag>
>             <bag name="blacklistedEmployers" table="job_seeker_to_blacklisted_employer">
>                 <key column="job_seeker_id"/>
>                 <many-to-many class="jawbs.domain.employer.Employer" column="blacklisted_employer_id"/>
>             </bag>
>             <bag name="resumes" table="resume" inverse="true" cascade="all-delete-orphan">
>                 <key column="job_seeker_id"/>
>                 <one-to-many class="jawbs.domain.resume.Resume" />
>             </bag>
>         </subclass>
> {code}
> And here is the mapping of the subclass of resume:
> {code}
>         <subclass name="jawbs.domain.resume.JobSeekerResume" discriminator-value="JOBSEEKER">
>             <many-to-one name="jobSeeker" class="jawbs.domain.jobseeker.JobSeeker"
>                          column="job_seeker_id" />
>         </subclass>
> {code}
> If you need any more assistance, please let me know. This is a critical blocker to getting a test to pass, this is most likely 100% fine.
> Thanks

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the hibernate-issues mailing list