Skip to content

Commit

Permalink
HHH-12107 - ClassCastException when using L2Cache with "structured_ca…
Browse files Browse the repository at this point in the history
…che"=true
  • Loading branch information
vladmihalcea committed Feb 1, 2018
1 parent 1f05afa commit f9b4430
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 15 deletions.
Expand Up @@ -28,7 +28,7 @@
*/
public class StandardCacheEntryImpl implements CacheEntry {
private final Serializable[] disassembledState;
private final String disassembledStateText;
private String disassembledStateText;
private final Object version;
private final String subclass;

Expand Down Expand Up @@ -66,8 +66,8 @@ public StandardCacheEntryImpl(
this.version = version;
}

StandardCacheEntryImpl(Serializable[] state, String disassembledStateText, String subclass, Object version) {
this.disassembledState = state;
StandardCacheEntryImpl(Serializable[] disassembledState, String disassembledStateText, String subclass, Object version) {
this.disassembledState = disassembledState;
this.disassembledStateText = disassembledStateText;
this.subclass = subclass;
this.version = version;
Expand Down Expand Up @@ -138,18 +138,26 @@ public Object[] assemble(
}

//assembled state gets put in a new array (we read from cache by value!)
final Object[] assembledProps = TypeHelper.assemble(
final Object[] state = TypeHelper.assemble(
disassembledState,
persister.getPropertyTypes(),
session, instance
);

if ( disassembledStateText == null ) {
disassembledStateText = TypeHelper.toLoggableString(
state,
persister.getPropertyTypes(),
session.getFactory()
);
}

//persister.setIdentifier(instance, id); //before calling interceptor, for consistency with normal load

//TODO: reuse the PreLoadEvent
final PreLoadEvent preLoadEvent = new PreLoadEvent( session )
.setEntity( instance )
.setState( assembledProps )
.setState( state )
.setId( id )
.setPersister( persister );

Expand All @@ -162,9 +170,9 @@ public Object[] assemble(
listener.onPreLoad( preLoadEvent );
}

persister.setPropertyValues( instance, assembledProps );
persister.setPropertyValues( instance, state );

return assembledProps;
return state;
}

@Override
Expand Down
Expand Up @@ -9,11 +9,9 @@
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.TypeHelper;

/**
* Structured CacheEntry format for entities. Used to store the entry into the second-level cache
Expand Down Expand Up @@ -45,15 +43,15 @@ public Object destructure(Object structured, SessionFactoryImplementor factory)
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];
final Serializable[] disassembledState = new Serializable[names.length];
for ( int i = 0; i < names.length; i++ ) {
state[i] = (Serializable) map.get( names[i] );
disassembledState[i] = (Serializable) map.get( names[i] );
}
return new StandardCacheEntryImpl(
state,
TypeHelper.toLoggableString( state, subclassPersister.getPropertyTypes(), factory ),
subclass,
version
disassembledState,
null,
subclass,
version
);
}

Expand Down
@@ -0,0 +1,171 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.querycache;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

import org.hibernate.CacheMode;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;

import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;

import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
* @author Vlad Mihalceca
*/
@TestForIssue( jiraKey = "HHH-12107" )
public class StructuredQueryCacheTest extends BaseNonConfigCoreFunctionalTestCase {

@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
OneToManyWithEmbeddedId.class,
OneToManyWithEmbeddedIdChild.class,
OneToManyWithEmbeddedIdKey.class
};
}

@Override
protected void addSettings(Map settings) {
settings.put( AvailableSettings.USE_QUERY_CACHE, "true" );
settings.put( AvailableSettings.CACHE_REGION_PREFIX, "foo" );
settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
settings.put( AvailableSettings.USE_STRUCTURED_CACHE, "true" );
}

@Override
protected boolean isCleanupTestDataRequired() {
return true;
}

@Override
protected String getCacheConcurrencyStrategy() {
return "transactional";
}

@Test
@TestForIssue( jiraKey = "HHH-12107" )
public void testEmbeddedIdInOneToMany() {

OneToManyWithEmbeddedIdKey key = new OneToManyWithEmbeddedIdKey( 1234 );
final OneToManyWithEmbeddedId o = new OneToManyWithEmbeddedId( key );
o.setItems( new HashSet<>() );
o.getItems().add( new OneToManyWithEmbeddedIdChild( 1 ) );

doInHibernate( this::sessionFactory, session -> {
session.persist( o );
});

doInHibernate( this::sessionFactory, session -> {
OneToManyWithEmbeddedId _entity = session.find( OneToManyWithEmbeddedId.class, key );
assertTrue( session.getSessionFactory().getCache().containsEntity( OneToManyWithEmbeddedId.class, key ) );
assertNotNull( _entity );
});

doInHibernate( this::sessionFactory, session -> {
OneToManyWithEmbeddedId _entity = session.find( OneToManyWithEmbeddedId.class, key );
assertTrue( session.getSessionFactory().getCache().containsEntity( OneToManyWithEmbeddedId.class, key ) );
assertNotNull( _entity );
});
}

@Entity(name = "OneToManyWithEmbeddedId")
public static class OneToManyWithEmbeddedId {

private OneToManyWithEmbeddedIdKey id;

private Set<OneToManyWithEmbeddedIdChild> items = new HashSet<>( );

public OneToManyWithEmbeddedId() {
}

public OneToManyWithEmbeddedId(OneToManyWithEmbeddedIdKey id) {
this.id = id;
}

@EmbeddedId
public OneToManyWithEmbeddedIdKey getId() {
return id;
}

public void setId(OneToManyWithEmbeddedIdKey id) {
this.id = id;
}

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = OneToManyWithEmbeddedIdChild.class, orphanRemoval = true)
@JoinColumn(name = "parent_id")
public Set<OneToManyWithEmbeddedIdChild> getItems() {
return items;
}

public void setItems(Set<OneToManyWithEmbeddedIdChild> items) {
this.items = items;
}
}

@Entity(name = "OneToManyWithEmbeddedIdChild")
public static class OneToManyWithEmbeddedIdChild {
private Integer id;

public OneToManyWithEmbeddedIdChild() {
}

public OneToManyWithEmbeddedIdChild(Integer id) {
this.id = id;
}

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

public void setId(int id) {
this.id = id;
}
}

@Embeddable
public static class OneToManyWithEmbeddedIdKey implements Serializable {
private Integer id;

public OneToManyWithEmbeddedIdKey() {
}

public OneToManyWithEmbeddedIdKey(Integer id) {
this.id = id;
}

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

public void setId(Integer id) {
this.id = id;
}
}
}

0 comments on commit f9b4430

Please sign in to comment.