[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)

Ken Egervari (JIRA) noreply at atlassian.com
Wed Oct 14 18:19:41 EDT 2009


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

Ken Egervari commented on HV-246:
---------------------------------

On the sessionFactory, there is a method called sessionFactory.openStatelessSession(). I thought that might get rid of the error (nope).

>From Spring, the sessionFactory is the most basic hibernate component one can interactive job. It contains the dataSource, and all the hibernate configuration (mappings, event listerners, etc.)

To get access it, one just needs to use dependency injection. However in my case, I need to do this:

SessionFactory sessionFactory =
	( SessionFactory ) ApplicationContextProvider.getBean( "sessionFactory" );

The applicationContextProvider is actually a bean in the spring context... and Spring will set the context on this bean and I expose it as a static property. I know this isn't the best way to get it, but from Hibernate Validator, I have no idea how to get access to Spring managed beans from within a ConstraintValidator implementation. This is the easiest way and perhaps the only way I could come up with.

Once I have a sessionFactory... there are only so many ways to go about making a session - openSession or openStatelessSession. These are hibernate methods - nothing to do with Spring.

Provided with a sessionFactory, is there a way to do as you suggest? Should I just build an entirely new instance of hibernate instead?

> 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