HHH-7736 - simple improvement and test fixing

This commit is contained in:
Strong Liu 2012-11-26 16:44:49 +08:00
parent 46a34a4cf4
commit 73f9df1bb5
7 changed files with 297 additions and 166 deletions

View File

@ -65,7 +65,7 @@ public class LockOptions implements Serializable {
private LockMode lockMode = LockMode.NONE;
private int timeout = WAIT_FOREVER;
private Map aliasSpecificLockModes = null; //initialize lazily as LockOptions is frequently created without needing this
private Map<String, LockMode> aliasSpecificLockModes = null; //initialize lazily as LockOptions is frequently created without needing this
public LockOptions() {
}
@ -114,7 +114,7 @@ public class LockOptions implements Serializable {
*/
public LockOptions setAliasSpecificLockMode(String alias, LockMode lockMode) {
if ( aliasSpecificLockModes == null ) {
aliasSpecificLockModes = new HashMap();
aliasSpecificLockModes = new HashMap<String, LockMode>();
}
aliasSpecificLockModes.put( alias, lockMode );
return this;
@ -135,7 +135,7 @@ public class LockOptions implements Serializable {
if ( aliasSpecificLockModes == null ) {
return null;
}
return (LockMode) aliasSpecificLockModes.get( alias );
return aliasSpecificLockModes.get( alias );
}
/**
@ -176,9 +176,9 @@ public class LockOptions implements Serializable {
*
* @return Iterator for accessing the Map.Entry's
*/
public Iterator getAliasLockIterator() {
public Iterator<Map.Entry<String, LockMode>> getAliasLockIterator() {
if ( aliasSpecificLockModes == null ) {
return Collections.emptyList().iterator();
return Collections.<String, LockMode>emptyMap().entrySet().iterator();
}
return aliasSpecificLockModes.entrySet().iterator();
}
@ -256,7 +256,7 @@ public class LockOptions implements Serializable {
dest.setScope(from.getScope());
dest.setTimeOut(from.getTimeOut());
if ( from.aliasSpecificLockModes != null ) {
dest.aliasSpecificLockModes = new HashMap( from.aliasSpecificLockModes );
dest.aliasSpecificLockModes = new HashMap<String, LockMode>( from.aliasSpecificLockModes );
}
return dest;
}

View File

@ -388,7 +388,7 @@ public class EntityClass extends ConfiguredClass {
// Custom persister
String entityPersisterClass = null;
final AnnotationInstance persisterAnnotation = JandexHelper.getSingleAnnotation(
getClassInfo(), HibernateDotNames.PERSISTER
getClassInfo(), HibernateDotNames.PERSISTER, ClassInfo.class
);
if ( persisterAnnotation != null && persisterAnnotation.value( "impl" ) != null ) {
entityPersisterClass = persisterAnnotation.value( "impl" ).asString();

View File

@ -87,6 +87,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterConfiguration;
import org.hibernate.internal.FilterHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jdbc.Expectation;
import org.hibernate.jdbc.Expectations;
@ -199,6 +200,7 @@ public abstract class AbstractEntityPersister
//information about lazy properties of this class
private final String[] lazyPropertyNames;
private final int[] lazyPropertyNumbers;
private final Type[] lazyPropertyTypes;
private final String[][] lazyPropertyColumnAliases;
@ -266,7 +268,7 @@ public abstract class AbstractEntityPersister
protected ExecuteUpdateResultCheckStyle[] deleteResultCheckStyles;
private InsertGeneratedIdentifierDelegate identityDelegate;
private LazyPropertyInitializer lazyPropertyInitializerDelegater = new LazyPropertyInitializerImpl( this );
private boolean[] tableHasColumns;
private final String loaderName;
@ -1204,147 +1206,11 @@ public abstract class AbstractEntityPersister
ArrayHelper.toIntArray( formulaNumbers ) );
}
@Override
public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
throws HibernateException {
return lazyPropertyInitializerDelegater.initializeLazyProperty( fieldName, entity, session );
final Serializable id = session.getContextEntityIdentifier( entity );
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
if ( entry == null ) {
throw new HibernateException( "entity is not associated with the session: " + id );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Initializing lazy properties of: {0}, field access: {1}", MessageHelper.infoString( this, id, getFactory() ), fieldName );
}
if ( hasCache() ) {
CacheKey cacheKey = session.generateCacheKey( id, getIdentifierType(), getEntityName() );
Object ce = getCacheAccessStrategy().get( cacheKey, session.getTimestamp() );
if (ce!=null) {
CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure(ce, factory);
if ( !cacheEntry.areLazyPropertiesUnfetched() ) {
//note early exit here:
return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry );
}
}
}
return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry );
}
private Object initializeLazyPropertiesFromDatastore(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Serializable id,
final EntityEntry entry) {
if ( !hasLazyProperties() ) {
throw new AssertionFailure( "no lazy properties" );
}
LOG.trace( "Initializing lazy properties from datastore" );
try {
Object result = null;
PreparedStatement ps = null;
try {
final String lazySelect = getSQLLazySelectString();
ResultSet rs = null;
try {
if ( lazySelect != null ) {
// null sql means that the only lazy properties
// are shared PK one-to-one associations which are
// handled differently in the Type#nullSafeGet code...
ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( lazySelect );
getIdentifierType().nullSafeSet( ps, id, 1, session );
rs = ps.executeQuery();
rs.next();
}
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
Object propValue = lazyPropertyTypes[j].nullSafeGet( rs, lazyPropertyColumnAliases[j], session, entity );
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
}
finally {
if ( rs != null ) {
rs.close();
}
}
}
finally {
if ( ps != null ) {
ps.close();
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
catch ( SQLException sqle ) {
throw getFactory().getSQLExceptionHelper().convert(
sqle,
"could not initialize lazy properties: " +
MessageHelper.infoString( this, id, getFactory() ),
getSQLLazySelectString()
);
}
}
private Object initializeLazyPropertiesFromCache(
final String fieldName,
final Object entity,
final SessionImplementor session,
final EntityEntry entry,
final CacheEntry cacheEntry
) {
LOG.trace( "Initializing lazy properties from second-level cache" );
Object result = null;
Serializable[] disassembledValues = cacheEntry.getDisassembledState();
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
final Object propValue = lazyPropertyTypes[j].assemble(
disassembledValues[ lazyPropertyNumbers[j] ],
session,
entity
);
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
private boolean initializeLazyProperty(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Object[] snapshot,
final int j,
final Object propValue) {
setPropertyValue( entity, lazyPropertyNumbers[j], propValue );
if ( snapshot != null ) {
// object have been loaded with setReadOnly(true); HHH-2236
snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, factory );
}
return fieldName.equals( lazyPropertyNames[j] );
}
public boolean isBatchable() {
@ -3014,15 +2880,23 @@ public abstract class AbstractEntityPersister
return identityDelegate.performInsert( sql, session, binder );
}
private ValueHolder<String> identitySelectStringValue = new ValueHolder<String>(
new ValueHolder.DeferredInitializer<String>() {
@Override
public String initialize() {
return getFactory().getDialect().getIdentitySelectString(
getTableName( 0 ),
getKeyColumns( 0 )[0],
getIdentifierType().sqlTypes( getFactory() )[0]
);
}
}
);
@Override
public String getIdentitySelectString() {
//TODO: cache this in an instvar
return getFactory().getDialect().getIdentitySelectString(
getTableName(0),
getKeyColumns(0)[0],
getIdentifierType().sqlTypes( getFactory() )[0]
);
return identitySelectStringValue.getValue();
}
@Override
public String getSelectByUniqueKeyString(String propertyName) {
return new SimpleSelect( getFactory().getDialect() )
.setTableName( getTableName(0) )
@ -5024,5 +4898,21 @@ public abstract class AbstractEntityPersister
public int determineTableNumberForColumn(String columnName) {
return 0;
}
Type[] getLazyPropertyTypes() {
return lazyPropertyTypes;
}
int[] getLazyPropertyNumbers() {
return lazyPropertyNumbers;
}
String[] getLazyPropertyNames() {
return lazyPropertyNames;
}
String[][] getLazyPropertyColumnAliases() {
return lazyPropertyColumnAliases;
}
}

View File

@ -0,0 +1,219 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.persister.entity;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.pretty.MessageHelper;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
class LazyPropertyInitializerImpl implements LazyPropertyInitializer {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
AbstractEntityPersister.class.getName()
);
private final AbstractEntityPersister entityPersister;
LazyPropertyInitializerImpl(AbstractEntityPersister entityPersister) {
this.entityPersister = entityPersister;
}
@Override
public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
throws HibernateException {
final Serializable id = session.getContextEntityIdentifier( entity );
final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
if ( entry == null ) {
throw new HibernateException( "entity is not associated with the session: " + id );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
"Initializing lazy properties of: {0}, field access: {1}", MessageHelper.infoString(
entityPersister,
id,
entityPersister.getFactory()
), fieldName
);
}
if ( entityPersister.hasCache() ) {
CacheKey cacheKey = session.generateCacheKey(
id,
entityPersister.getIdentifierType(),
entityPersister.getEntityName()
);
Object ce = entityPersister.getCacheAccessStrategy().get( cacheKey, session.getTimestamp() );
if ( ce != null ) {
CacheEntry cacheEntry = (CacheEntry) entityPersister.getCacheEntryStructure()
.destructure( ce, entityPersister.getFactory() );
if ( !cacheEntry.areLazyPropertiesUnfetched() ) {
//note early exit here:
return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry );
}
}
}
return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry );
}
private Object initializeLazyPropertiesFromDatastore(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Serializable id,
final EntityEntry entry) {
if ( !entityPersister.hasLazyProperties() ) {
throw new AssertionFailure( "no lazy properties" );
}
LOG.trace( "Initializing lazy properties from datastore" );
try {
Object result = null;
PreparedStatement ps = null;
try {
final String lazySelect = entityPersister.getSQLLazySelectString();
ResultSet rs = null;
try {
if ( lazySelect != null ) {
// null sql means that the only lazy properties
// are shared PK one-to-one associations which are
// handled differently in the Type#nullSafeGet code...
ps = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( lazySelect );
entityPersister.getIdentifierType().nullSafeSet( ps, id, 1, session );
rs = ps.executeQuery();
rs.next();
}
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < entityPersister.getLazyPropertyNames().length;
j++ ) {
Object propValue = entityPersister.getLazyPropertyTypes()[j].nullSafeGet(
rs,
entityPersister.getLazyPropertyColumnAliases()[j],
session,
entity
);
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
}
finally {
if ( rs != null ) {
rs.close();
}
}
}
finally {
if ( ps != null ) {
ps.close();
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
catch ( SQLException sqle ) {
throw entityPersister.getFactory().getSQLExceptionHelper().convert(
sqle,
"could not initialize lazy properties: " +
MessageHelper.infoString( entityPersister, id, entityPersister.getFactory() ),
entityPersister.getSQLLazySelectString()
);
}
}
private Object initializeLazyPropertiesFromCache(
final String fieldName,
final Object entity,
final SessionImplementor session,
final EntityEntry entry,
final CacheEntry cacheEntry
) {
LOG.trace( "Initializing lazy properties from second-level cache" );
Object result = null;
Serializable[] disassembledValues = cacheEntry.getDisassembledState();
final Object[] snapshot = entry.getLoadedState();
for ( int j = 0; j < entityPersister.getLazyPropertyNames().length; j++ ) {
final Object propValue = entityPersister.getLazyPropertyTypes()[j].assemble(
disassembledValues[entityPersister.getLazyPropertyNumbers()[j]],
session,
entity
);
if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
result = propValue;
}
}
LOG.trace( "Done initializing lazy properties" );
return result;
}
private boolean initializeLazyProperty(
final String fieldName,
final Object entity,
final SessionImplementor session,
final Object[] snapshot,
final int j,
final Object propValue) {
entityPersister.setPropertyValue( entity, entityPersister.getLazyPropertyNumbers()[j], propValue );
if ( snapshot != null ) {
// object have been loaded with setReadOnly(true); HHH-2236
snapshot[entityPersister.getLazyPropertyNumbers()[j]] = entityPersister.getLazyPropertyTypes()[j].deepCopy(
propValue,
entityPersister.getFactory()
);
}
return fieldName.equals( entityPersister.getLazyPropertyNames()[j] );
}
}

View File

@ -5,6 +5,7 @@ import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Collection;
import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeBinding;
import org.hibernate.persister.collection.OneToManyPersister;
/**
@ -16,4 +17,9 @@ public class CollectionPersister extends OneToManyPersister {
SessionFactoryImplementor factory) throws MappingException, CacheException {
super( collection, cache, factory );
}
public CollectionPersister(AbstractPluralAttributeBinding collection, CollectionRegionAccessStrategy cacheAccessStrategy, SessionFactoryImplementor factory)
throws MappingException, CacheException {
super( collection, cacheAccessStrategy, factory );
}
}

View File

@ -1,20 +1,27 @@
package org.hibernate.test.annotations.persister;
import org.hibernate.HibernateException;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.persister.entity.SingleTableEntityPersister;
/**
* @author Shawn Clowater
*/
public class EntityPersister extends SingleTableEntityPersister {
@SuppressWarnings( {"UnusedDeclaration"})
@SuppressWarnings({ "UnusedDeclaration" })
public EntityPersister(PersistentClass persistentClass, EntityRegionAccessStrategy cache,
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
SessionFactoryImplementor factory, Mapping cfg) throws HibernateException {
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
SessionFactoryImplementor factory, Mapping cfg) throws HibernateException {
super( persistentClass, cache, naturalIdRegionAccessStrategy, factory, cfg );
}
public EntityPersister(EntityBinding entityBinding, EntityRegionAccessStrategy cacheAccessStrategy, NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy, SessionFactoryImplementor factory, Mapping mapping)
throws HibernateException {
super( entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory, mapping );
}
}

View File

@ -23,12 +23,13 @@
*/
package org.hibernate.test.annotations.persister;
import java.util.Iterator;
import org.junit.Test;
import org.hibernate.mapping.Collection;
import org.hibernate.metamodel.spi.binding.PluralAttributeBinding;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.testing.FailureExpectedWithNewMetamodel;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
@ -36,14 +37,13 @@ import static org.junit.Assert.assertEquals;
/**
* @author Shawn Clowater
*/
@FailureExpectedWithNewMetamodel
public class PersisterTest extends BaseCoreFunctionalTestCase {
@Test
public void testEntityEntityPersisterAndPersisterSpecified() throws Exception {
//checks to see that the persister specified with the @Persister annotation takes precedence if a @Entity.persister() is also specified
Class<? extends EntityPersister> clazz = getEntityBinding( Deck.class ).getCustomEntityPersisterClass();
assertEquals( "Incorrect Persister class for " + Deck.class.getName(),
EntityPersister.class, clazz );
org.hibernate.test.annotations.persister.EntityPersister.class, clazz );
}
@Test
@ -56,11 +56,20 @@ public class PersisterTest extends BaseCoreFunctionalTestCase {
@Test
public void testCollectionPersisterSpecified() throws Exception {
// TODO: use getCollectionBindings()
//tests the persister specified by the @Persister annotation on a collection
Collection collection = configuration().getCollectionMapping( Deck.class.getName() + ".cards" );
assertEquals( "Incorrect Persister class for collection " + collection.getRole(), CollectionPersister.class,
collection.getCollectionPersisterClass() );
String expectedRole = Deck.class.getName() + ".cards";
Iterator<PluralAttributeBinding> collectionBindings = getCollectionBindings();
while ( collectionBindings.hasNext() ) {
PluralAttributeBinding attributeBinding = collectionBindings.next();
String role = attributeBinding.getAttribute().getRole();
//tests the persister specified by the @Persister annotation on a collection
if ( expectedRole.equals( role ) ) {
assertEquals(
"Incorrect Persister class for collection " + role, CollectionPersister.class,
attributeBinding.getExplicitPersisterClass()
);
break;
}
}
}
@Override