[Hibernate-JIRA] Created: (HV-246) BeanValidationEventListener has a bug (technically a problem with hibernate-annotations 3.5.0beta1... but I couldn't find that project)
by Ken Egervari (JIRA)
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
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
14 years, 8 months
[Hibernate-JIRA] Created: (HHH-2668) Caching Fails with Composite-Ids Containing Nested, Complex Entities
by Juan Osuna (JIRA)
Caching Fails with Composite-Ids Containing Nested, Complex Entities
--------------------------------------------------------------------
Key: HHH-2668
URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-2668
Project: Hibernate3
Issue Type: Bug
Components: caching (L2)
Affects Versions: 3.2.4.sp1
Environment: Problem is independent of environment or platform and most likely exists in prior versions.
Reporter: Juan Osuna
Attachments: hibernate-caching-fix.zip
Description of Failing Test Case Scenario
Preconditions: An entity class is mapped that uses a composite-id that contains a nested entity class. Only the composite-id class implements equals/hashcode, not the nested entity class.
Steps to Reproduce:
1. open session and fetch object using composite-id
2. open new session and fetch same object again using different instance of composite-id but with same identity
Invalid Postconditions: On second retrieve, Hibernate fails to get the object from the cache and unnecessarily reloads the object. CachKeys containing different instances of the composite-id always fail to be equal even though they have the same persistent identity.
Attachment Contents
Code fix is attached as well as a Junit test case that reproduces the problem and validates the fix. The full Hibernate suite was also executed with no impact.
Attachment contains:
New Test Method:
org.hibernate.test.cache.BaseCacheProviderTestCase.testQueryCacheComplexItem
New Test Entity Items:
org\hibernate\test\cache\ComplexItem.hbm.xml
org.hibernate.test.cache.ComplexItem
org.hibernate.test.cache.ComplexItemPK
Code Fix:
org.hibernate.cache.CacheKey (see FIX comments)
Problem and Fix Details
Hibernate generally strives to use persistent identifiers for managing object identity rather than the equals/hashcode methods implemented by entity classes. While it is good practice to implement equals/hashcode, Hibernate does not generally force users to do this.
When wrapping a composite-id object, the current implementation of CacheKey fails to recurse through nested complex entities to query for equality based on persistent identity. Instead, when the recursion algorithm hits a complex entity, it invokes equals directly on that entity rather than further recursing through the identifier object.
Notably, the recursion logic for equals is not symmetrical with the recursion logic for hashcode, which does recurse through identifier objects. So, while CacheKey never invokes hashcode on nested complex entities, it does invoke equals on these entities.
A simple fix to this inconsistency is to store the factory parameter passed to CacheKey and later pass that parameter to the overloaded method:
Type.isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory).
This fix restores symmetry to equals and hashcode behavior. By calling this overloaded method, the thread of execution will enter EntityType. isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory), which correctly recurses through complex identifiers.
Design Principles
Hibernate should strive to behave predictably even in scenarios where users do not follow best practices.
Hibernate should strive to be as forgiving as possible as long there is no negative consequence caused by such forgiveness.
Hibernate should behave as consistently as possible. If Hibernate does not generally rely user-implemented equals/hashcode, it is best to avoid exceptions to this rule wherever possible.
Possible Future Enhancement
Mapping composite-ids that contain complex entities can cause deep object graphs to be cached as part of CacheKey. This is unsettling because of it's potential to consume memory unnecessarily and unpredictably.
Currently, CacheKey caches the hashcode by recursing through a complex graph of identifier objects. Perhaps, it would also be possible for CacheKey to cache an object graph of identifier objects whose leaves hold primitive values. This would further add symmetry between hashcode and equals and lighten the load for caching composite-ids that hold entity classes.
Robustly supporting composite-ids that hold complex identifiers seems like a worthwhile design goal.
--
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
14 years, 8 months
[Hibernate-JIRA] Created: (HHH-4057) Error occurs when the not-null column has a default value in the DB but the field in object has no default value.
by Rupesh Kumar (JIRA)
Error occurs when the not-null column has a default value in the DB but the field in object has no default value.
-----------------------------------------------------------------------------------------------------------------
Key: HHH-4057
URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-4057
Project: Hibernate Core
Issue Type: Bug
Components: core
Affects Versions: 3.3.2
Environment: Hibernate 3.3.2
Reporter: Rupesh Kumar
I have a column in the database which is not null and has a default value.
In the Java object, I don't have any default value set on the field mapped to this column. When this object is saved, Hibernate throws up an error because it tried to insert a null value in the not-null column. Since DB can generate the value for this column, hibernate should use that.
I can not use generated attribute in hibernate mapping file because this column value is not always generated. It is generated only when no value is specified for this column while inserting.
Hibernate should inspect the database and find out which columns has default value set on it. If there is anything set on it, insert should ignore the mapped field if the value is not defined for it.
--
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
14 years, 8 months
[Hibernate-JIRA] Created: (HHH-3817) JBC second level cache integration can cache collection data
by Brian Stansberry (JIRA)
JBC second level cache integration can cache collection data
------------------------------------------------------------
Key: HHH-3817
URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3817
Project: Hibernate Core
Issue Type: Bug
Components: caching (L2)
Reporter: Brian Stansberry
Assignee: Brian Stansberry
Fix For: 3.3.x
Scenario with two transactions dealing with a single collection:
tx1 reads collection, cache miss
tx1 goes to database to read collection
tx2 reads collection, cache miss
tx2 goes to database to read collection
tx2 does JBC putForExternalRead to store empty collection
tx2 updates collection
tx2 removes collection from JBC (since any collection update triggers a org.hibernate.cache.Cache.evict which is implemented as a JBC removeNode)
tx1 does JBC putForExternalRead to store empty collection -- STALE DATA
With entities, if the db is using REPEATABLE_READ this won't be a problem, as the DB won't allow the tx2 update until tx1 commits. With a collection there is nothing to lock on. It would be a problem for entities with READ_COMMITTED as well.
--
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
14 years, 8 months