Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: ClassCastException: Object; cannot be cast to Collection
PostPosted: Thu Nov 09, 2017 9:10 am 
Newbie

Joined: Thu Nov 09, 2017 8:47 am
Posts: 5
My first post here....

Have been assigned to understand why we cannot step version beyond 5.2.2. The project has been running for a couple of years using Hibernate but when trying to step up from 5.2.2 it fails with some exception. I've located the problem to the introduction of https://hibernate.atlassian.net/browse/HHH-11097. The problem seems to origin in:

StructuredCacheEntry:destructure(Object structured, SessionFactoryImplementor factory) {
...
return new StandardCacheEntryImpl(
state,
TypeHelper.toLoggableString( state, subclassPersister.getPropertyTypes(), factory ),
subclass,
version
);
}


We get an exception in the toLoggableString() due to that the object that is a Set and we get e cast to CollectionType but it is not a Collection and when it tries to get the iterator from it it crashes. However in the 5.2.2 version the code above is pretty much the same (excepti for the toLoggableString()) and I can see that we have exactly the same behvour, i.e. the object is not a Collection but here it seems not to matter since no iteration is done. Understand if the problem description is a bit thin but perpaps someone has seen something similar.

Caused by: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.util.Collection
at org.hibernate.type.CollectionType.getElementsIterator(CollectionType.java:250)
at org.hibernate.type.CollectionType.renderLoggableString(CollectionType.java:208)
at org.hibernate.type.CollectionType.toLoggableString(CollectionType.java:198)
at org.hibernate.type.TypeHelper.toLoggableString(TypeHelper.java:439)
at org.hibernate.cache.spi.entry.StructuredCacheEntry.destructure(StructuredCacheEntry.java:54)
at org.hibernate.event.internal.DefaultLoadEventListener.processCachedEntry(DefaultLoadEventListener.java:612)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromSecondLevelCache(DefaultLoadEventListener.java:602)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:462)


Top
 Profile  
 
 Post subject: Re: Problem with HHH-11097
PostPosted: Thu Nov 09, 2017 9:20 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1515
1. How can you have a CollectionType if the underlying attribute is not a collection? Add your mapping to get a better understanding of your use case.

2. Do you happen to include a Collection in toString? That's quite wrong since you don't want toString to trigger a Collection initialization, or to fail if you didn't initialize the collection and you closed the origin Persistence Context. Again, we need to see your mappings.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: ClassCastException: Object; cannot be cast to Collection
PostPosted: Thu Nov 09, 2017 9:36 am 
Newbie

Joined: Thu Nov 09, 2017 8:47 am
Posts: 5
The underlaying object is a Collection (Set).. and that is the strange thing here. The mapping are through annotations and the class structure is pretty complex but I'll try simplify it.

... and thanx for a superfast response!


Top
 Profile  
 
 Post subject: Re: ClassCastException: Object; cannot be cast to Collection
PostPosted: Fri Nov 10, 2017 8:36 am 
Newbie

Joined: Thu Nov 09, 2017 8:47 am
Posts: 5
Some more findings before I'll try to make a "simple" testcase for this... s

The target system is rather big with more than 1000 database requests/seq during peak and it has worked well for a few years but with the HHH-11097 it does not anymore. Have verified that if we do not use the L2Cache it works. Have also verified that if we have the L2Cache enabled and "removes" the creation of the logstring as below it works as before (except for log output) . My conclusion is that we must have run into some form of cornercase here.

public class StructuredCacheEntry implements CacheEntryStructure {
....
@Override
@SuppressWarnings("unchecked")
public Object destructure(Object structured, SessionFactoryImplementor factory) {
final Map map = (Map) structured;
final String subclass = (String) map.get( SUBCLASS_KEY );
final Object version = map.get( VERSION_KEY );
final EntityPersister subclassPersister = factory.getEntityPersister( subclass );
final String[] names = subclassPersister.getPropertyNames();
final Serializable[] state = new Serializable[names.length];
for ( int i = 0; i < names.length; i++ ) {
state[i] = (Serializable) map.get( names[i] );
}
return new StandardCacheEntryImpl(
state,
//TypeHelper.toLoggableString( state, subclassPersister.getPropertyTypes(), factory ),
"some dummy",
subclass,
version
);
}


Top
 Profile  
 
 Post subject: Re: ClassCastException: Object; cannot be cast to Collection
PostPosted: Fri Nov 10, 2017 8:41 am 
Newbie

Joined: Thu Nov 09, 2017 8:47 am
Posts: 5
We did just find this which seems to be similar to our problem: https://stackoverflow.com/questions/459 ... -of-compos

.... and this! Seems exactly what we see, https://stackoverflow.com/questions/400 ... oesnt-work


Top
 Profile  
 
 Post subject: Re: ClassCastException: Object; cannot be cast to Collection
PostPosted: Thu Nov 16, 2017 9:07 am 
Newbie

Joined: Thu Nov 09, 2017 8:47 am
Posts: 5
Have been able to reproduce the fault in a simple Maven context. The problem drills down to the embedded identifier Key in the HibernateRoot.class in combination that the object is retreived from the second level cache. The root cause prior the exception below is in CollectionType:toLoggableString() on the line if ( persister.getKeyType().getReturnedClass().isInstance( value ) ) which should return true but is false. The returned class is Key but value is Object[].Integer. If we replace the embeded id wiht a simple integer it works well.

Callchain:
Caused by: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.util.Collection
at org.hibernate.type.CollectionType.getElementsIterator(CollectionType.java:249)
at org.hibernate.type.CollectionType.renderLoggableString(CollectionType.java:207)
at org.hibernate.type.CollectionType.toLoggableString(CollectionType.java:197)
at org.hibernate.type.TypeHelper.toLoggableString(TypeHelper.java:439)
at org.hibernate.cache.spi.entry.StructuredCacheEntry.destructure(StructuredCacheEntry.java:54)
at org.hibernate.event.internal.DefaultLoadEventListener.processCachedEntry(DefaultLoadEventListener.java:614)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromSecondLevelCache(DefaultLoadEventListener.java:604)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:463)


Testcase
@Transactional
public class HibernateL2CacheTest extends AbstractApplicationContextTest {
@PersistenceContext
private EntityManager entityManager;

@Before
public void init() {
HibernateRoot root = new HibernateRoot(new Key(1234));
entityManager.persist(root);
entityManager.flush();
Session session = entityManager.unwrap(Session.class);
session.evict(root);
}

@Test
public void test1() {
HibernateRoot root = entityManager.find(HibernateRoot.class, new Key(1234));
Assert.assertNotNull(root);
}
}

Test code
@Embeddable
public class Key implements Serializable{
private int id;
public Key(int id) {this.id = id; }
public Key() {}

@Column(name = "id")
public int getId() {
return this.id;
}
public void setId(int id) {this.id = id;}
}

@Entity(name = "hibernateroot")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class HibernateRoot
{
private Key id;
private Set<HibernateLeaf> leafs = new HashSet<>();

protected HibernateRoot() {}

public HibernateRoot(Key id) {this.id = id;}

@EmbeddedId
public Key getId() {
return id;
}
public void setId(Key id) {
this.id = id;
}

@Fetch(FetchMode.SUBSELECT)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = HibernateLeaf.class, orphanRemoval = true)
@JoinColumn(name = "hibernateroot_id")
public Set<HibernateLeaf> getLeafs() {leafs = new HashSet<>();}

public void setLeafs(Set<HibernateLeaf> leafs) {this.leafs = leafs;}
}

@Entity(name = "hibernateleaf")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class HibernateLeaf
{
private int id;
protected HibernateLeaf() {}

public HibernateLeaf(int id) { this.id = id;}

@Id
@Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}


Top
 Profile  
 
 Post subject: Re: ClassCastException: Object; cannot be cast to Collection
PostPosted: Thu Nov 16, 2017 9:20 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1515
It's good that you can replicate it. Now, we need to have this replicating test case as a dedicated unit test, which you can do as follows:

- you can use the JPA test case template to build the replicating test case
- or, you can fork Hibernate, use the EhCacheTest as a template for your new test, and send us a Pull Request.

Once you have that, you should open a Jira issue and either upload the test case or mention the Pull Request.

Thanks

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.