[
http://opensource.atlassian.com/projects/hibernate/browse/HV-246?page=com...
]
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....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira