HHH-2879 - create a SimpleNaturalIdLoadAccess for easier access for simple (single attribute) natural ids

This commit is contained in:
Steve Ebersole 2012-01-13 15:23:02 -06:00
parent f6c4868a44
commit 3071fa892f
4 changed files with 232 additions and 11 deletions

View File

@ -824,6 +824,32 @@ public interface Session extends SharedSessionContract {
*/
public NaturalIdLoadAccess byNaturalId(Class entityClass);
/**
* Create an {@link SimpleNaturalIdLoadAccess} instance to retrieve the specified entity by
* its natural id.
*
* @param entityName The entity name of the entity type to be retrieved
*
* @return load delegate for loading the specified entity type by natural id
*
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity, or if the
* entity does not define a natural-id or if its natural-id is made up of multiple attributes.
*/
public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName);
/**
* Create an {@link SimpleNaturalIdLoadAccess} instance to retrieve the specified entity by
* its simple (single attribute) natural id.
*
* @param entityClass The entity type to be retrieved
*
* @return load delegate for loading the specified entity type by natural id
*
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity, or if the
* entity does not define a natural-id or if its natural-id is made up of multiple attributes.
*/
public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass);
/**
* Enable the named filter for this current session.
*

View File

@ -0,0 +1,70 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, 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;
/**
* Loads an entity by its natural identifier
*
* @author Eric Dalquist
* @author Steve Ebersole
*
* @see org.hibernate.annotations.NaturalId
* @see NaturalIdLoadAccess
*/
public interface SimpleNaturalIdLoadAccess {
/**
* Specify the {@link org.hibernate.LockOptions} to use when retrieving the entity.
*
* @param lockOptions The lock options to use.
*
* @return {@code this}, for method chaining
*/
public SimpleNaturalIdLoadAccess with(LockOptions lockOptions);
/**
* Return the persistent instance with the given natural id value, assuming that the instance exists. This method
* might return a proxied instance that is initialized on-demand, when a non-identifier method is accessed.
*
* You should not use this method to determine if an instance exists; to check for existence, use {@link #load}
* instead. Use this only to retrieve an instance that you assume exists, where non-existence would be an
* actual error.
*
* @param naturalIdValue The value of the natural-id for the entity to retrieve
*
* @return the persistent instance or proxy
*/
public Object getReference(Object naturalIdValue);
/**
* Return the persistent instance with the given natural id value, or {@code null} if there is no such persistent
* instance. If the instance is already associated with the session, return that instance, initializing it if
* needed. This method never returns an uninitialized instance.
*
* @param naturalIdValue The value of the natural-id for the entity to retrieve
*
* @return The persistent instance or {@code null}
*/
public Object load(Object naturalIdValue);
}

View File

@ -71,6 +71,7 @@ import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionBuilder;
import org.hibernate.SimpleNaturalIdLoadAccess;
import org.hibernate.Transaction;
import org.hibernate.TransientObjectException;
import org.hibernate.TypeHelper;
@ -946,6 +947,16 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return new NaturalIdLoadAccessImpl( entityClass );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName) {
return new SimpleNaturalIdLoadAccessImpl( entityName );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass) {
return new SimpleNaturalIdLoadAccessImpl( entityClass );
}
private void fireLoad(LoadEvent event, LoadType loadType) {
errorIfClosed();
checkTransactionSynchStatus();
@ -975,7 +986,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
public void refresh(Object object, LockMode lockMode) throws HibernateException {
fireRefresh( new RefreshEvent(object, lockMode, this) );
fireRefresh( new RefreshEvent( object, lockMode, this ) );
}
public void refresh(Object object, LockOptions lockOptions) throws HibernateException {
@ -1735,7 +1746,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
public void setReadOnly(Object entity, boolean readOnly) {
errorIfClosed();
checkTransactionSynchStatus();
persistenceContext.setReadOnly(entity, readOnly);
persistenceContext.setReadOnly( entity, readOnly );
}
public void doWork(final Work work) throws HibernateException {
@ -2171,20 +2182,23 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
fireLock( object, lockOptions );
}
}
private class IdentifierLoadAccessImpl implements IdentifierLoadAccess {
private final EntityPersister entityPersister;
private LockOptions lockOptions;
private IdentifierLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
if ( ! entityPersister.hasNaturalIdentifier() ) {
throw new HibernateException(
String.format( "Entity [%s] did not define a natural id", entityPersister.getEntityName() )
);
}
}
private IdentifierLoadAccessImpl(String entityName) {
this( factory.getEntityPersister( entityName ) );
if ( entityPersister == null ) {
throw new HibernateException( "Unable to locate persister: " + entityName );
}
this( locateEntityPersister( entityName ) );
}
private IdentifierLoadAccessImpl(Class entityClass) {
@ -2241,6 +2255,14 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
}
private EntityPersister locateEntityPersister(String entityName) {
final EntityPersister entityPersister = factory.getEntityPersister( entityName );
if ( entityPersister == null ) {
throw new HibernateException( "Unable to locate persister: " + entityName );
}
return entityPersister;
}
private class NaturalIdLoadAccessImpl implements NaturalIdLoadAccess {
private final EntityPersister entityPersister;
private final Map<String, Object> naturalIdParameters = new LinkedHashMap<String, Object>();
@ -2248,13 +2270,16 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
private NaturalIdLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
if ( ! entityPersister.hasNaturalIdentifier() ) {
throw new HibernateException(
String.format( "Entity [%s] did not define a natural id", entityPersister.getEntityName() )
);
}
}
private NaturalIdLoadAccessImpl(String entityName) {
this( factory.getEntityPersister( entityName ) );
if ( entityPersister == null ) {
throw new HibernateException( "Unable to locate persister: " + entityName );
}
this( locateEntityPersister( entityName ) );
}
private NaturalIdLoadAccessImpl(Class entityClass) {
@ -2306,4 +2331,77 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
return this.getIdentifierLoadAccess().load( entityId );
}
}
private class SimpleNaturalIdLoadAccessImpl implements SimpleNaturalIdLoadAccess {
private final EntityPersister entityPersister;
private final String naturalIdAttributeName;
private LockOptions lockOptions;
private SimpleNaturalIdLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
if ( ! entityPersister.hasNaturalIdentifier() ) {
throw new HibernateException(
String.format( "Entity [%s] did not define a natural id", entityPersister.getEntityName() )
);
}
if ( entityPersister.getNaturalIdentifierProperties().length != 1 ) {
throw new HibernateException(
String.format( "Entity [%s] did not define a simple natural id", entityPersister.getEntityName() )
);
}
final int naturalIdAttributePosition = entityPersister.getNaturalIdentifierProperties()[0];
this.naturalIdAttributeName = entityPersister.getPropertyNames()[ naturalIdAttributePosition ];
}
private SimpleNaturalIdLoadAccessImpl(String entityName) {
this( locateEntityPersister( entityName ) );
}
private SimpleNaturalIdLoadAccessImpl(Class entityClass) {
this( entityClass.getName() );
}
@Override
public final SimpleNaturalIdLoadAccessImpl with(LockOptions lockOptions) {
this.lockOptions = lockOptions;
return this;
}
@Override
public Object getReference(Object naturalIdValue) {
final Serializable entityId = resolveNaturalId( naturalIdValue );
if ( entityId == null ) {
return null;
}
return this.getIdentifierLoadAccess().getReference( entityId );
}
@Override
public Object load(Object naturalIdValue) {
final Serializable entityId = resolveNaturalId( naturalIdValue );
if ( entityId == null ) {
return null;
}
return this.getIdentifierLoadAccess().load( entityId );
}
private Serializable resolveNaturalId(Object naturalIdValue) {
final Map<String,Object> naturalIdValueMap = Collections.singletonMap( naturalIdAttributeName, naturalIdValue );
final ResolveNaturalIdEvent event =
new ResolveNaturalIdEvent( naturalIdValueMap, entityPersister, SessionImpl.this );
fireResolveNaturalId( event );
return event.getEntityId();
}
private IdentifierLoadAccess getIdentifierLoadAccess() {
final IdentifierLoadAccessImpl identifierLoadAccess = new IdentifierLoadAccessImpl( entityPersister );
if ( this.lockOptions != null ) {
identifierLoadAccess.with( lockOptions );
}
return identifierLoadAccess;
}
}
}

View File

@ -36,6 +36,8 @@ import org.hibernate.test.jpa.AbstractJPATest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
@ -106,6 +108,31 @@ public class ImmutableNaturalIdTest extends AbstractJPATest {
s.close();
}
@Test
public void testSimpleNaturalIdLoadAccessCache() {
Session s = openSession();
s.beginTransaction();
User u = new User( "steve", "superSecret" );
s.persist( u );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
u = (User) s.bySimpleNaturalId( User.class ).load( "steve" );
assertNotNull( u );
User u2 = (User) s.bySimpleNaturalId( User.class ).getReference( "steve" );
assertTrue( u == u2 );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
s.createQuery( "delete User" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
@Test
public void testNaturalIdLoadAccessCache() {
Session s = openSession();