[hibernate-issues] [Hibernate-JIRA] Commented: (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
Tue Nov 3 05:53:56 EST 2009


    [ http://opensource.atlassian.com/projects/hibernate/browse/HV-246?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=34411#action_34411 ] 

Hardy Ferentschik commented on HV-246:
--------------------------------------

Just some final comments before I resolve this issue, since I don't think it belongs here. This is a discussion for the forum and not a actual bug.

#1 Regarding the need to access ApplicationContextProvider directly in the ConstraintValidator implementation. The right approach would be to implement a custom ConstraintValidatorFactory. I guess within the factory you can still use ApplicationContextProvider to get to the SessionFactory or maybe even better you could pass the SessionFactory as constructor into the ConstraintValidatorFactory. That depends of course on how you bootstrap Bean Validation within Spring

#2 To open a child session (a new session on top of a connection of a current session) you can use SessionFactoty.getCurrentSession().connection() to get hold of the current connection. Then you can pass this connection to one of the open session calls of the SessionFactory. Session.connection() is deprecated, but I don't think there is an alternative way of doing this as yet. 

> 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