From dfe33ffa1a6f17c973783e464fcd6437a9e629e7 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 25 Apr 2013 12:07:52 -0700 Subject: [PATCH 01/17] HHH-7841 - Redesign Loader --- .../loader/plan/spi/CompositeFetch.java | 3 +- .../entity/AbstractEntityPersister.java | 57 ++- .../AbstractCompositionDefinition.java | 36 +- ...positeAttributeResultSetProcessorTest.java | 338 ++++++++++++++++++ ...LazyCollectionResultSetProcessorTest.java} | 2 +- ...zyOneToManyListResultSetProcessorTest.java | 215 +++++++++++ ...zyOneToManySetResultSetProcessorTest.java} | 2 +- 7 files changed, 629 insertions(+), 24 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java rename hibernate-core/src/test/java/org/hibernate/loader/{EntityWithCollectionResultSetProcessorTest.java => EntityWithNonLazyCollectionResultSetProcessorTest.java} (98%) create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java rename hibernate-core/src/test/java/org/hibernate/loader/{EntityWithOneToManyResultSetProcessorTest.java => EntityWithNonLazyOneToManySetResultSetProcessorTest.java} (98%) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index 0d1a9fddd0..68e0c19291 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -31,6 +31,7 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; @@ -78,7 +79,7 @@ public class CompositeFetch extends AbstractSingularAttributeFetch { @Override public CompositeFetch buildCompositeFetch( CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 249c141061..e2cb4a1108 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -111,6 +111,7 @@ import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.Value; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; @@ -130,6 +131,7 @@ import org.hibernate.sql.Update; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.AssociationType; +import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; @@ -5120,13 +5122,13 @@ public abstract class AbstractEntityPersister final Type idType = getIdentifierType(); if ( !idType.isComponentType() ) { - entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); + entityIdentifierDefinition = buildSimpleEncapsulatedIdentifierDefinition(); return; } final CompositeType cidType = (CompositeType) idType; if ( !cidType.isEmbedded() ) { - entityIdentifierDefinition = buildEncapsulatedIdentifierDefinition(); + entityIdentifierDefinition = buildEncapsulatedCompositeIdentifierDefinition(); return; } @@ -5155,7 +5157,7 @@ public abstract class AbstractEntityPersister }; } - private EntityIdentifierDefinition buildEncapsulatedIdentifierDefinition() { + private EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition() { final AttributeDefinition simpleIdentifierAttributeAdapter = new AttributeDefinition() { @Override public String getName() { @@ -5196,6 +5198,55 @@ public abstract class AbstractEntityPersister }; } + private EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition() { + final CompositionDefinition compositeIdentifierAttributeAdapter = new CompositionDefinition() { + @Override + public String getName() { + return entityMetamodel.getIdentifierProperty().getName(); + } + + @Override + public Type getType() { + return entityMetamodel.getIdentifierProperty().getType(); + } + + @Override + public AttributeSource getSource() { + return AbstractEntityPersister.this; + } + + @Override + public String toString() { + return ""; + } + + @Override + public Iterable getAttributes() { + ComponentType componentType = (ComponentType) getType(); + //for ( Type type : componentType.getSubtypes() ) { + throw new NotYetImplementedException( "cannot create sub-attribute definitions for a ComponentType yet." ); + //} + } + }; + + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return compositeIdentifierAttributeAdapter; + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return AbstractEntityPersister.this; + } + }; + } + private void collectAttributeDefinitions() { // todo : leverage the attribute definitions housed on EntityMetamodel // for that to work, we'd have to be able to walk our super entity persister(s) diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java index 0584f843ff..3f4dcf216e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java @@ -72,21 +72,21 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie public Iterator iterator() { return new Iterator() { private final int numberOfAttributes = getType().getSubtypes().length; - private int currentAttributeNumber = 0; + private int currentSubAttributeNumber = 0; private int currentColumnPosition = 0; @Override public boolean hasNext() { - return currentAttributeNumber < numberOfAttributes; + return currentSubAttributeNumber < numberOfAttributes; } @Override public AttributeDefinition next() { - final int attributeNumber = currentAttributeNumber; - currentAttributeNumber++; + final int subAttributeNumber = currentSubAttributeNumber; + currentSubAttributeNumber++; - final String name = getType().getPropertyNames()[attributeNumber]; - final Type type = getType().getSubtypes()[attributeNumber]; + final String name = getType().getPropertyNames()[subAttributeNumber]; + final Type type = getType().getSubtypes()[subAttributeNumber]; int columnPosition = currentColumnPosition; currentColumnPosition += type.getColumnSpan( sessionFactory() ); @@ -122,7 +122,7 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie return new CompositeBasedAssociationAttribute( AbstractCompositionDefinition.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, (AssociationType) type, new BaselineAttributeInformation.Builder() @@ -130,11 +130,11 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation(), AbstractCompositionDefinition.this.attributeNumber(), associationKey @@ -144,7 +144,7 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie return new CompositionBasedCompositionAttribute( AbstractCompositionDefinition.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, (CompositeType) type, new BaselineAttributeInformation.Builder() @@ -152,11 +152,11 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() ); } @@ -164,7 +164,7 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie return new CompositeBasedBasicAttribute( AbstractCompositionDefinition.this, sessionFactory(), - currentAttributeNumber, + subAttributeNumber, name, type, new BaselineAttributeInformation.Builder() @@ -172,11 +172,11 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) - .setNullable( getType().getPropertyNullability()[currentAttributeNumber] ) + .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) - .setCascadeStyle( getType().getCascadeStyle( currentAttributeNumber ) ) - .setFetchMode( getType().getFetchMode( currentAttributeNumber ) ) + .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) + .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java new file mode 100644 index 0000000000..3f76e0607b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java @@ -0,0 +1,338 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.Id; + +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * @author Gail Badner + */ +public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class, Customer.class }; + } + + @Test + public void testSimpleNestedCompositeAttributeProcessing() throws Exception { + // create some test data + Session session = openSession(); + session.beginTransaction(); + Person person = new Person(); + person.id = 1; + person.name = "Joe Blow"; + person.address = new Address(); + person.address.address1 = "1313 Mockingbird Lane"; + person.address.city = "Pleasantville"; + person.address.country = "USA"; + AddressType addressType = new AddressType(); + addressType.typeName = "snail mail"; + person.address.type = addressType; + session.save( person ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Person personGotten = (Person) session.get( Person.class, person.id ); + assertEquals( person.id, personGotten.id ); + assertEquals( person.address.address1, personGotten.address.address1 ); + assertEquals( person.address.city, personGotten.address.city ); + assertEquals( person.address.country, personGotten.address.country ); + assertEquals( person.address.type.typeName, personGotten.address.type.typeName ); + session.getTransaction().commit(); + session.close(); + + List results = getResults( sessionFactory().getEntityPersister( Person.class.getName() ) ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Person personWork = ExtraAssertions.assertTyping( Person.class, result ); + assertEquals( person.id, personWork.id ); + assertEquals( person.address.address1, personWork.address.address1 ); + assertEquals( person.address.city, personWork.address.city ); + assertEquals( person.address.country, personWork.address.country ); + assertEquals( person.address.type.typeName, personGotten.address.type.typeName ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Person" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + /* + @Test + public void testNestedCompositeElementCollectionProcessing() throws Exception { + // create some test data + Session session = openSession(); + session.beginTransaction(); + Customer customer = new Customer(); + customer.id = 1L; + Investment investment1 = new Investment(); + investment1.description = "stock"; + investment1.date = new Date(); + investment1.monetaryAmount = new MonetaryAmount(); + investment1.monetaryAmount.currency = MonetaryAmount.CurrencyCode.USD; + investment1.monetaryAmount.amount = BigDecimal.valueOf( 1234, 2 ); + Investment investment2 = new Investment(); + investment2.description = "bond"; + investment2.date = new Date(); + investment2.monetaryAmount = new MonetaryAmount(); + investment2.monetaryAmount.currency = MonetaryAmount.CurrencyCode.EUR; + investment2.monetaryAmount.amount = BigDecimal.valueOf( 98176, 1 ); + customer.investments.add( investment1 ); + customer.investments.add( investment2 ); + session.save( customer ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Customer customerGotten = (Customer) session.get( Customer.class, customer.id ); + assertEquals( customer.id, customerGotten.id ); + session.getTransaction().commit(); + session.close(); + + List results = getResults( sessionFactory().getEntityPersister( Customer.class.getName() ) ); + + assertEquals( 2, results.size() ); + assertSame( results.get( 0 ), results.get( 1 ) ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Customer customerWork = ExtraAssertions.assertTyping( Customer.class, result ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Customer" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + */ + + private List getResults(EntityPersister entityPersister ) { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 1 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + workSession.getTransaction().commit(); + workSession.close(); + return results; + } + + @Entity( name = "Person" ) + public static class Person implements Serializable { + @Id + Integer id; + String name; + + @Embedded + Address address; + } + + @Embeddable + public static class Address implements Serializable { + String address1; + String city; + String country; + AddressType type; + } + + @Embeddable + public static class AddressType { + String typeName; + } + + @Entity( name = "Customer" ) + public static class Customer { + private Long id; + private List investments = new ArrayList(); + + @Id + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + @ElementCollection(fetch = FetchType.EAGER) + public List getInvestments() { + return investments; + } + public void setInvestments(List investments) { + this.investments = investments; + } + } + + @Embeddable + public static class Investment { + private MonetaryAmount monetaryAmount; + private String description; + private Date date; + + @Embedded + public MonetaryAmount getMonetaryAmount() { + return monetaryAmount; + } + public void setMonetaryAmount(MonetaryAmount monetaryAmount) { + this.monetaryAmount = monetaryAmount; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public Date getDate() { + return date; + } + public void setDate(Date date) { + this.date = date; + } + } + + @Embeddable + public static class MonetaryAmount { + public static enum CurrencyCode { + USD, + EUR + } + private BigDecimal amount; + @Column(length = 3) + @Enumerated(EnumType.STRING) + private CurrencyCode currency; + + public BigDecimal getAmount() { + return amount; + } + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public CurrencyCode getCurrency() { + return currency; + } + public void setCurrency(CurrencyCode currency) { + this.currency = currency; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java rename to hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java index da1186e0f6..dfc467ccda 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithCollectionResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyCollectionResultSetProcessorTest.java @@ -67,7 +67,7 @@ import static org.junit.Assert.assertTrue; /** * @author Gail Badner */ -public class EntityWithCollectionResultSetProcessorTest extends BaseCoreFunctionalTestCase { +public class EntityWithNonLazyCollectionResultSetProcessorTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java new file mode 100644 index 0000000000..abd4dadf7f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManyListResultSetProcessorTest.java @@ -0,0 +1,215 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class EntityWithNonLazyOneToManyListResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Poster.class, Message.class }; + } + + @Test + public void testEntityWithList() throws Exception { + final EntityPersister entityPersister = sessionFactory().getEntityPersister( Poster.class.getName() ); + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Poster poster = new Poster(); + poster.pid = 0; + poster.name = "John Doe"; + Message message1 = new Message(); + message1.mid = 1; + message1.msgTxt = "Howdy!"; + message1.poster = poster; + poster.messages.add( message1 ); + Message message2 = new Message(); + message2.mid = 2; + message2.msgTxt = "Bye!"; + message2.poster = poster; + poster.messages.add( message2 ); + session.save( poster ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Poster posterGotten = (Poster) session.get( Poster.class, poster.pid ); + assertEquals( 0, posterGotten.pid.intValue() ); + assertEquals( poster.name, posterGotten.name ); + assertTrue( Hibernate.isInitialized( posterGotten.messages ) ); + assertEquals( 2, posterGotten.messages.size() ); + assertEquals( message1.msgTxt, posterGotten.messages.get( 0 ).msgTxt ); + assertEquals( message2.msgTxt, posterGotten.messages.get( 1 ).msgTxt ); + assertSame( posterGotten, posterGotten.messages.get( 0 ).poster ); + assertSame( posterGotten, posterGotten.messages.get( 1 ).poster ); + session.getTransaction().commit(); + session.close(); + + { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + ps.setInt( 1, 0 ); + ResultSet resultSet = ps.executeQuery(); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + new QueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + assertEquals( 2, results.size() ); + Object result1 = results.get( 0 ); + assertNotNull( result1 ); + assertSame( result1, results.get( 1 ) ); + + Poster workPoster = ExtraAssertions.assertTyping( Poster.class, result1 ); + assertEquals( 0, workPoster.pid.intValue() ); + assertEquals( poster.name, workPoster.name ); + assertTrue( Hibernate.isInitialized( workPoster.messages ) ); + assertEquals( 2, workPoster.messages.size() ); + assertTrue( Hibernate.isInitialized( posterGotten.messages ) ); + assertEquals( 2, workPoster.messages.size() ); + assertEquals( message1.msgTxt, workPoster.messages.get( 0 ).msgTxt ); + assertEquals( message2.msgTxt, workPoster.messages.get( 1 ).msgTxt ); + assertSame( workPoster, workPoster.messages.get( 0 ).poster ); + assertSame( workPoster, workPoster.messages.get( 1 ).poster ); + workSession.getTransaction().commit(); + workSession.close(); + } + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.delete( poster ); + session.getTransaction().commit(); + session.close(); + } + + @Entity( name = "Message" ) + public static class Message { + @Id + private Integer mid; + private String msgTxt; + @ManyToOne + @JoinColumn + private Poster poster; + } + + @Entity( name = "Poster" ) + public static class Poster { + @Id + private Integer pid; + private String name; + @OneToMany(mappedBy = "poster", fetch = FetchType.EAGER, cascade = CascadeType.ALL ) + private List messages = new ArrayList(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java rename to hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java index 23b3e860ee..eee117e602 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EntityWithOneToManyResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EntityWithNonLazyOneToManySetResultSetProcessorTest.java @@ -70,7 +70,7 @@ import static org.junit.Assert.fail; /** * @author Gail Badner */ -public class EntityWithOneToManyResultSetProcessorTest extends BaseCoreFunctionalTestCase { +public class EntityWithNonLazyOneToManySetResultSetProcessorTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { From 4620ff4b4fe670bbf121b709e9da6b418c24ee43 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 13 May 2013 11:54:13 -0700 Subject: [PATCH 02/17] HHH-7841 - Redesign Loader --- .../AbstractJoinableAssociationImpl.java | 4 +- .../CollectionJoinableAssociationImpl.java | 1 + .../EntityJoinableAssociationImpl.java | 2 + .../internal/EntityLoadQueryBuilderImpl.java | 4 + .../plan/internal/LoadPlanBuildingHelper.java | 4 +- .../loader/plan/spi/CompositeFetch.java | 7 +- .../loader/plan/spi/EntityFetch.java | 6 +- .../loader/plan/spi/EntityReturn.java | 15 +- .../AbstractLoadPlanBuilderStrategy.java | 175 +++++-- .../entity/AbstractEntityPersister.java | 122 +---- .../EntityIdentifierDefinitionHelper.java | 313 +++++++++++++ .../{Helper.java => FetchStrategyHelper.java} | 2 +- .../spi/MetadataDrivenModelGraphVisitor.java | 37 +- .../AbstractCompositionDefinition.java | 4 +- .../CompositeBasedAssociationAttribute.java | 10 +- .../EntityBasedAssociationAttribute.java | 10 +- ...atedCompositeIdResultSetProcessorTest.java | 434 ++++++++++++++++++ .../graph/internal/advisor/AdviceHelper.java | 7 + 18 files changed, 964 insertions(+), 193 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java rename hibernate-core/src/main/java/org/hibernate/persister/walking/internal/{Helper.java => FetchStrategyHelper.java} (99%) create mode 100644 hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java index 74c9c31ad0..11bf50aaf5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java @@ -56,12 +56,10 @@ public abstract class AbstractJoinableAssociationImpl implements JoinableAssocia EntityReference currentEntityReference, CollectionReference currentCollectionReference, String withClause, + boolean isNullable, boolean hasRestriction, Map enabledFilters) throws MappingException { this.propertyPath = currentFetch.getPropertyPath(); - final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) currentFetch.getOwner().retrieveFetchSourcePersister(); - final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( currentFetch.getOwnerPropertyName() ); - final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber ); if ( currentFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) { joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java index 254c1a9c33..df49038b35 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java @@ -56,6 +56,7 @@ public class CollectionJoinableAssociationImpl extends AbstractJoinableAssociati currentEntityReference, collectionFetch, withClause, + true, hasRestriction, enabledFilters ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java index 34d22b56ba..d52323bc09 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java @@ -48,6 +48,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm EntityFetch entityFetch, CollectionReference currentCollectionReference, String withClause, + boolean isNullable, boolean hasRestriction, Map enabledFilters) throws MappingException { super( @@ -55,6 +56,7 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm entityFetch, currentCollectionReference, withClause, + isNullable, hasRestriction, enabledFilters ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java index aef90267f1..769f419583 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java @@ -149,10 +149,14 @@ public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder { @Override public void startingEntityFetch(EntityFetch entityFetch) { + final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) entityFetch.getOwner().retrieveFetchSourcePersister(); + final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( entityFetch.getOwnerPropertyName() ); + final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber ); EntityJoinableAssociationImpl assoc = new EntityJoinableAssociationImpl( entityFetch, getCurrentCollectionReference(), "", // getWithClause( entityFetch.getPropertyPath() ) + isNullable, false, // hasRestriction( entityFetch.getPropertyPath() ) loadQueryInfluencers.getEnabledFilters() ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java index 8a645e1e2c..ef71c92038 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/internal/LoadPlanBuildingHelper.java @@ -33,6 +33,7 @@ import org.hibernate.loader.plan.spi.FetchOwner; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.EntityType; /** * @author Steve Ebersole @@ -63,6 +64,7 @@ public class LoadPlanBuildingHelper { LockMode.NONE, // todo : for now fetchOwner, attributeDefinition.getName(), + (EntityType) attributeDefinition.getType(), fetchStrategy ); } @@ -73,7 +75,7 @@ public class LoadPlanBuildingHelper { LoadPlanBuildingContext loadPlanBuildingContext) { return new CompositeFetch( loadPlanBuildingContext.getSessionFactory(), - (AbstractFetchOwner) fetchOwner, + fetchOwner, attributeDefinition.getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index 68e0c19291..b002c7b567 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -73,7 +73,12 @@ public class CompositeFetch extends AbstractSingularAttributeFetch { AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, LoadPlanBuildingContext loadPlanBuildingContext) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 444e80cdc1..72d2b4744b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -55,10 +55,14 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit LockMode lockMode, FetchOwner owner, String ownerProperty, + EntityType entityType, FetchStrategy fetchStrategy) { super( sessionFactory, lockMode, owner, ownerProperty, fetchStrategy ); - this.associationType = (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); + this.associationType = entityType; + // (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); + //this.associationType = + // (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( getPropertyPath().getFullPath() ); this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java index 2d3bb88b52..cb15874c8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java @@ -134,7 +134,7 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe @Override public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - EntityKey entityKey = context.getDictatedRootEntityKey(); + EntityKey entityKey = getEntityKeyFromContext( context ); if ( entityKey != null ) { context.getIdentifierResolutionContext( this ).registerEntityKey( entityKey ); return; @@ -147,6 +147,19 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe } } + private EntityKey getEntityKeyFromContext(ResultSetProcessingContext context) { + if ( context.getDictatedRootEntityKey() != null ) { + return context.getDictatedRootEntityKey(); + } + else if ( context.getQueryParameters().getOptionalId() != null ) { + return context.getSession().generateEntityKey( + context.getQueryParameters().getOptionalId(), + getEntityPersister() + ); + } + return null; + } + @Override public void resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { final IdentifierResolutionContext identifierResolutionContext = context.getIdentifierResolutionContext( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java index e6968905d1..89a5272ae5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java @@ -37,6 +37,7 @@ import org.jboss.logging.MDC; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.EntityKey; @@ -44,6 +45,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.PropertyPath; import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.loader.plan.spi.AbstractSingularAttributeFetch; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; @@ -83,6 +85,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private ArrayDeque fetchOwnerStack = new ArrayDeque(); private ArrayDeque collectionReferenceStack = new ArrayDeque(); + //private AbstractIdentifierAttributeCollector currentIdentifierAttributeCollector = null; + protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; } @@ -117,6 +121,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder MDC.remove( MDC_KEY ); fetchOwnerStack.clear(); collectionReferenceStack.clear(); +// currentIdentifierAttributeCollector = null; } @Override @@ -413,10 +418,21 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder ); } - if ( FetchOwner.class.isInstance( associationFetch ) ) { + // If we are collecting fetches for the identifier then + // currentIdentifierAttributeCollector will be non-null. + // In that case, we do not want to continue walking the association, so + // don't push associationFetch to the stack. + //final boolean continueWalk = currentIdentifierAttributeCollector == null; + //if ( continueWalk && FetchOwner.class.isInstance( associationFetch) ) { + if ( FetchOwner.class.isInstance( associationFetch) ) { pushToStack( (FetchOwner) associationFetch ); } + //if ( ! continueWalk ) { + // popFromStack(); + //} + + //return continueWalk; return true; } @@ -431,7 +447,21 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } private void pushToStack(FetchOwner fetchOwner) { - log.trace( "Pushing fetch owner to stack : " + fetchOwner ); +// if ( fetchOwner instanceof AbstractIdentifierAttributeCollector ) { +// if ( currentIdentifierAttributeCollector != null ) { +// throw new WalkingException( +// String.format( +// "An AbstractIdentifierAttributeCollector is already being processed: %s", +// currentIdentifierAttributeCollector +// ) +// ); +// } +// currentIdentifierAttributeCollector = (AbstractIdentifierAttributeCollector) fetchOwner; +// log.trace( "Pushing AbstractIdentifierAttributeCollector fetch owner to stack : " + fetchOwner ); +// } +// else { + log.trace( "Pushing fetch owner to stack : " + fetchOwner ); +// } mdcStack().push( fetchOwner.getPropertyPath() ); fetchOwnerStack.addFirst( fetchOwner ); } @@ -442,7 +472,30 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private FetchOwner popFromStack() { final FetchOwner last = fetchOwnerStack.removeFirst(); - log.trace( "Popped fetch owner from stack : " + last ); +// if ( last instanceof AbstractIdentifierAttributeCollector ) { +// if ( currentIdentifierAttributeCollector == null ) { +// throw new WalkingException( +// String.format( +// "Popped fetch owner was an AbstractIdentifierAttributeCollector [%s], but none in process (currentIdentifierAttributeCollector == null)", +// last +// ) +// ); +// } +// else if ( currentIdentifierAttributeCollector != last ) { +// throw new WalkingException( +// String.format( +// "Expected popped fetch owner to be [%s], but instead it was [%s])", +// currentIdentifierAttributeCollector, +// last +// ) +// ); +// } +// currentIdentifierAttributeCollector = null; +// log.trace( "Popped AbstractIdentifierAttributeCollector fetch owner from stack : " + last ); +// } +// else { + log.trace( "Popped fetch owner from stack : " + last ); +// } mdcStack().pop(); if ( FetchStackAware.class.isInstance( last ) ) { ( (FetchStackAware) last ).poppedFromStack(); @@ -487,15 +540,13 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder implements FetchOwner, EntityReference, FetchStackAware { protected final EntityReference entityReference; - private final PropertyPath propertyPath; - protected final List identifierFetches = new ArrayList(); - protected final Map fetchToHydratedStateExtractorMap - = new HashMap(); + protected final List identifierFetches = new ArrayList(); + protected final Map fetchToHydratedStateExtractorMap + = new HashMap(); public AbstractIdentifierAttributeCollector(EntityReference entityReference) { this.entityReference = entityReference; - this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); } @Override @@ -533,7 +584,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder LoadPlanBuildingContext loadPlanBuildingContext) { // we have a key-many-to-one // - // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch // impl. We collect them there and later build the IdentifierDescription final EntityFetch fetch = LoadPlanBuildingHelper.buildStandardEntityFetch( this, @@ -551,7 +602,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder CompositionDefinition attributeDefinition, LoadPlanBuildingContext loadPlanBuildingContext) { // nested composition. Unusual, but not disallowed. // - // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back throw our #addFetch + // IMPL NOTE: we pass ourselves as the FetchOwner which will route the fetch back through our #addFetch // impl. We collect them there and later build the IdentifierDescription return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, @@ -570,7 +621,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public void addFetch(Fetch fetch) { - identifierFetches.add( (EntityFetch) fetch ); + identifierFetches.add( (AbstractSingularAttributeFetch) fetch ); } @Override @@ -588,11 +639,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return ( (FetchOwner) entityReference ).retrieveFetchSourcePersister(); } - @Override - public PropertyPath getPropertyPath() { - return propertyPath; - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { throw new WalkingException( @@ -602,43 +648,59 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + private final PropertyPath propertyPath; + public EncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); } @Override protected IdentifierDescription buildIdentifierDescription() { return new IdentifierDescriptionImpl( entityReference, - identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), null ); } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } } protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + private final PropertyPath propertyPath; public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); } @Override protected IdentifierDescription buildIdentifierDescription() { return new IdentifierDescriptionImpl( entityReference, - identifierFetches.toArray( new EntityFetch[ identifierFetches.size() ] ), + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), fetchToHydratedStateExtractorMap ); } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } } private static class IdentifierDescriptionImpl implements IdentifierDescription { private final EntityReference entityReference; - private final EntityFetch[] identifierFetches; - private final Map fetchToHydratedStateExtractorMap; + private final AbstractSingularAttributeFetch[] identifierFetches; + private final Map fetchToHydratedStateExtractorMap; private IdentifierDescriptionImpl( - EntityReference entityReference, EntityFetch[] identifierFetches, - Map fetchToHydratedStateExtractorMap) { + EntityReference entityReference, + AbstractSingularAttributeFetch[] identifierFetches, + Map fetchToHydratedStateExtractorMap) { this.entityReference = entityReference; this.identifierFetches = identifierFetches; this.fetchToHydratedStateExtractorMap = fetchToHydratedStateExtractorMap; @@ -656,24 +718,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final Object ownerIdentifierHydratedState = ownerIdentifierResolutionContext.getHydratedForm(); if ( ownerIdentifierHydratedState != null ) { - for ( EntityFetch fetch : identifierFetches ) { - final IdentifierResolutionContext identifierResolutionContext = - context.getIdentifierResolutionContext( fetch ); - // if the identifier was already hydrated, nothing to do - if ( identifierResolutionContext.getHydratedForm() != null ) { - continue; - } + for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + if ( fetch instanceof EntityFetch ) { + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( (EntityFetch) fetch ); + // if the identifier was already hydrated, nothing to do + if ( identifierResolutionContext.getHydratedForm() != null ) { + continue; + } + // try to extract the sub-hydrated value from the owners tuple array + if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { + Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) + .extract( ownerIdentifierHydratedState ); + identifierResolutionContext.registerHydratedForm( extracted ); + continue; + } - // try to extract the sub-hydrated value from the owners tuple array - if ( fetchToHydratedStateExtractorMap != null && ownerIdentifierHydratedState != null ) { - Serializable extracted = (Serializable) fetchToHydratedStateExtractorMap.get( fetch ) - .extract( ownerIdentifierHydratedState ); - identifierResolutionContext.registerHydratedForm( extracted ); - continue; + // if we can't, then read from result set + fetch.hydrate( resultSet, context ); + } + else { + throw new NotYetImplementedException( "Cannot hydrate identifier Fetch that is not an EntityFetch" ); } - - // if we can't, then read from result set - fetch.hydrate( resultSet, context ); } return; } @@ -689,15 +755,8 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder @Override public EntityKey resolve(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { - for ( EntityFetch fetch : identifierFetches ) { - final IdentifierResolutionContext identifierResolutionContext = - context.getIdentifierResolutionContext( fetch ); - if ( identifierResolutionContext.getEntityKey() != null ) { - continue; - } - - EntityKey fetchKey = fetch.resolveInIdentifier( resultSet, context ); - identifierResolutionContext.registerEntityKey( fetchKey ); + for ( AbstractSingularAttributeFetch fetch : identifierFetches ) { + resolveIdentifierFetch( resultSet, context, fetch ); } final IdentifierResolutionContext ownerIdentifierResolutionContext = @@ -710,6 +769,28 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } + private static void resolveIdentifierFetch( + ResultSet resultSet, + ResultSetProcessingContext context, + AbstractSingularAttributeFetch fetch) throws SQLException { + if ( fetch instanceof EntityFetch ) { + EntityFetch entityFetch = (EntityFetch) fetch; + final IdentifierResolutionContext identifierResolutionContext = + context.getIdentifierResolutionContext( entityFetch ); + if ( identifierResolutionContext.getEntityKey() != null ) { + return; + } + + EntityKey fetchKey = entityFetch.resolveInIdentifier( resultSet, context ); + identifierResolutionContext.registerEntityKey( fetchKey ); + } + else if ( fetch instanceof CompositeFetch ) { + for ( Fetch subFetch : fetch.getFetches() ) { + resolveIdentifierFetch( resultSet, context, (AbstractSingularAttributeFetch) subFetch ); + } + } + } + public static class MDCStack { private ArrayDeque pathStack = new ArrayDeque(); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index e2cb4a1108..51062e1524 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -109,6 +109,7 @@ import org.hibernate.metamodel.binding.SimpleValueBinding; import org.hibernate.metamodel.binding.SingularAttributeBinding; import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.Value; +import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeSource; import org.hibernate.persister.walking.spi.CompositionDefinition; @@ -5122,129 +5123,20 @@ public abstract class AbstractEntityPersister final Type idType = getIdentifierType(); if ( !idType.isComponentType() ) { - entityIdentifierDefinition = buildSimpleEncapsulatedIdentifierDefinition(); + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildSimpleEncapsulatedIdentifierDefinition( this ); return; } final CompositeType cidType = (CompositeType) idType; if ( !cidType.isEmbedded() ) { - entityIdentifierDefinition = buildEncapsulatedCompositeIdentifierDefinition(); + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildEncapsulatedCompositeIdentifierDefinition( this ); return; } - entityIdentifierDefinition = new NonEncapsulatedEntityIdentifierDefinition() { - @Override - public Iterable getAttributes() { - // todo : implement - throw new NotYetImplementedException(); - } - - @Override - public Class getSeparateIdentifierMappingClass() { - // todo : implement - throw new NotYetImplementedException(); - } - - @Override - public boolean isEncapsulated() { - return false; - } - - @Override - public EntityDefinition getEntityDefinition() { - return AbstractEntityPersister.this; - } - }; - } - - private EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition() { - final AttributeDefinition simpleIdentifierAttributeAdapter = new AttributeDefinition() { - @Override - public String getName() { - return entityMetamodel.getIdentifierProperty().getName(); - } - - @Override - public Type getType() { - return entityMetamodel.getIdentifierProperty().getType(); - } - - @Override - public AttributeSource getSource() { - return AbstractEntityPersister.this; - } - - @Override - public String toString() { - return ""; - } - }; - - return new EncapsulatedEntityIdentifierDefinition() { - @Override - public AttributeDefinition getAttributeDefinition() { - return simpleIdentifierAttributeAdapter; - } - - @Override - public boolean isEncapsulated() { - return true; - } - - @Override - public EntityDefinition getEntityDefinition() { - return AbstractEntityPersister.this; - } - }; - } - - private EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition() { - final CompositionDefinition compositeIdentifierAttributeAdapter = new CompositionDefinition() { - @Override - public String getName() { - return entityMetamodel.getIdentifierProperty().getName(); - } - - @Override - public Type getType() { - return entityMetamodel.getIdentifierProperty().getType(); - } - - @Override - public AttributeSource getSource() { - return AbstractEntityPersister.this; - } - - @Override - public String toString() { - return ""; - } - - @Override - public Iterable getAttributes() { - ComponentType componentType = (ComponentType) getType(); - //for ( Type type : componentType.getSubtypes() ) { - throw new NotYetImplementedException( "cannot create sub-attribute definitions for a ComponentType yet." ); - //} - } - }; - - return new EncapsulatedEntityIdentifierDefinition() { - @Override - public AttributeDefinition getAttributeDefinition() { - return compositeIdentifierAttributeAdapter; - } - - @Override - public boolean isEncapsulated() { - return true; - } - - @Override - public EntityDefinition getEntityDefinition() { - return AbstractEntityPersister.this; - } - }; + entityIdentifierDefinition = + EntityIdentifierDefinitionHelper.buildNonEncapsulatedCompositeIdentifierDefinition( this ); } private void collectAttributeDefinitions() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java new file mode 100644 index 0000000000..abb5e5b958 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java @@ -0,0 +1,313 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.walking.internal; + +import java.util.Iterator; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.AssociationType; +import org.hibernate.type.ComponentType; +import org.hibernate.type.Type; + +import static org.hibernate.engine.internal.JoinHelper.getLHSColumnNames; +import static org.hibernate.engine.internal.JoinHelper.getLHSTableName; + +/** + * @author Gail Badner + */ +public class EntityIdentifierDefinitionHelper { + + public static EntityIdentifierDefinition buildSimpleEncapsulatedIdentifierDefinition(final AbstractEntityPersister entityPersister) { + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return new AttributeDefinitionAdapter( entityPersister); + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + public static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) { + + return new EncapsulatedEntityIdentifierDefinition() { + @Override + public AttributeDefinition getAttributeDefinition() { + return new CompositeAttributeDefinitionAdapter( entityPersister ); + } + + @Override + public boolean isEncapsulated() { + return true; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + public static EntityIdentifierDefinition buildNonEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) { + return new NonEncapsulatedEntityIdentifierDefinition() { + @Override + public Iterable getAttributes() { + return new CompositeAttributeDefinitionAdapter( entityPersister ).getAttributes(); + } + + @Override + public Class getSeparateIdentifierMappingClass() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getType().getReturnedClass(); + } + + @Override + public boolean isEncapsulated() { + return false; + } + + @Override + public EntityDefinition getEntityDefinition() { + return entityPersister; + } + }; + } + + private static class AttributeDefinitionAdapter implements AttributeDefinition { + private final AbstractEntityPersister entityPersister; + + AttributeDefinitionAdapter(AbstractEntityPersister entityPersister) { + this.entityPersister = entityPersister; + } + + @Override + public String getName() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getName(); + } + + @Override + public Type getType() { + return entityPersister.getEntityMetamodel().getIdentifierProperty().getType(); + } + + @Override + public AttributeSource getSource() { + return entityPersister; + } + + @Override + public String toString() { + return ""; + } + + protected AbstractEntityPersister getEntityPersister() { + return entityPersister; + } + } + + private static class CompositeAttributeDefinitionAdapter extends AttributeDefinitionAdapter implements CompositionDefinition { + + CompositeAttributeDefinitionAdapter(AbstractEntityPersister entityPersister) { + super( entityPersister ); + } + + @Override + public Iterable getAttributes() { + return new Iterable() { + @Override + public Iterator iterator() { + final ComponentType componentType = (ComponentType) getType(); + return new Iterator() { + private final int numberOfAttributes = componentType.getSubtypes().length; + private int currentSubAttributeNumber = 0; + private int currentColumnPosition = 0; + + @Override + public boolean hasNext() { + return currentSubAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + final int subAttributeNumber = currentSubAttributeNumber; + currentSubAttributeNumber++; + + final AttributeSource source = getSource(); + final String name = componentType.getPropertyNames()[subAttributeNumber]; + final Type type = componentType.getSubtypes()[subAttributeNumber]; + + final int columnPosition = currentColumnPosition; + currentColumnPosition += type.getColumnSpan( getEntityPersister().getFactory() ); + + if ( type.isAssociationType() ) { + final AssociationType aType = (AssociationType) type; + final Joinable joinable = aType.getAssociatedJoinable( getEntityPersister().getFactory() ); + return new AssociationAttributeDefinition() { + @Override + public AssociationKey getAssociationKey() { + /* TODO: is this always correct? */ + //return new AssociationKey( + // joinable.getTableName(), + // JoinHelper.getRHSColumnNames( aType, getEntityPersister().getFactory() ) + //); + return new AssociationKey( + getEntityPersister().getTableName(), + getLHSColumnNames( + aType, + -1, + columnPosition, + getEntityPersister(), + getEntityPersister().getFactory() + ) + ); + + } + + @Override + public boolean isCollection() { + return false; + } + + @Override + public EntityDefinition toEntityDefinition() { + return (EntityPersister) joinable; + } + + @Override + public CollectionDefinition toCollectionDefinition() { + throw new WalkingException( "A collection cannot be mapped to a composite ID sub-attribute." ); + } + + @Override + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { + return new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + } + + @Override + public CascadeStyle determineCascadeStyle() { + return CascadeStyles.NONE; + } + + @Override + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + else if ( type.isComponentType() ) { + return new CompositionDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + + @Override + public Iterable getAttributes() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + }; + } + else { + return new AttributeDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java similarity index 99% rename from hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java rename to hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java index 29eb9f5dbe..7d6cf84335 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/FetchStrategyHelper.java @@ -42,7 +42,7 @@ import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class Helper { +public class FetchStrategyHelper { /** * Determine the fetch-style (if one) explicitly set for this association via fetch profiles. *

diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index 8a9ceb77a9..c48461a885 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -28,12 +28,21 @@ import java.util.Set; import org.jboss.logging.Logger; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.type.Type; +import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; + /** * Provides model graph visitation based on the defined metadata (as opposed to based on the incoming graph * as we see in cascade processing). In layman terms, we are walking the graph of the users model as defined by @@ -116,7 +125,7 @@ public class MetadataDrivenModelGraphVisitor { final boolean continueWalk; if ( attributeDefinition.getType().isAssociationType() ) { continueWalk = - ! isDuplicateAssociation( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) && + ! isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) && strategy.startingAttribute( attributeDefinition ); } else { @@ -143,6 +152,11 @@ public class MetadataDrivenModelGraphVisitor { private void visitAssociation(AssociationAttributeDefinition attribute) { // todo : do "too deep" checks; but see note about adding depth to PropertyPath + if ( !addAssociationKey( attribute.getAssociationKey() ) ) { + log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() ); + return; + } + if ( attribute.isCollection() ) { visitCollectionDefinition( attribute.toCollectionDefinition() ); } @@ -212,15 +226,18 @@ public class MetadataDrivenModelGraphVisitor { private final Set visitedAssociationKeys = new HashSet(); - protected boolean isDuplicateAssociation(AssociationKey associationKey) { - boolean isDuplicate = !visitedAssociationKeys.add( associationKey ); - if ( isDuplicate ) { - log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() ); - return true; - } - else { - return false; - } + /** + * Add association key. + * @param associationKey - the association key. + * @return true, if the association key was added; + * false, otherwise (indicating the association key was already visited). + */ + protected boolean addAssociationKey(AssociationKey associationKey) { + return visitedAssociationKeys.add( associationKey ); + } + + protected boolean isDuplicateAssociationKey(AssociationKey associationKey) { + return visitedAssociationKeys.contains( associationKey ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java index 3f4dcf216e..554a630ed6 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java @@ -101,13 +101,13 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie getLHSTableName( aType, attributeNumber(), - (OuterJoinLoadable) joinable + (OuterJoinLoadable) locateOwningPersister() ), getLHSColumnNames( aType, attributeNumber(), columnPosition, - (OuterJoinLoadable) joinable, + (OuterJoinLoadable) locateOwningPersister(), sessionFactory() ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java index eb7224d94d..81c7f343f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java @@ -23,8 +23,6 @@ */ package org.hibernate.tuple.component; -import java.io.Serializable; - import org.hibernate.FetchMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; @@ -37,7 +35,7 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; -import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.CollectionDefinition; @@ -132,7 +130,7 @@ public class CompositeBasedAssociationAttribute EntityPersister owningPersister, PropertyPath propertyPath, int ownerAttributeNumber) { - return Helper.determineFetchStyleByProfile( + return FetchStrategyHelper.determineFetchStyleByProfile( loadQueryInfluencers, owningPersister, propertyPath, @@ -141,11 +139,11 @@ public class CompositeBasedAssociationAttribute } protected FetchStyle determineFetchStyleByMetadata(FetchMode fetchMode, AssociationType type) { - return Helper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() ); + return FetchStrategyHelper.determineFetchStyleByMetadata( fetchMode, type, sessionFactory() ); } private FetchTiming determineFetchTiming(FetchStyle style) { - return Helper.determineFetchTiming( style, getType(), sessionFactory() ); + return FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ); } private EntityPersister locateOwningPersister() { diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java index 88405553db..78d803e43b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedAssociationAttribute.java @@ -34,7 +34,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; -import org.hibernate.persister.walking.internal.Helper; +import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.CollectionDefinition; @@ -129,22 +129,22 @@ public class EntityBasedAssociationAttribute public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { final EntityPersister owningPersister = getSource().getEntityPersister(); - FetchStyle style = Helper.determineFetchStyleByProfile( + FetchStyle style = FetchStrategyHelper.determineFetchStyleByProfile( loadQueryInfluencers, owningPersister, propertyPath, attributeNumber() ); if ( style == null ) { - style = Helper.determineFetchStyleByMetadata( - ((OuterJoinLoadable) getSource().getEntityPersister()).getFetchMode( attributeNumber() ), + style = FetchStrategyHelper.determineFetchStyleByMetadata( + ( (OuterJoinLoadable) getSource().getEntityPersister() ).getFetchMode( attributeNumber() ), getType(), sessionFactory() ); } return new FetchStrategy( - Helper.determineFetchTiming( style, getType(), sessionFactory() ), + FetchStrategyHelper.determineFetchTiming( style, getType(), sessionFactory() ), style ); } diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java new file mode 100644 index 0000000000..2482fcf4ca --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeIdResultSetProcessorTest.java @@ -0,0 +1,434 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.junit.Test; + +import org.hibernate.LockOptions; +import org.hibernate.Session; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.jdbc.Work; +import org.hibernate.loader.internal.EntityLoadQueryBuilderImpl; +import org.hibernate.loader.internal.LoadQueryAliasResolutionContextImpl; +import org.hibernate.loader.internal.ResultSetProcessorImpl; +import org.hibernate.loader.plan.internal.SingleRootReturnLoadPlanBuilderStrategy; +import org.hibernate.loader.plan.spi.LoadPlan; +import org.hibernate.loader.plan.spi.build.LoadPlanBuilder; +import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; +import org.hibernate.loader.spi.NamedParameterContext; +import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.junit4.ExtraAssertions; +import org.hibernate.type.Type; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class EncapsulatedCompositeIdResultSetProcessorTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Parent.class, CardField.class, Card.class }; + } + + @Test + public void testSimpleCompositeId() throws Exception { + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Parent parent = new Parent(); + parent.id = new ParentPK(); + parent.id.firstName = "Joe"; + parent.id.lastName = "Blow"; + session.save( parent ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Parent parentGotten = (Parent) session.get( Parent.class, parent.id ); + assertEquals( parent, parentGotten ); + session.getTransaction().commit(); + session.close(); + + final List results = getResults( + sessionFactory().getEntityPersister( Parent.class.getName() ), + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, "Joe" ); + ps.setString( 2, "Blow" ); + } + + @Override + public QueryParameters getQueryParameters() { + return new QueryParameters(); + } + + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + Parent parentWork = ExtraAssertions.assertTyping( Parent.class, result ); + assertEquals( parent, parentWork ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Parent" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testCompositeIdWithKeyManyToOne() throws Exception { + final String cardId = "ace-of-spades"; + + // create some test data + Session session = openSession(); + session.beginTransaction(); + Card card = new Card( cardId ); + final CardField cardField = new CardField( card, 1 ); + session.persist( card ); + session.persist( cardField ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + Card cardProxy = (Card) session.load( Card.class, cardId ); + final CardFieldPK cardFieldPK = new CardFieldPK( cardProxy, 1 ); + CardField cardFieldGotten = (CardField) session.get( CardField.class, cardFieldPK ); + + //assertEquals( card, cardGotten ); + session.getTransaction().commit(); + session.close(); + + final EntityPersister entityPersister = sessionFactory().getEntityPersister( CardField.class.getName() ); + + final List results = getResults( + entityPersister, + new Callback() { + @Override + public void bind(PreparedStatement ps) throws SQLException { + ps.setString( 1, cardField.primaryKey.card.id ); + ps.setInt( 2, cardField.primaryKey.fieldNumber ); + } + + @Override + public QueryParameters getQueryParameters() { + QueryParameters qp = new QueryParameters(); + qp.setPositionalParameterTypes( new Type[] { entityPersister.getIdentifierType() } ); + qp.setPositionalParameterValues( new Object[] { cardFieldPK } ); + qp.setOptionalObject( null ); + qp.setOptionalEntityName( entityPersister.getEntityName() ); + qp.setOptionalId( cardFieldPK ); + qp.setLockOptions( LockOptions.NONE ); + return qp; + } + + } + ); + assertEquals( 1, results.size() ); + Object result = results.get( 0 ); + assertNotNull( result ); + + CardField cardFieldWork = ExtraAssertions.assertTyping( CardField.class, result ); + assertEquals( cardFieldGotten, cardFieldWork ); + + // clean up test data + session = openSession(); + session.beginTransaction(); + session.createQuery( "delete CardField" ).executeUpdate(); + session.createQuery( "delete Card" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + private List getResults(final EntityPersister entityPersister, final Callback callback) { + final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( + sessionFactory(), + LoadQueryInfluencers.NONE + ); + final LoadPlan plan = LoadPlanBuilder.buildRootEntityLoadPlan( strategy, entityPersister ); + final LoadQueryAliasResolutionContext aliasResolutionContext = + new LoadQueryAliasResolutionContextImpl( + sessionFactory(), + 0, + Collections.singletonMap( plan.getReturns().get( 0 ), new String[] { "abc" } ) + ); + final EntityLoadQueryBuilderImpl queryBuilder = new EntityLoadQueryBuilderImpl( + LoadQueryInfluencers.NONE, + plan + ); + final String sql = queryBuilder.generateSql( 1, sessionFactory(), aliasResolutionContext ); + + final ResultSetProcessorImpl resultSetProcessor = new ResultSetProcessorImpl( plan ); + final List results = new ArrayList(); + + final Session workSession = openSession(); + workSession.beginTransaction(); + workSession.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( sql ); + callback.bind( ps ); + ResultSet resultSet = ps.executeQuery(); + //callback.beforeExtractResults( workSession ); + results.addAll( + resultSetProcessor.extractResults( + NoOpLoadPlanAdvisor.INSTANCE, + resultSet, + (SessionImplementor) workSession, + callback.getQueryParameters(), + new NamedParameterContext() { + @Override + public int[] getNamedParameterLocations(String name) { + return new int[0]; + } + }, + aliasResolutionContext, + true, + false, + null, + null + ) + ); + resultSet.close(); + ps.close(); + } + } + ); + workSession.getTransaction().commit(); + workSession.close(); + + return results; + } + + + private interface Callback { + void bind(PreparedStatement ps) throws SQLException; + QueryParameters getQueryParameters (); + } + + @Entity ( name = "Parent" ) + public static class Parent { + @EmbeddedId + public ParentPK id; + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Parent ) ) return false; + + final Parent parent = (Parent) o; + + if ( !id.equals( parent.id ) ) return false; + + return true; + } + + public int hashCode() { + return id.hashCode(); + } + } + + @Embeddable + public static class ParentPK implements Serializable { + private String firstName; + private String lastName; + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof ParentPK ) ) return false; + + final ParentPK parentPk = (ParentPK) o; + + if ( !firstName.equals( parentPk.firstName ) ) return false; + if ( !lastName.equals( parentPk.lastName ) ) return false; + + return true; + } + + public int hashCode() { + int result; + result = firstName.hashCode(); + result = 29 * result + lastName.hashCode(); + return result; + } + } + + @Entity ( name = "CardField" ) + public static class CardField implements Serializable { + + @EmbeddedId + private CardFieldPK primaryKey; + + CardField(Card card, int fieldNumber) { + this.primaryKey = new CardFieldPK(card, fieldNumber); + } + + CardField() { + } + + public CardFieldPK getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(CardFieldPK primaryKey) { + this.primaryKey = primaryKey; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + CardField cardField = (CardField) o; + + if ( primaryKey != null ? !primaryKey.equals( cardField.primaryKey ) : cardField.primaryKey != null ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return primaryKey != null ? primaryKey.hashCode() : 0; + } + } + + @Embeddable + public static class CardFieldPK implements Serializable { + @ManyToOne(optional = false) + private Card card; + + private int fieldNumber; + + public CardFieldPK(Card card, int fieldNumber) { + this.card = card; + this.fieldNumber = fieldNumber; + } + + CardFieldPK() { + } + + public Card getCard() { + return card; + } + + public void setCard(Card card) { + this.card = card; + } + + public int getFieldNumber() { + return fieldNumber; + } + + public void setFieldNumber(int fieldNumber) { + this.fieldNumber = fieldNumber; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + CardFieldPK that = (CardFieldPK) o; + + if ( fieldNumber != that.fieldNumber ) { + return false; + } + if ( card != null ? !card.equals( that.card ) : that.card != null ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = card != null ? card.hashCode() : 0; + result = 31 * result + fieldNumber; + return result; + } + } + + @Entity ( name = "Card" ) + public static class Card implements Serializable { + @Id + private String id; + + public Card(String id) { + this(); + this.id = id; + } + + Card() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java index 297912b513..3f7bcab15b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/advisor/AdviceHelper.java @@ -29,11 +29,13 @@ import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.graph.spi.AttributeNodeImplementor; +import org.hibernate.jpa.internal.metamodel.Helper; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CompositeFetch; import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.FetchOwner; +import org.hibernate.type.EntityType; /** * @author Steve Ebersole @@ -54,11 +56,16 @@ public class AdviceHelper { ); } else { + EntityType entityType = (EntityType) Helper.resolveType( + (SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(), + attributeNode.getAttribute() + ); return new EntityFetch( (SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(), LockMode.NONE, fetchOwner, attributeNode.getAttributeName(), + entityType, new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT ) ); } From 4dac8052d5655168d91124eaa9a1d4bda15b2ceb Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 13 May 2013 13:28:01 -0700 Subject: [PATCH 03/17] HHH-7841 - Redesign Loader --- .../loader/plan/spi/EntityFetch.java | 3 - .../AbstractLoadPlanBuilderStrategy.java | 55 +------------------ .../spi/MetadataDrivenModelGraphVisitor.java | 34 +++++++----- 3 files changed, 22 insertions(+), 70 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 72d2b4744b..124b3f7944 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -60,9 +60,6 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit super( sessionFactory, lockMode, owner, ownerProperty, fetchStrategy ); this.associationType = entityType; - // (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( ownerProperty ); - //this.associationType = - // (EntityType) owner.retrieveFetchSourcePersister().getPropertyType( getPropertyPath().getFullPath() ); this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java index 89a5272ae5..a15dd2a877 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java @@ -85,8 +85,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private ArrayDeque fetchOwnerStack = new ArrayDeque(); private ArrayDeque collectionReferenceStack = new ArrayDeque(); - //private AbstractIdentifierAttributeCollector currentIdentifierAttributeCollector = null; - protected AbstractLoadPlanBuilderStrategy(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; } @@ -121,7 +119,6 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder MDC.remove( MDC_KEY ); fetchOwnerStack.clear(); collectionReferenceStack.clear(); -// currentIdentifierAttributeCollector = null; } @Override @@ -418,21 +415,10 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder ); } - // If we are collecting fetches for the identifier then - // currentIdentifierAttributeCollector will be non-null. - // In that case, we do not want to continue walking the association, so - // don't push associationFetch to the stack. - //final boolean continueWalk = currentIdentifierAttributeCollector == null; - //if ( continueWalk && FetchOwner.class.isInstance( associationFetch) ) { if ( FetchOwner.class.isInstance( associationFetch) ) { pushToStack( (FetchOwner) associationFetch ); } - //if ( ! continueWalk ) { - // popFromStack(); - //} - - //return continueWalk; return true; } @@ -447,21 +433,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } private void pushToStack(FetchOwner fetchOwner) { -// if ( fetchOwner instanceof AbstractIdentifierAttributeCollector ) { -// if ( currentIdentifierAttributeCollector != null ) { -// throw new WalkingException( -// String.format( -// "An AbstractIdentifierAttributeCollector is already being processed: %s", -// currentIdentifierAttributeCollector -// ) -// ); -// } -// currentIdentifierAttributeCollector = (AbstractIdentifierAttributeCollector) fetchOwner; -// log.trace( "Pushing AbstractIdentifierAttributeCollector fetch owner to stack : " + fetchOwner ); -// } -// else { - log.trace( "Pushing fetch owner to stack : " + fetchOwner ); -// } + log.trace( "Pushing fetch owner to stack : " + fetchOwner ); mdcStack().push( fetchOwner.getPropertyPath() ); fetchOwnerStack.addFirst( fetchOwner ); } @@ -472,30 +444,7 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder private FetchOwner popFromStack() { final FetchOwner last = fetchOwnerStack.removeFirst(); -// if ( last instanceof AbstractIdentifierAttributeCollector ) { -// if ( currentIdentifierAttributeCollector == null ) { -// throw new WalkingException( -// String.format( -// "Popped fetch owner was an AbstractIdentifierAttributeCollector [%s], but none in process (currentIdentifierAttributeCollector == null)", -// last -// ) -// ); -// } -// else if ( currentIdentifierAttributeCollector != last ) { -// throw new WalkingException( -// String.format( -// "Expected popped fetch owner to be [%s], but instead it was [%s])", -// currentIdentifierAttributeCollector, -// last -// ) -// ); -// } -// currentIdentifierAttributeCollector = null; -// log.trace( "Popped AbstractIdentifierAttributeCollector fetch owner from stack : " + last ); -// } -// else { - log.trace( "Popped fetch owner from stack : " + last ); -// } + log.trace( "Popped fetch owner from stack : " + last ); mdcStack().pop(); if ( FetchStackAware.class.isInstance( last ) ) { ( (FetchStackAware) last ).poppedFromStack(); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index c48461a885..3b5caa44ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -123,10 +123,10 @@ public class MetadataDrivenModelGraphVisitor { log.debug( "Visiting attribute path : " + subPath.getFullPath() ); final boolean continueWalk; - if ( attributeDefinition.getType().isAssociationType() ) { - continueWalk = - ! isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) && - strategy.startingAttribute( attributeDefinition ); + if ( attributeDefinition.getType().isAssociationType() && + isDuplicateAssociationKey( ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey() ) ) { + log.debug( "Property path deemed to be circular : " + subPath.getFullPath() ); + continueWalk = false; } else { continueWalk = strategy.startingAttribute( attributeDefinition ); @@ -152,10 +152,7 @@ public class MetadataDrivenModelGraphVisitor { private void visitAssociation(AssociationAttributeDefinition attribute) { // todo : do "too deep" checks; but see note about adding depth to PropertyPath - if ( !addAssociationKey( attribute.getAssociationKey() ) ) { - log.debug( "Property path deemed to be circular : " + currentPropertyPath.getFullPath() ); - return; - } + addAssociationKey( attribute.getAssociationKey() ); if ( attribute.isCollection() ) { visitCollectionDefinition( attribute.toCollectionDefinition() ); @@ -227,17 +224,26 @@ public class MetadataDrivenModelGraphVisitor { private final Set visitedAssociationKeys = new HashSet(); /** - * Add association key. + * Add association key to indicate the association is being visited. * @param associationKey - the association key. - * @return true, if the association key was added; - * false, otherwise (indicating the association key was already visited). + * @throws WalkingException if the association with the specified association key + * has already been visited. */ - protected boolean addAssociationKey(AssociationKey associationKey) { - return visitedAssociationKeys.add( associationKey ); + protected void addAssociationKey(AssociationKey associationKey) { + if ( ! visitedAssociationKeys.add( associationKey ) ) { + throw new WalkingException( + String.format( "Association has already been visited: %s", associationKey ) + ); + } } + /** + * Has an association with the specified key been visited already? + * @param associationKey - the association key. + * @return true, if the association with the specified association key has already been visited; + * false, otherwise. + */ protected boolean isDuplicateAssociationKey(AssociationKey associationKey) { return visitedAssociationKeys.contains( associationKey ); } - } From 3e311a1370489338f6d3f8862935d7ec2689b2bf Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Fri, 10 May 2013 23:03:09 -0400 Subject: [PATCH 04/17] HHH-8233 fix for exclude-unlisted-classes (was being ignored) --- .../org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java index da5af94f25..3d2c441370 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java @@ -68,7 +68,7 @@ public abstract class AbstractScannerImpl implements Scanner { if ( persistenceUnit.getPersistenceUnitRootUrl() != null ) { final ArchiveDescriptor descriptor = buildArchiveDescriptor( persistenceUnit.getPersistenceUnitRootUrl(), true, scanOptions ); - final ArchiveContext context = buildArchiveContext( persistenceUnit, false, resultCollector ); + final ArchiveContext context = buildArchiveContext( persistenceUnit, true, resultCollector ); descriptor.visitArchive( context ); } From fca8f3c1afe4e9d7cd4f1d0e13f634d8943a8dce Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 10 May 2013 15:02:22 -0400 Subject: [PATCH 05/17] HHH-8236 Update to hibernate-commons-annotations 4.0.2.Final --- libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries.gradle b/libraries.gradle index 2db72b6e4c..7b49cb4c88 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -43,7 +43,7 @@ ext { // Annotations commons_annotations: - 'org.hibernate.common:hibernate-commons-annotations:4.0.1.Final@jar', + 'org.hibernate.common:hibernate-commons-annotations:4.0.2.Final@jar', jandex: 'org.jboss:jandex:1.1.0.Alpha1', classmate: 'com.fasterxml:classmate:0.8.0', From 08215bad73d026b93368428132a759dff7f79c7a Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 15 May 2013 00:35:12 -0400 Subject: [PATCH 06/17] HHH-2664 full join not working --- .../java/org/hibernate/hql/internal/ast/util/JoinProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java index 5e45e92dea..7ad4a7e0f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/JoinProcessor.java @@ -94,6 +94,8 @@ public class JoinProcessor implements SqlTokenTypes { return JoinType.INNER_JOIN; case RIGHT_OUTER: return JoinType.RIGHT_OUTER_JOIN; + case FULL: + return JoinType.FULL_JOIN; default: throw new AssertionFailure( "undefined join type " + astJoinType ); } From 24f1a15d077d64fda3b6f26ecb1f712a86556891 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Thu, 16 May 2013 11:46:04 -0400 Subject: [PATCH 07/17] HHH-8238 OsgiJtaPlatform null pointer exception --- .../src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java index 5dd4888037..c79318c3a5 100644 --- a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java +++ b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiJtaPlatform.java @@ -54,13 +54,13 @@ public class OsgiJtaPlatform implements JtaPlatform { @Override public TransactionManager retrieveTransactionManager() { final ServiceReference sr = bundleContext.getServiceReference( TransactionManager.class.getName() ); - return (TransactionManager) bundleContext.getService( sr ); + return sr == null ? null : (TransactionManager) bundleContext.getService( sr ); } @Override public UserTransaction retrieveUserTransaction() { final ServiceReference sr = bundleContext.getServiceReference( UserTransaction.class.getName() ); - return (UserTransaction) bundleContext.getService( sr ); + return sr == null ? null : (UserTransaction) bundleContext.getService( sr ); } @Override From 8b9ec5cca50022994aa7472d929983f30324d14e Mon Sep 17 00:00:00 2001 From: george-007 Date: Wed, 15 May 2013 20:18:49 +0200 Subject: [PATCH 08/17] Update DefaultInitializeCollectionEventListener.java --- .../internal/DefaultInitializeCollectionEventListener.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java index 4ce8666ddf..0aec798eaa 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java @@ -73,8 +73,10 @@ public class DefaultInitializeCollectionEventListener implements InitializeColle source ); - if ( foundInCache && traceEnabled ) { - LOG.trace( "Collection initialized from cache" ); + if ( foundInCache ) { + if ( traceEnabled ) { + LOG.trace( "Collection initialized from cache" ); + } } else { if ( traceEnabled ) { From bc6f5d8a1728e83f519ebe3b3e0920f2411cab82 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 15 May 2013 15:44:17 -0400 Subject: [PATCH 09/17] HHH-6813 @Id @OneToOne cause NullPointerException during query --- .../java/org/hibernate/cfg/BinderHelper.java | 1 + .../java/org/hibernate/cfg/HbmBinder.java | 2 + .../org/hibernate/cfg/OneToOneSecondPass.java | 17 ++-- .../cfg/annotations/CollectionBinder.java | 1 + .../java/org/hibernate/mapping/ManyToOne.java | 3 +- .../java/org/hibernate/mapping/OneToMany.java | 3 +- .../java/org/hibernate/mapping/OneToOne.java | 6 +- .../java/org/hibernate/mapping/ToOne.java | 9 ++ .../java/org/hibernate/type/EntityType.java | 74 ++++++++++------ .../org/hibernate/type/ManyToOneType.java | 28 +++++-- .../java/org/hibernate/type/OneToOneType.java | 27 ++++-- .../hibernate/type/SpecialOneToOneType.java | 21 ++++- .../java/org/hibernate/type/TypeFactory.java | 84 ++++++++++++++----- .../OneToOneWithDerivedIdentityTest.java | 3 +- 14 files changed, 204 insertions(+), 75 deletions(-) mode change 100755 => 100644 hibernate-core/src/main/java/org/hibernate/type/EntityType.java mode change 100755 => 100644 hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index 03aafe41db..962c868c21 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -341,6 +341,7 @@ public class BinderHelper { */ if ( value instanceof ToOne ) { ( (ToOne) value ).setReferencedPropertyName( syntheticPropertyName ); + ( (ToOne) value ).setReferenceToPrimaryKey( syntheticPropertyName == null ); mappings.addUniquePropertyReference( ownerEntity.getEntityName(), syntheticPropertyName ); } else if ( value instanceof Collection ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java index c721a072e7..0cea5956ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java @@ -1629,6 +1629,7 @@ public final class HbmBinder { if ( ukName != null ) { manyToOne.setReferencedPropertyName( ukName.getValue() ); } + manyToOne.setReferenceToPrimaryKey( manyToOne.getReferencedPropertyName() == null ); manyToOne.setReferencedEntityName( getEntityName( node, mappings ) ); @@ -1728,6 +1729,7 @@ public final class HbmBinder { Attribute ukName = node.attribute( "property-ref" ); if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() ); + oneToOne.setReferenceToPrimaryKey( oneToOne.getReferencedPropertyName() == null ); oneToOne.setPropertyName( node.attributeValue( "name" ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java index a922034d74..f99d3d64a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java @@ -39,6 +39,7 @@ import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; import org.hibernate.mapping.SimpleValue; import org.hibernate.type.ForeignKeyDirection; @@ -212,12 +213,10 @@ public class OneToOneSecondPass implements SecondPass { else { propertyHolder.addProperty( prop, inferredData.getDeclaringClass() ); } + + value.setReferencedPropertyName( mappedBy ); // HHH-6813 - // If otherSide's id is derived, do not set EntityType#uniqueKeyPropertyName. - // EntityType#isReferenceToPrimaryKey() assumes that, if it's set, - // a PK is not referenced. Example: - // // Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar // Bar: @Id @OneToOne Foo foo boolean referencesDerivedId = false; @@ -228,8 +227,14 @@ public class OneToOneSecondPass implements SecondPass { catch ( MappingException e ) { // ignore } - String referencedPropertyName = referencesDerivedId ? null : mappedBy; - value.setReferencedPropertyName( referencedPropertyName ); + boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null; + value.setReferenceToPrimaryKey( referenceToPrimaryKey ); + + // If the other side is a derived ID, prevent an infinite + // loop of attempts to resolve identifiers. + if ( referencesDerivedId ) { + ( (ManyToOne) otherSideProperty.getValue() ).setReferenceToPrimaryKey( false ); + } String propertyRef = value.getReferencedPropertyName(); if ( propertyRef != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index b087052506..358a72893c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -1484,6 +1484,7 @@ public abstract class CollectionBinder { ( (ManyToOne) value ).setReferencedPropertyName( referencedPropertyName ); mappings.addUniquePropertyReference( referencedEntity.getEntityName(), referencedPropertyName ); } + ( (ManyToOne) value ).setReferenceToPrimaryKey( referencedPropertyName == null ); value.createForeignKey(); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java index 9736b5bf51..49cf99a548 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java @@ -45,7 +45,8 @@ public class ManyToOne extends ToOne { public Type getType() throws MappingException { return getMappings().getTypeResolver().getTypeFactory().manyToOne( - getReferencedEntityName(), + getReferencedEntityName(), + referenceToPrimaryKey, getReferencedPropertyName(), isLazy(), isUnwrapProxy(), diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java index 18b45cded1..9d15fb7381 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java @@ -47,7 +47,8 @@ public class OneToMany implements Value { private EntityType getEntityType() { return mappings.getTypeResolver().getTypeFactory().manyToOne( - getReferencedEntityName(), + getReferencedEntityName(), + true, null, false, false, diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java index ca8e196db6..596cfe4ebf 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java @@ -69,7 +69,8 @@ public class OneToOne extends ToOne { if ( getColumnIterator().hasNext() ) { return getMappings().getTypeResolver().getTypeFactory().specialOneToOne( getReferencedEntityName(), - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, referencedPropertyName, isLazy(), isUnwrapProxy(), @@ -80,7 +81,8 @@ public class OneToOne extends ToOne { else { return getMappings().getTypeResolver().getTypeFactory().oneToOne( getReferencedEntityName(), - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, referencedPropertyName, isLazy(), isUnwrapProxy(), diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java index 9a6ab734f8..c6150ac3ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java @@ -41,6 +41,7 @@ public abstract class ToOne extends SimpleValue implements Fetchable { private boolean embedded; private boolean lazy = true; protected boolean unwrapProxy; + protected boolean referenceToPrimaryKey = true; protected ToOne(Mappings mappings, Table table) { super( mappings, table ); @@ -129,5 +130,13 @@ public abstract class ToOne extends SimpleValue implements Fetchable { public void setUnwrapProxy(boolean unwrapProxy) { this.unwrapProxy = unwrapProxy; } + + public boolean isReferenceToPrimaryKey() { + return referenceToPrimaryKey; + } + + public void setReferenceToPrimaryKey(boolean referenceToPrimaryKey) { + this.referenceToPrimaryKey = referenceToPrimaryKey; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java old mode 100755 new mode 100644 index 4ee8ed7744..f6ae0946cf --- a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java @@ -23,6 +23,11 @@ */ package org.hibernate.type; +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; + import org.dom4j.Element; import org.dom4j.Node; import org.hibernate.AssertionFailure; @@ -30,7 +35,11 @@ import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.engine.internal.ForeignKeys; -import org.hibernate.engine.spi.*; +import org.hibernate.engine.spi.EntityUniqueKey; +import org.hibernate.engine.spi.Mapping; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; @@ -38,11 +47,6 @@ import org.hibernate.persister.entity.UniqueKeyLoadable; import org.hibernate.proxy.HibernateProxy; import org.hibernate.tuple.ElementWrapper; -import java.io.Serializable; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Map; - /** * Base for types which map associations to persistent entities. * @@ -56,6 +60,7 @@ public abstract class EntityType extends AbstractType implements AssociationType protected final boolean isEmbeddedInXML; private final boolean eager; private final boolean unwrapProxy; + private final boolean referenceToPrimaryKey; private transient Class returnedClass; @@ -72,7 +77,7 @@ public abstract class EntityType extends AbstractType implements AssociationType * says to return the "implementation target" of lazy prooxies; typically only possible * with lazy="no-proxy". * - * @deprecated Use {@link #EntityType(TypeFactory.TypeScope, String, String, boolean, boolean )} instead. + * @deprecated Use {@link #EntityType(org.hibernate.type.TypeFactory.TypeScope, String, boolean, String, boolean, boolean)} instead. * See Jira issue: HHH-7771 */ @Deprecated @@ -83,12 +88,7 @@ public abstract class EntityType extends AbstractType implements AssociationType boolean eager, boolean isEmbeddedInXML, boolean unwrapProxy) { - this.scope = scope; - this.associatedEntityName = entityName; - this.uniqueKeyPropertyName = uniqueKeyPropertyName; - this.isEmbeddedInXML = isEmbeddedInXML; - this.eager = eager; - this.unwrapProxy = unwrapProxy; + this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy ); } /** @@ -102,10 +102,36 @@ public abstract class EntityType extends AbstractType implements AssociationType * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping * says to return the "implementation target" of lazy prooxies; typically only possible * with lazy="no-proxy". + * + * @deprecated Use {@link #EntityType(org.hibernate.type.TypeFactory.TypeScope, String, boolean, String, boolean, boolean)} instead. + */ + @Deprecated + protected EntityType( + TypeFactory.TypeScope scope, + String entityName, + String uniqueKeyPropertyName, + boolean eager, + boolean unwrapProxy) { + this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy ); + } + + /** + * Constructs the requested entity type mapping. + * + * @param scope The type scope + * @param entityName The name of the associated entity. + * @param referenceToPrimaryKey True if association references a primary key. + * @param uniqueKeyPropertyName The property-ref name, or null if we + * reference the PK of the associated entity. + * @param eager Is eager fetching enabled. + * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping + * says to return the "implementation target" of lazy prooxies; typically only possible + * with lazy="no-proxy". */ protected EntityType( TypeFactory.TypeScope scope, String entityName, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean eager, boolean unwrapProxy) { @@ -115,6 +141,7 @@ public abstract class EntityType extends AbstractType implements AssociationType this.isEmbeddedInXML = true; this.eager = eager; this.unwrapProxy = unwrapProxy; + this.referenceToPrimaryKey = referenceToPrimaryKey; } protected TypeFactory.TypeScope scope() { @@ -169,7 +196,7 @@ public abstract class EntityType extends AbstractType implements AssociationType * @return True if this association reference the PK of the associated entity. */ public boolean isReferenceToPrimaryKey() { - return uniqueKeyPropertyName==null; + return referenceToPrimaryKey; } public String getRHSUniqueKeyPropertyName() { @@ -456,21 +483,16 @@ public abstract class EntityType extends AbstractType implements AssociationType return value; } - if ( value == null ) { - return null; - } - else { - if ( isNull( owner, session ) ) { - return null; //EARLY EXIT! - } - + if ( value != null && !isNull( owner, session ) ) { if ( isReferenceToPrimaryKey() ) { return resolveIdentifier( (Serializable) value, session ); } - else { + else if ( uniqueKeyPropertyName != null ) { return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session ); } } + + return null; } public Type getSemiResolvedType(SessionFactoryImplementor factory) { @@ -482,7 +504,7 @@ public abstract class EntityType extends AbstractType implements AssociationType return value; } - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) { return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls } else if ( value == null ) { @@ -599,7 +621,7 @@ public abstract class EntityType extends AbstractType implements AssociationType * or unique key property name. */ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException { - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) { return getIdentifierType(factory); } else { @@ -621,7 +643,7 @@ public abstract class EntityType extends AbstractType implements AssociationType */ public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory) throws MappingException { - if ( isReferenceToPrimaryKey() ) { + if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) { return factory.getIdentifierPropertyName( getAssociatedEntityName() ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java index c7cd51db99..7fb9c8fe78 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java @@ -67,13 +67,12 @@ public class ManyToOneType extends EntityType { * @param lazy Should the association be handled lazily */ public ManyToOneType(TypeFactory.TypeScope scope, String referencedEntityName, boolean lazy) { - this( scope, referencedEntityName, null, lazy, true, false, false, false ); + this( scope, referencedEntityName, true, null, lazy, true, false, false ); } /** - * @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, String, boolean, boolean, boolean, boolean ) } instead. - * See Jira issue: HHH-7771 + * @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, boolean, String, boolean, boolean, boolean, boolean ) } instead. */ @Deprecated public ManyToOneType( @@ -85,11 +84,14 @@ public class ManyToOneType extends EntityType { boolean isEmbeddedInXML, boolean ignoreNotFound, boolean isLogicalOneToOne) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy ); - this.ignoreNotFound = ignoreNotFound; - this.isLogicalOneToOne = isLogicalOneToOne; + this( scope, referencedEntityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne ); } + /** + * @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, boolean, String, boolean, boolean, boolean, boolean ) } instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public ManyToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -98,7 +100,19 @@ public class ManyToOneType extends EntityType { boolean unwrapProxy, boolean ignoreNotFound, boolean isLogicalOneToOne) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); + this( scope, referencedEntityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne ); + } + + public ManyToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + boolean ignoreNotFound, + boolean isLogicalOneToOne) { + super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy ); this.ignoreNotFound = ignoreNotFound; this.isLogicalOneToOne = isLogicalOneToOne; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java index 838955bf72..960adf18d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java @@ -48,7 +48,7 @@ public class OneToOneType extends EntityType { private final String entityName; /** - * @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, String, boolean, boolean, String, String)} + * @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} * instead. * See Jira issue: HHH-7771 */ @@ -63,12 +63,14 @@ public class OneToOneType extends EntityType { boolean isEmbeddedInXML, String entityName, String propertyName) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy ); - this.foreignKeyType = foreignKeyType; - this.propertyName = propertyName; - this.entityName = entityName; + this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); } + /** + * @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} + * instead. + */ + @Deprecated public OneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -78,7 +80,20 @@ public class OneToOneType extends EntityType { boolean unwrapProxy, String entityName, String propertyName) { - super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); + this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); + } + + public OneToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy ); this.foreignKeyType = foreignKeyType; this.propertyName = propertyName; this.entityName = entityName; diff --git a/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java old mode 100755 new mode 100644 index 9ef9852d5c..70c86b3f6a --- a/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SpecialOneToOneType.java @@ -43,6 +43,10 @@ import org.hibernate.metamodel.relational.Size; */ public class SpecialOneToOneType extends OneToOneType { + /** + * @deprecated Use {@link #SpecialOneToOneType(org.hibernate.type.TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} instead. + */ + @Deprecated public SpecialOneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -52,14 +56,27 @@ public class SpecialOneToOneType extends OneToOneType { boolean unwrapProxy, String entityName, String propertyName) { + this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); + } + + public SpecialOneToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { super( scope, referencedEntityName, - foreignKeyType, + foreignKeyType, + referenceToPrimaryKey, uniqueKeyPropertyName, lazy, unwrapProxy, - true, entityName, propertyName ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java index 131251349c..60c8427551 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java @@ -27,8 +27,6 @@ import java.io.Serializable; import java.util.Comparator; import java.util.Properties; -import org.jboss.logging.Logger; - import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.classic.Lifecycle; @@ -39,6 +37,7 @@ import org.hibernate.tuple.component.ComponentMetamodel; import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.ParameterizedType; import org.hibernate.usertype.UserType; +import org.jboss.logging.Logger; /** * Used internally to build instances of {@link Type}, specifically it builds instances of @@ -235,8 +234,7 @@ public final class TypeFactory implements Serializable { // one-to-one type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** - * @deprecated Use {@link #oneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String)} - * instead. + * @deprecated Use {@link #oneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String, boolean)} instead. * See Jira issue: HHH-7771 */ @Deprecated @@ -249,10 +247,14 @@ public final class TypeFactory implements Serializable { boolean isEmbeddedInXML, String entityName, String propertyName) { - return new OneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, isEmbeddedInXML, entityName, propertyName ); + return oneToOne( persistentClass, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, + propertyName ); } + /** + * @deprecated Use {@link #oneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String, boolean)} instead. + */ + @Deprecated public EntityType oneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -261,10 +263,27 @@ public final class TypeFactory implements Serializable { boolean unwrapProxy, String entityName, String propertyName) { - return new OneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName ); + return oneToOne( persistentClass, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, + propertyName ); } + public EntityType oneToOne( + String persistentClass, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + return new OneToOneType( typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey, + uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); + } + + /** + * @deprecated Use {@link #specialOneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String, boolean)} instead. + */ + @Deprecated public EntityType specialOneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -273,8 +292,21 @@ public final class TypeFactory implements Serializable { boolean unwrapProxy, String entityName, String propertyName) { - return new SpecialOneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, - lazy, unwrapProxy, entityName, propertyName ); + return specialOneToOne( persistentClass, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, + entityName, propertyName ); + } + + public EntityType specialOneToOne( + String persistentClass, + ForeignKeyDirection foreignKeyType, + boolean referenceToPrimaryKey, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + return new SpecialOneToOneType( typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey, + uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName ); } @@ -289,8 +321,7 @@ public final class TypeFactory implements Serializable { } /** - * @deprecated Use {@link #manyToOne(String, String, boolean, boolean, boolean, boolean)} - * instead. + * @deprecated Use {@link #manyToOne(String, boolean, String, boolean, boolean, boolean, boolean)} instead. * See Jira issue: HHH-7771 */ @Deprecated @@ -302,20 +333,28 @@ public final class TypeFactory implements Serializable { boolean isEmbeddedInXML, boolean ignoreNotFound, boolean isLogicalOneToOne) { - return new ManyToOneType( - typeScope, - persistentClass, - uniqueKeyPropertyName, - lazy, - unwrapProxy, - isEmbeddedInXML, - ignoreNotFound, - isLogicalOneToOne - ); + return manyToOne( persistentClass, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, + isLogicalOneToOne ); + } + + /** + * @deprecated Use {@link #manyToOne(String, boolean, String, boolean, boolean, boolean, boolean)} instead. + */ + @Deprecated + public EntityType manyToOne( + String persistentClass, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + boolean ignoreNotFound, + boolean isLogicalOneToOne) { + return manyToOne( persistentClass, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, + isLogicalOneToOne ); } public EntityType manyToOne( String persistentClass, + boolean referenceToPrimaryKey, String uniqueKeyPropertyName, boolean lazy, boolean unwrapProxy, @@ -324,6 +363,7 @@ public final class TypeFactory implements Serializable { return new ManyToOneType( typeScope, persistentClass, + referenceToPrimaryKey, uniqueKeyPropertyName, lazy, unwrapProxy, diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java index d530da8612..a63ca1e92e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java @@ -27,14 +27,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.hibernate.Session; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase { @Test - @FailureExpected(jiraKey = "HHH-5695") + @TestForIssue(jiraKey = "HHH-5695") public void testInsertFooAndBarWithDerivedId() { Session s = openSession(); s.beginTransaction(); From 8290662c9d0617a7056e5ab9e72dc38003a2fe79 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 16 May 2013 18:34:36 -0500 Subject: [PATCH 10/17] HHH-8144 - Create a 'release' task that performs all tasks needed for doing a release --- release/release.gradle | 199 +++++++++++++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 28 deletions(-) diff --git a/release/release.gradle b/release/release.gradle index e5cddbd8f5..c0e885cb10 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -8,15 +8,23 @@ buildDir = "target" idea.module { } +final String[] versionComponents = version.split( '\\.' ); +final String majorVersion = versionComponents[0]; +final String majorMinorVersion = versionComponents[0] + '.' + versionComponents[1]; + +final File documentationDir = mkdir( new File( project.buildDir, 'documentation' ) ); +final File versionedDocumentationDir = mkdir( new File( new File( documentationDir, "orm" ), majorMinorVersion ) ); +final File javadocDir = mkdir( new File( versionedDocumentationDir, 'javadocs' ) ); -// Javadocs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -ext.javadocBuildDir = mkdir( "${buildDir}/documentation/javadocs" ) - -def copyRightYear = new java.util.GregorianCalendar().get( java.util.Calendar.YEAR ); - +/** + * Builds the JavaDocs aggregated (unified) across all the sub-projects + */ task aggregateJavadocs(type: Javadoc) { + description = 'Builds the aggregated (unified) JavaDocs across all sub-projects' + + final int copyrightYear = new GregorianCalendar().get( Calendar.YEAR ); + // exclude any generated sources (this is not working: http://forums.gradle.org/gradle/topics/excluding_generated_source_from_javadoc) exclude "**/generated-src/**" @@ -65,13 +73,13 @@ task aggregateJavadocs(type: Javadoc) { // apply standard config description = "Build the aggregated JavaDocs for all modules" maxMemory = '512m' - destinationDir = javadocBuildDir + destinationDir = javadocDir configure( options ) { overview = rootProject.file( 'shared/javadoc/overview.html' ) stylesheetFile = rootProject.file( 'shared/javadoc/stylesheet.css' ) windowTitle = 'Hibernate JavaDocs' docTitle = "Hibernate JavaDoc ($project.version)" - bottom = "Copyright © 2001-$copyRightYear Red Hat, Inc. All Rights Reserved." + bottom = "Copyright © 2001-$copyrightYear Red Hat, Inc. All Rights Reserved." use = true links = [ 'http://download.oracle.com/javase/6/docs/api/', 'http://download.oracle.com/javaee/6/api/' ] group( 'API', apiPackages.asList() ) @@ -83,27 +91,83 @@ task aggregateJavadocs(type: Javadoc) { // work around: addStringOption( "tag", "todo:X" ) } -} -aggregateJavadocs.doLast { - copy { - from rootProject.file( 'shared/javadoc/images' ) - into new File( javadocBuildDir, "/images" ) + doLast { + copy { + from rootProject.file( 'shared/javadoc/images' ) + into new File( javadocDir, "/images" ) + } } } -// release bundles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Builds the complete documentation set in preparation for upload to the doc server. + * + * It builds the docbook manuals as well as the aggregated, cross-project JavaDocs and stages them into + * a single directory that can be directly rsync-ed to the doc server + */ +task buildDocumentation(type: Task, dependsOn: [rootProject.project( 'documentation' ).tasks.buildDocs, aggregateJavadocs]) { + description = "Builds and consolidates all documentation" -ext.releaseBuildDir = mkdir( buildDir ) -task prepareReleaseBundles( dependsOn: [parent.project( 'documentation' ).tasks.buildDocs,aggregateJavadocs] ) + doLast { + copy { + from "${rootProject.project( 'documentation' ).buildDir}/docbook/publish" + into versionedDocumentationDir + } + if ( ! version.endsWith( 'SNAPSHOT' ) ) { + final File currentSymLinkContainerDir = new File( documentationDir, 'current' ) + currentSymLinkContainerDir.mkdirs(); + ant.symlink( + action: 'delete', + link: "${currentSymLinkContainerDir.absolutePath}/orm" + ) + ant.symlink( + action: 'single', + link: "${currentSymLinkContainerDir.absolutePath}/orm", + resource: "${versionedDocumentationDir.absolutePath}" + ) + } + } +} + +/** + * Upload the documentation to the JBoss doc server + */ +task uploadDocumentation(type:Exec, dependsOn: buildDocumentation) { + description = "Uploads documentation to the JBoss doc server" + + final String url = 'filemgmt.jboss.org:/docs_htdocs/hibernate/'; + + executable 'rsync' + args '-rv', '--links', '--protocol=28', "${documentationDir.absolutePath}/", url + + doFirst { + if ( version.endsWith( "SNAPSHOT" ) ) { + logger.error( "Cannot perform upload of SNAPSHOT documentation" ); + throw new RuntimeException( "Cannot perform upload of SNAPSHOT documentation" ); + } + else { + logger.lifecycle( "Uploading documentation [{$url}]..." ) + } + } + + doLast { + logger.lifecycle( 'Done uploading documentation' ) + } +} + + +/** + * Configuration of the distribution plugin, used to build release bundle as both ZIP and TGZ + */ distributions { main { baseName = 'hibernate-release' contents { - from new File( parent.projectDir, 'lgpl.txt' ) - from new File( parent.projectDir, 'changelog.txt' ) - from new File( parent.projectDir, 'hibernate_logo.gif' ) + from rootProject.file( 'lgpl.txt' ) + from rootProject.file( 'changelog.txt' ) + from rootProject.file( 'hibernate_logo.gif' ) into('lib/required') { from parent.project( 'hibernate-core' ).configurations.provided.files { dep -> dep.name == 'jta' } @@ -145,11 +209,7 @@ distributions { } into('documentation') { - from new File( parent.project( 'documentation' ).buildDir, 'docbook/publish' ) - } - - into('documentation/javadocs') { - from javadocBuildDir + from versionedDocumentationDir } into( 'project' ) { @@ -180,12 +240,95 @@ distributions { } } -distZip.dependsOn prepareReleaseBundles -distTar.dependsOn prepareReleaseBundles +distZip.dependsOn buildDocumentation +distTar.dependsOn buildDocumentation distTar { compression = Compression.GZIP } -task buildReleaseBundles( dependsOn: [distZip,distTar] ) { - description = "Build release bundle in all formats" +/** + * "virtual" task for building both types of dist bundles + */ +task buildBundles(type: Task, dependsOn: [distZip,distTar]) { + description = "Builds all release bundles" +} + +task uploadBundles(type: Exec, dependsOn: buildBundles) { + description = "Uploads release bundles to SourceForge" + + final String url = "frs.sourceforge.net:/home/frs/project/hibernate/hibernate${majorVersion}/${version}"; + + executable 'rsync' + args '-vr', '-e ssh', "${project.buildDir}/distributions/", url + + doFirst { + if ( version.endsWith( "SNAPSHOT" ) ) { + logger.error( "Cannot perform upload of SNAPSHOT documentation" ); + throw new RuntimeException( "Cannot perform upload of SNAPSHOT bundles" ) + } + else { + logger.lifecycle( "Uploading release bundles [${url}]..." ) + } + } + + doLast { + logger.lifecycle( 'Done uploading release bundles' ) + } +} + + +// Full release related tasks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +task cleanAllSubProjects(type: Task) { + description = 'Performs clean on all sub-projects' +} + +task testAllSubProjects(type: Task) { + description = 'Performs test on all sub-projects' +} + +task publishAllSubProjects(type: Task) { + description = 'Performs publish on all sub-projects' +} + +task buildAllSubProjects(type: Task, dependsOn: [testAllSubProjects,publishAllSubProjects]) + +task uploadReleaseArtifacts(type: Task, dependsOn: [uploadDocumentation, uploadBundles]) + +task announce(type: Task) { doFirst { println 'Hear ye, hear ye...' } } + +task release(type: Task, dependsOn: [cleanAllSubProjects, buildAllSubProjects, uploadReleaseArtifacts, announce]) { + description = "Coordinates all release tasks" +} + + +// must-run-afters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +buildAllSubProjects.mustRunAfter cleanAllSubProjects + +publishAllSubProjects.mustRunAfter testAllSubProjects +publishAllSubProjects.mustRunAfter cleanAllSubProjects + +uploadReleaseArtifacts.mustRunAfter buildAllSubProjects + +announce.mustRunAfter uploadReleaseArtifacts + + +// sub-project task dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rootProject.subprojects { Project subproject -> + final Task subprojectCleanTask = subproject.tasks.findByPath( 'clean' ); + if ( subprojectCleanTask != null ) { + cleanAllSubProjects.dependsOn subprojectCleanTask + } + + final Task subprojectTestTask = subproject.tasks.findByPath( 'test' ); + if ( subprojectTestTask != null ) { + testAllSubProjects.dependsOn subprojectTestTask + } + + final Task subprojectPublishTask = subproject.tasks.findByPath( 'publish' ); + if ( subprojectPublishTask != null ) { + publishAllSubProjects.dependsOn subprojectPublishTask + } } From 7bd9ba2adfa3268eb4bd8fb326eaa71c2d2de658 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 16 May 2013 18:51:53 -0500 Subject: [PATCH 11/17] HHH-8144 - Create a 'release' task that performs all tasks needed for doing a release --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 0774db1ace..d208a294e0 100644 --- a/build.gradle +++ b/build.gradle @@ -479,3 +479,5 @@ subprojects { subProject -> } } + +task release(type: Task, dependsOn: 'release:release') From 043d618c030a02b36a62e02c767f1fdee7d6c93b Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 16 May 2013 20:30:00 -0700 Subject: [PATCH 12/17] HHH-7841 - Redesign Loader HHH-7841 - Redesign Loader HHH-7841 - Redesign Loader HHH-7841 - Redesign Loader --- .../AbstractJoinableAssociationImpl.java | 3 +- .../CollectionJoinableAssociationImpl.java | 1 - .../EntityJoinableAssociationImpl.java | 2 - .../internal/EntityLoadQueryBuilderImpl.java | 4 - .../LoadQueryAliasResolutionContextImpl.java | 26 +-- .../loader/plan/spi/AbstractFetchOwner.java | 72 +++++- .../spi/AbstractSingularAttributeFetch.java | 14 +- .../loader/plan/spi/CollectionFetch.java | 12 + .../plan/spi/CompositeElementGraph.java | 81 ++----- .../loader/plan/spi/CompositeFetch.java | 18 +- .../plan/spi/CompositeFetchOwnerDelegate.java | 98 ++++++++ .../loader/plan/spi/CompositeIndexGraph.java | 84 ++----- .../loader/plan/spi/EntityElementGraph.java | 88 ++------ .../loader/plan/spi/EntityFetch.java | 58 ++--- .../plan/spi/EntityFetchOwnerDelegate.java | 72 ++++++ .../loader/plan/spi/EntityIndexGraph.java | 81 +------ .../loader/plan/spi/EntityReturn.java | 56 ++--- .../org/hibernate/loader/plan/spi/Fetch.java | 4 + .../hibernate/loader/plan/spi/FetchOwner.java | 7 + .../loader/plan/spi/FetchOwnerDelegate.java | 38 ++++ .../plan/spi/FetchableCollectionElement.java | 4 + .../AbstractLoadPlanBuilderStrategy.java | 133 ++++++++++- .../AbstractCollectionPersister.java | 41 +++- .../entity/AbstractEntityPersister.java | 7 - ...ompositionSingularSubAttributesHelper.java | 212 ++++++++++++++++++ .../EntityIdentifierDefinitionHelper.java | 184 +-------------- .../spi/AssociationVisitationStrategy.java | 3 + .../spi/CollectionElementDefinition.java | 2 +- .../spi/CompositionElementDefinition.java | 31 +++ .../spi/MetadataDrivenModelGraphVisitor.java | 18 +- .../AbstractCompositeBasedAttribute.java | 6 +- ...java => AbstractCompositionAttribute.java} | 44 ++-- .../CompositeBasedAssociationAttribute.java | 2 +- .../CompositionBasedCompositionAttribute.java | 2 +- .../EntityBasedCompositionAttribute.java | 4 +- ...positeAttributeResultSetProcessorTest.java | 20 +- .../persister/walking/BasicWalkingTest.java | 23 ++ 37 files changed, 947 insertions(+), 608 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java rename hibernate-core/src/main/java/org/hibernate/tuple/component/{AbstractCompositionDefinition.java => AbstractCompositionAttribute.java} (79%) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java index 11bf50aaf5..acc41f7839 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/AbstractJoinableAssociationImpl.java @@ -56,12 +56,11 @@ public abstract class AbstractJoinableAssociationImpl implements JoinableAssocia EntityReference currentEntityReference, CollectionReference currentCollectionReference, String withClause, - boolean isNullable, boolean hasRestriction, Map enabledFilters) throws MappingException { this.propertyPath = currentFetch.getPropertyPath(); if ( currentFetch.getFetchStrategy().getStyle() == FetchStyle.JOIN ) { - joinType = isNullable ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; + joinType = currentFetch.isNullable() ? JoinType.LEFT_OUTER_JOIN : JoinType.INNER_JOIN; } else { joinType = JoinType.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java index df49038b35..254c1a9c33 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/CollectionJoinableAssociationImpl.java @@ -56,7 +56,6 @@ public class CollectionJoinableAssociationImpl extends AbstractJoinableAssociati currentEntityReference, collectionFetch, withClause, - true, hasRestriction, enabledFilters ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java index d52323bc09..34d22b56ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityJoinableAssociationImpl.java @@ -48,7 +48,6 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm EntityFetch entityFetch, CollectionReference currentCollectionReference, String withClause, - boolean isNullable, boolean hasRestriction, Map enabledFilters) throws MappingException { super( @@ -56,7 +55,6 @@ public class EntityJoinableAssociationImpl extends AbstractJoinableAssociationIm entityFetch, currentCollectionReference, withClause, - isNullable, hasRestriction, enabledFilters ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java index 769f419583..aef90267f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/EntityLoadQueryBuilderImpl.java @@ -149,14 +149,10 @@ public class EntityLoadQueryBuilderImpl implements LoadQueryBuilder { @Override public void startingEntityFetch(EntityFetch entityFetch) { - final OuterJoinLoadable ownerPersister = (OuterJoinLoadable) entityFetch.getOwner().retrieveFetchSourcePersister(); - final int propertyNumber = ownerPersister.getEntityMetamodel().getPropertyIndex( entityFetch.getOwnerPropertyName() ); - final boolean isNullable = ownerPersister.isSubclassPropertyNullable( propertyNumber ); EntityJoinableAssociationImpl assoc = new EntityJoinableAssociationImpl( entityFetch, getCurrentCollectionReference(), "", // getWithClause( entityFetch.getPropertyPath() ) - isNullable, false, // hasRestriction( entityFetch.getPropertyPath() ) loadQueryInfluencers.getEnabledFilters() ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java index d829669a29..193a7e42f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/LoadQueryAliasResolutionContextImpl.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.Map; import org.hibernate.cfg.NotYetImplementedException; -import org.hibernate.engine.internal.JoinHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.loader.CollectionAliases; @@ -36,6 +35,8 @@ import org.hibernate.loader.EntityAliases; import org.hibernate.loader.GeneratedCollectionAliases; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.CompositeElementGraph; +import org.hibernate.loader.plan.spi.CompositeIndexGraph; import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.Fetch; @@ -46,7 +47,6 @@ import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; -import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.type.EntityType; /** @@ -214,9 +214,18 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu if ( EntityReference.class.isInstance( currentFetch.getOwner() ) ) { lhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch.getOwner() ); } - else { - throw new NotYetImplementedException( "Cannot determine LHS alias for a FetchOwner that is not an EntityReference yet." ); + else if ( CompositeElementGraph.class.isInstance( currentFetch.getOwner() ) ) { + CompositeElementGraph compositeElementGraph = (CompositeElementGraph) currentFetch.getOwner(); + lhsAlias = resolveCollectionTableAlias( compositeElementGraph.getCollectionReference() ); } + else if ( CompositeIndexGraph.class.isInstance( currentFetch.getOwner() ) ) { + CompositeIndexGraph compositeIndexGraph = (CompositeIndexGraph) currentFetch.getOwner(); + lhsAlias = resolveCollectionTableAlias( compositeIndexGraph.getCollectionReference() ); + } + else { + throw new NotYetImplementedException( "Cannot determine LHS alias for FetchOwner." ); + } + final String[] aliasedLhsColumnNames = StringHelper.qualify( lhsAlias, currentFetch.getColumnNames() ); final String rhsAlias; if ( EntityReference.class.isInstance( currentFetch ) ) { rhsAlias = resolveEntityTableAlias( (EntityReference) currentFetch ); @@ -229,15 +238,6 @@ public class LoadQueryAliasResolutionContextImpl implements LoadQueryAliasResolu } // TODO: can't this be found in CollectionAliases or EntityAliases? should be moved to LoadQueryAliasResolutionContextImpl - final OuterJoinLoadable fetchSourcePersister = (OuterJoinLoadable) currentFetch.getOwner().retrieveFetchSourcePersister(); - final int propertyNumber = fetchSourcePersister.getEntityMetamodel().getPropertyIndex( currentFetch.getOwnerPropertyName() ); - final String[] aliasedLhsColumnNames = JoinHelper.getAliasedLHSColumnNames( - joinableAssociation.getAssociationType(), - lhsAlias, - propertyNumber, - fetchSourcePersister, - sessionFactory - ); aliases = new JoinableAssociationAliasesImpl( lhsAlias, aliasedLhsColumnNames, rhsAlias ); aliasesByJoinableAssociation.put( joinableAssociation, aliases ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java index ffb5656f12..2e6293c1b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractFetchOwner.java @@ -27,20 +27,24 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.hibernate.LockMode; +import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; +import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.Type; /** * @author Steve Ebersole + * @author Gail Badner */ public abstract class AbstractFetchOwner extends AbstractPlanNode implements FetchOwner { - private final LockMode lockMode; private List fetches; - public AbstractFetchOwner(SessionFactoryImplementor factory, LockMode lockMode) { + public AbstractFetchOwner(SessionFactoryImplementor factory) { super( factory ); - this.lockMode = lockMode; validate(); } @@ -50,11 +54,10 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet /** * A "copy" constructor. Used while making clones/copies of this. * - * @param original + * @param original - the original object to copy. */ protected AbstractFetchOwner(AbstractFetchOwner original, CopyContext copyContext) { super( original ); - this.lockMode = original.lockMode; validate(); copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); @@ -62,6 +65,7 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet this.fetches = Collections.emptyList(); } else { + // TODO: don't think this is correct... List fetchesCopy = new ArrayList(); for ( Fetch fetch : fetches ) { fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); @@ -71,11 +75,6 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); } - public LockMode getLockMode() { - return lockMode; - } - - @Override public void addFetch(Fetch fetch) { if ( fetch.getOwner() != this ) { throw new IllegalArgumentException( "Fetch and owner did not match" ); @@ -92,4 +91,55 @@ public abstract class AbstractFetchOwner extends AbstractPlanNode implements Fet public Fetch[] getFetches() { return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); } + + protected abstract FetchOwnerDelegate getFetchOwnerDelegate(); + + @Override + public boolean isNullable(Fetch fetch) { + return getFetchOwnerDelegate().isNullable( fetch ); + } + + @Override + public Type getType(Fetch fetch) { + return getFetchOwnerDelegate().getType( fetch ); + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return getFetchOwnerDelegate().getColumnNames( fetch ); + } + + @Override + public CollectionFetch buildCollectionFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCollectionFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public EntityFetch buildEntityFetch( + AssociationAttributeDefinition attributeDefinition, + FetchStrategy fetchStrategy, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardEntityFetch( + this, + attributeDefinition, + fetchStrategy, + loadPlanBuildingContext + ); + } + + @Override + public CompositeFetch buildCompositeFetch( + CompositionDefinition attributeDefinition, + LoadPlanBuildingContext loadPlanBuildingContext) { + return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java index ddb4b4491f..0b3689844d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/AbstractSingularAttributeFetch.java @@ -24,7 +24,6 @@ package org.hibernate.loader.plan.spi; import org.hibernate.HibernateException; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -42,11 +41,10 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner public AbstractSingularAttributeFetch( SessionFactoryImplementor factory, - LockMode lockMode, FetchOwner owner, String ownerProperty, FetchStrategy fetchStrategy) { - super( factory, lockMode ); + super( factory ); this.owner = owner; this.ownerProperty = ownerProperty; this.fetchStrategy = fetchStrategy; @@ -77,6 +75,16 @@ public abstract class AbstractSingularAttributeFetch extends AbstractFetchOwner return ownerProperty; } + @Override + public boolean isNullable() { + return owner.isNullable( this ); + } + + @Override + public String[] getColumnNames() { + return owner.getColumnNames( this ); + } + @Override public FetchStrategy getFetchStrategy() { return fetchStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java index fef9cd1b65..242daf427c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CollectionFetch.java @@ -28,8 +28,10 @@ import java.sql.SQLException; import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.internal.JoinHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.spi.ResultSetProcessingContext; +import org.hibernate.persister.entity.Joinable; import org.hibernate.type.CollectionType; /** @@ -74,6 +76,16 @@ public class CollectionFetch extends AbstractCollectionReference implements Fetc return getPropertyPath().getProperty(); } + @Override + public boolean isNullable() { + return true; + } + + @Override + public String[] getColumnNames() { + return getOwner().getColumnNames( this ); + } + @Override public FetchStrategy getFetchStrategy() { return fetchStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java index b1ddd79d30..e2d23540df 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeElementGraph.java @@ -1,29 +1,24 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.HibernateException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class CompositeElementGraph extends AbstractPlanNode implements FetchableCollectionElement { +public class CompositeElementGraph extends AbstractFetchOwner implements FetchableCollectionElement { private final CollectionReference collectionReference; private final PropertyPath propertyPath; private final CollectionPersister collectionPersister; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; public CompositeElementGraph( SessionFactoryImplementor sessionFactory, @@ -34,39 +29,24 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable this.collectionReference = collectionReference; this.collectionPersister = collectionReference.getCollectionPersister(); this.propertyPath = collectionPath.append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) collectionPersister.getElementType(), + ( (QueryableCollection) collectionPersister ).getElementColumnNames() + ); } public CompositeElementGraph(CompositeElementGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + public CollectionReference getCollectionReference() { + return collectionReference; } @Override @@ -83,6 +63,16 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable return propertyPath; } + @Override + public CompositeElementGraph makeCopy(CopyContext copyContext) { + return new CompositeElementGraph( this, copyContext ); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } + @Override public CollectionFetch buildCollectionFetch( AssociationAttributeDefinition attributeDefinition, @@ -90,29 +80,4 @@ public class CompositeElementGraph extends AbstractPlanNode implements Fetchable LoadPlanBuildingContext loadPlanBuildingContext) { throw new HibernateException( "Collection composite element cannot define collections" ); } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - - @Override - public CompositeElementGraph makeCopy(CopyContext copyContext) { - return new CompositeElementGraph( this, copyContext ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java index b002c7b567..c1c043308c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetch.java @@ -25,8 +25,6 @@ package org.hibernate.loader.plan.spi; import java.sql.ResultSet; import java.sql.SQLException; - -import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; @@ -37,6 +35,7 @@ import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole @@ -44,15 +43,28 @@ import org.hibernate.persister.walking.spi.CompositionDefinition; public class CompositeFetch extends AbstractSingularAttributeFetch { public static final FetchStrategy FETCH_PLAN = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + private final FetchOwnerDelegate delegate; + public CompositeFetch( SessionFactoryImplementor sessionFactory, FetchOwner owner, String ownerProperty) { - super( sessionFactory, LockMode.NONE, owner, ownerProperty, FETCH_PLAN ); + super( sessionFactory, owner, ownerProperty, FETCH_PLAN ); + this.delegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) getOwner().getType( this ), + getOwner().getColumnNames( this ) + ); } public CompositeFetch(CompositeFetch original, CopyContext copyContext, FetchOwner fetchOwnerCopy) { super( original, copyContext, fetchOwnerCopy ); + this.delegate = original.getFetchOwnerDelegate(); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return delegate; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java new file mode 100644 index 0000000000..8c530eb5d2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeFetchOwnerDelegate.java @@ -0,0 +1,98 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader.plan.spi; + +import java.util.Arrays; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class CompositeFetchOwnerDelegate implements FetchOwnerDelegate { + private final SessionFactoryImplementor sessionFactory; + private final CompositeType compositeType; + private final String[] columnNames; + + public CompositeFetchOwnerDelegate( + SessionFactoryImplementor sessionFactory, + CompositeType compositeType, + String[] columnNames) { + this.sessionFactory = sessionFactory; + this.compositeType = compositeType; + this.columnNames = columnNames; + } + + @Override + public boolean isNullable(Fetch fetch) { + return compositeType.getPropertyNullability()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public Type getType(Fetch fetch) { + return compositeType.getSubtypes()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public String[] getColumnNames(Fetch fetch) { + // TODO: probably want to cache this + int begin = 0; + String[] subColumnNames = null; + for ( int i = 0; i < compositeType.getSubtypes().length; i++ ) { + final int columnSpan = compositeType.getSubtypes()[i].getColumnSpan( sessionFactory ); + subColumnNames = ArrayHelper.slice( columnNames, begin, columnSpan ); + if ( compositeType.getPropertyNames()[ i ].equals( fetch.getOwnerPropertyName() ) ) { + break; + } + begin += columnSpan; + } + return subColumnNames; + } + + private int determinePropertyIndex(Fetch fetch) { + // TODO: probably want to cache this + final String[] subAttributeNames = compositeType.getPropertyNames(); + int subAttributeIndex = -1; + for ( int i = 0; i < subAttributeNames.length ; i++ ) { + if ( subAttributeNames[ i ].equals( fetch.getOwnerPropertyName() ) ) { + subAttributeIndex = i; + break; + } + } + if ( subAttributeIndex == -1 ) { + throw new WalkingException( + String.format( + "Owner property [%s] not found in composite properties [%s]", + fetch.getOwnerPropertyName(), + Arrays.asList( subAttributeNames ) + ) + ); + } + return subAttributeIndex; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java index bccc7b1cd7..f4f4eb7fe4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/CompositeIndexGraph.java @@ -1,29 +1,24 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.HibernateException; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.CompositeType; /** * @author Steve Ebersole */ -public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCollectionIndex { +public class CompositeIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex { private final CollectionReference collectionReference; private final PropertyPath propertyPath; private final CollectionPersister collectionPersister; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; public CompositeIndexGraph( SessionFactoryImplementor sessionFactory, @@ -33,39 +28,19 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo this.collectionReference = collectionReference; this.collectionPersister = collectionReference.getCollectionPersister(); this.propertyPath = propertyPath.append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + sessionFactory, + (CompositeType) collectionPersister.getIndexType(), + ( (QueryableCollection) collectionPersister ).getIndexColumnNames() + ); } protected CompositeIndexGraph(CompositeIndexGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); - } - - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -77,12 +52,25 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo return collectionPersister.getOwnerEntityPersister(); } + public CollectionReference getCollectionReference() { + return collectionReference; + } + @Override public PropertyPath getPropertyPath() { return propertyPath; } @Override + public CompositeIndexGraph makeCopy(CopyContext copyContext) { + return new CompositeIndexGraph( this, copyContext ); + } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } + public CollectionFetch buildCollectionFetch( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy, @@ -90,28 +78,4 @@ public class CompositeIndexGraph extends AbstractPlanNode implements FetchableCo throw new HibernateException( "Composite index cannot define collections" ); } - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - - @Override - public CompositeIndexGraph makeCopy(CopyContext copyContext) { - return new CompositeIndexGraph( this, copyContext ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java index d779a119ef..a3ccde17d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityElementGraph.java @@ -1,32 +1,23 @@ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class EntityElementGraph extends AbstractPlanNode implements FetchableCollectionElement, EntityReference { +public class EntityElementGraph extends AbstractFetchOwner implements FetchableCollectionElement, EntityReference { private final CollectionReference collectionReference; private final CollectionPersister collectionPersister; private final AssociationType elementType; private final EntityPersister elementPersister; private final PropertyPath propertyPath; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -40,30 +31,19 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol this.collectionPersister = collectionReference.getCollectionPersister(); this.elementType = (AssociationType) collectionPersister.getElementType(); this.elementPersister = (EntityPersister) this.elementType.getAssociatedJoinable( sessionFactory() ); - this.propertyPath = collectionPath.append( "" ); + this.propertyPath = collectionPath; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( elementPersister ); } public EntityElementGraph(EntityElementGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionReference.getCollectionPersister(); this.elementType = original.elementType; this.elementPersister = original.elementPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -86,19 +66,6 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return identifierDescription; } - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); - } - @Override public void validateFetchPlan(FetchStrategy fetchStrategy) { } @@ -113,39 +80,6 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -156,8 +90,18 @@ public class EntityElementGraph extends AbstractPlanNode implements FetchableCol return new EntityElementGraph( this, copyContext ); } + @Override + public CollectionReference getCollectionReference() { + return collectionReference; + } + @Override public String toString() { return "EntityElementGraph(collection=" + collectionPersister.getRole() + ", type=" + elementPersister.getEntityName() + ")"; } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java index 124b3f7944..9842166561 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetch.java @@ -32,21 +32,19 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.EntityType; /** * @author Steve Ebersole */ -public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference { +public class EntityFetch extends AbstractSingularAttributeFetch implements EntityReference, Fetch { private final EntityType associationType; private final EntityPersister persister; + private final LockMode lockMode; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -57,10 +55,12 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit String ownerProperty, EntityType entityType, FetchStrategy fetchStrategy) { - super( sessionFactory, lockMode, owner, ownerProperty, fetchStrategy ); + super( sessionFactory, owner, ownerProperty, fetchStrategy ); this.associationType = entityType; this.persister = sessionFactory.getEntityPersister( associationType.getAssociatedEntityName() ); + this.lockMode = lockMode; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister ); } /** @@ -73,6 +73,8 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit super( original, copyContext, fetchOwnerCopy ); this.associationType = original.associationType; this.persister = original.persister; + this.lockMode = original.lockMode; + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } public EntityType getAssociationType() { @@ -94,45 +96,16 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit return identifierDescription; } + @Override + public LockMode getLockMode() { + return lockMode; + } + @Override public EntityPersister retrieveFetchSourcePersister() { return persister; } - - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -268,4 +241,9 @@ public class EntityFetch extends AbstractSingularAttributeFetch implements Entit copyContext.getReturnGraphVisitationStrategy().finishingEntityFetch( this ); return copy; } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java new file mode 100644 index 0000000000..7ef801df07 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityFetchOwnerDelegate.java @@ -0,0 +1,72 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader.plan.spi; + +import org.hibernate.engine.internal.JoinHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.type.AssociationType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class EntityFetchOwnerDelegate implements FetchOwnerDelegate { + private final EntityPersister entityPersister; + + public EntityFetchOwnerDelegate(EntityPersister entityPersister) { + this.entityPersister = entityPersister; + } + + @Override + public boolean isNullable(Fetch fetch) { + return entityPersister.getPropertyNullability()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public Type getType(Fetch fetch) { + return entityPersister.getPropertyTypes()[ determinePropertyIndex( fetch ) ]; + } + + @Override + public String[] getColumnNames(Fetch fetch) { + final OuterJoinLoadable outerJoinLoadable = (OuterJoinLoadable) entityPersister; + Type fetchType = getType( fetch ); + if ( fetchType.isAssociationType() ) { + return JoinHelper.getLHSColumnNames( + (AssociationType) fetchType, + determinePropertyIndex( fetch ), + outerJoinLoadable, + outerJoinLoadable.getFactory() + ); + } + else { + return outerJoinLoadable.getPropertyColumnNames( determinePropertyIndex( fetch ) ); + } + } + + private int determinePropertyIndex(Fetch fetch) { + return entityPersister.getEntityMetamodel().getPropertyIndex( fetch.getOwnerPropertyName() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java index ed12d31a99..384b8e98d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityIndexGraph.java @@ -23,33 +23,24 @@ */ package org.hibernate.loader.plan.spi; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.type.AssociationType; /** * @author Steve Ebersole */ -public class EntityIndexGraph extends AbstractPlanNode implements FetchableCollectionIndex, EntityReference { +public class EntityIndexGraph extends AbstractFetchOwner implements FetchableCollectionIndex, EntityReference { private final CollectionReference collectionReference; private final CollectionPersister collectionPersister; private final AssociationType indexType; private final EntityPersister indexPersister; private final PropertyPath propertyPath; - - private List fetches; + private final FetchOwnerDelegate fetchOwnerDelegate; private IdentifierDescription identifierDescription; @@ -63,28 +54,17 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle this.indexType = (AssociationType) collectionPersister.getIndexType(); this.indexPersister = (EntityPersister) this.indexType.getAssociatedJoinable( sessionFactory() ); this.propertyPath = collectionPath.append( "" ); // todo : do we want the part? + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( indexPersister ); } public EntityIndexGraph(EntityIndexGraph original, CopyContext copyContext) { - super( original ); + super( original, copyContext ); this.collectionReference = original.collectionReference; this.collectionPersister = original.collectionReference.getCollectionPersister(); this.indexType = original.indexType; this.indexPersister = original.indexPersister; this.propertyPath = original.propertyPath; - - copyContext.getReturnGraphVisitationStrategy().startingFetches( original ); - if ( fetches == null || fetches.size() == 0 ) { - this.fetches = Collections.emptyList(); - } - else { - List fetchesCopy = new ArrayList(); - for ( Fetch fetch : fetches ) { - fetchesCopy.add( fetch.makeCopy( copyContext, this ) ); - } - this.fetches = fetchesCopy; - } - copyContext.getReturnGraphVisitationStrategy().finishingFetches( original ); + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } @Override @@ -107,19 +87,6 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle return identifierDescription; } - @Override - public void addFetch(Fetch fetch) { - if ( fetches == null ) { - fetches = new ArrayList(); - } - fetches.add( fetch ); - } - - @Override - public Fetch[] getFetches() { - return fetches == null ? NO_FETCHES : fetches.toArray( new Fetch[ fetches.size() ] ); - } - @Override public void validateFetchPlan(FetchStrategy fetchStrategy) { } @@ -134,39 +101,6 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void injectIdentifierDescription(IdentifierDescription identifierDescription) { this.identifierDescription = identifierDescription; @@ -176,4 +110,9 @@ public class EntityIndexGraph extends AbstractPlanNode implements FetchableColle public EntityIndexGraph makeCopy(CopyContext copyContext) { return new EntityIndexGraph( this, copyContext ); } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java index cb15874c8b..7680e4a30f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/EntityReturn.java @@ -32,12 +32,8 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; -import org.hibernate.loader.plan.internal.LoadPlanBuildingHelper; -import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.CompositionDefinition; import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; @@ -50,24 +46,32 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe private final PropertyPath propertyPath = new PropertyPath(); // its a root + private final LockMode lockMode; + + private final FetchOwnerDelegate fetchOwnerDelegate; + private IdentifierDescription identifierDescription; public EntityReturn( SessionFactoryImplementor sessionFactory, LockMode lockMode, String entityName) { - super( sessionFactory, lockMode ); - + super( sessionFactory ); this.persister = sessionFactory.getEntityPersister( entityName ); + this.lockMode = lockMode; + this.fetchOwnerDelegate = new EntityFetchOwnerDelegate( persister ); } protected EntityReturn(EntityReturn original, CopyContext copyContext) { super( original, copyContext ); this.persister = original.persister; + this.lockMode = original.lockMode; + this.fetchOwnerDelegate = original.fetchOwnerDelegate; } + @Override public LockMode getLockMode() { - return super.getLockMode(); + return lockMode; } @Override @@ -99,39 +103,6 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe return propertyPath; } - @Override - public CollectionFetch buildCollectionFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCollectionFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public EntityFetch buildEntityFetch( - AssociationAttributeDefinition attributeDefinition, - FetchStrategy fetchStrategy, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardEntityFetch( - this, - attributeDefinition, - fetchStrategy, - loadPlanBuildingContext - ); - } - - @Override - public CompositeFetch buildCompositeFetch( - CompositionDefinition attributeDefinition, - LoadPlanBuildingContext loadPlanBuildingContext) { - return LoadPlanBuildingHelper.buildStandardCompositeFetch( this, attributeDefinition, loadPlanBuildingContext ); - } - @Override public void hydrate(ResultSet resultSet, ResultSetProcessingContext context) throws SQLException { EntityKey entityKey = getEntityKeyFromContext( context ); @@ -207,4 +178,9 @@ public class EntityReturn extends AbstractFetchOwner implements Return, EntityRe public EntityReturn makeCopy(CopyContext copyContext) { return new EntityReturn( this, copyContext ); } + + @Override + protected FetchOwnerDelegate getFetchOwnerDelegate() { + return fetchOwnerDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java index ab344a17f2..9d34ff51ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/Fetch.java @@ -52,6 +52,10 @@ public interface Fetch extends CopyableFetch { */ public String getOwnerPropertyName(); + public boolean isNullable(); + + public String[] getColumnNames(); + public FetchStrategy getFetchStrategy(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java index 102180bdb1..9558e987d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwner.java @@ -29,6 +29,7 @@ import org.hibernate.loader.plan.spi.build.LoadPlanBuildingContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.type.Type; /** * Contract for owners of fetches. Any non-scalar return could be a fetch owner. @@ -56,6 +57,12 @@ public interface FetchOwner { */ public Fetch[] getFetches(); + public Type getType(Fetch fetch); + + public boolean isNullable(Fetch fetch); + + public String[] getColumnNames(Fetch fetch); + /** * Is the asserted plan valid from this owner to a fetch? * diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java new file mode 100644 index 0000000000..523891b3ef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchOwnerDelegate.java @@ -0,0 +1,38 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.loader.plan.spi; + +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public interface FetchOwnerDelegate { + + public boolean isNullable(Fetch fetch); + + public Type getType(Fetch fetch); + + public String[] getColumnNames(Fetch fetch); +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java index 5b18a01029..c4f090f9e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/FetchableCollectionElement.java @@ -23,10 +23,14 @@ */ package org.hibernate.loader.plan.spi; +import org.hibernate.persister.collection.CollectionPersister; + /** * @author Steve Ebersole */ public interface FetchableCollectionElement extends FetchOwner, CopyableReturn { @Override public FetchableCollectionElement makeCopy(CopyContext copyContext); + + public CollectionReference getCollectionReference(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java index a15dd2a877..d7c12c762d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/spi/build/AbstractLoadPlanBuilderStrategy.java @@ -49,16 +49,20 @@ import org.hibernate.loader.plan.spi.AbstractSingularAttributeFetch; import org.hibernate.loader.plan.spi.CollectionFetch; import org.hibernate.loader.plan.spi.CollectionReference; import org.hibernate.loader.plan.spi.CollectionReturn; +import org.hibernate.loader.plan.spi.CompositeElementGraph; import org.hibernate.loader.plan.spi.CompositeFetch; +import org.hibernate.loader.plan.spi.CompositeFetchOwnerDelegate; import org.hibernate.loader.plan.spi.EntityFetch; import org.hibernate.loader.plan.spi.EntityReference; import org.hibernate.loader.plan.spi.EntityReturn; import org.hibernate.loader.plan.spi.Fetch; import org.hibernate.loader.plan.spi.FetchOwner; +import org.hibernate.loader.plan.spi.FetchOwnerDelegate; import org.hibernate.loader.plan.spi.IdentifierDescription; import org.hibernate.loader.plan.spi.Return; import org.hibernate.loader.spi.ResultSetProcessingContext; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; import org.hibernate.persister.walking.spi.AttributeDefinition; @@ -66,9 +70,11 @@ import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.CompositeType; import org.hibernate.type.Type; import static org.hibernate.loader.spi.ResultSetProcessingContext.IdentifierResolutionContext; @@ -192,7 +198,12 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder final FetchOwner identifierAttributeCollector; if ( entityIdentifierDefinition.isEncapsulated() ) { - identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); + if ( entityIdentifierDefinition.getEntityDefinition().getEntityPersister().getIdentifierType().isComponentType() ) { + identifierAttributeCollector = new EncapsulatedCompositeIdentifierAttributeCollector( entityReference ); + } + else { + identifierAttributeCollector = new EncapsulatedIdentifierAttributeCollector( entityReference ); + } } else { identifierAttributeCollector = new NonEncapsulatedIdentifierAttributeCollector( entityReference ); @@ -307,6 +318,35 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder // - the element graph pushed while starting would be popped in finishing/Entity/finishingComposite } + @Override + public void startingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + System.out.println( + String.format( + "%s Starting composite collection element for (%s)", + StringHelper.repeat( ">>", fetchOwnerStack.size() ), + compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole() + ) + ); + } + + @Override + public void finishingCompositeElement(CompositionElementDefinition compositeElementDefinition) { + // pop the current fetch owner, and make sure what we just popped represents this composition + final FetchOwner poppedFetchOwner = popFromStack(); + + if ( ! CompositeElementGraph.class.isInstance( poppedFetchOwner ) ) { + throw new WalkingException( "Mismatched FetchOwner from stack on pop" ); + } + + // NOTE : not much else we can really check here atm since on the walking spi side we do not have path + + log.tracef( + "%s Finished composite element for : %s", + StringHelper.repeat( "<<", fetchOwnerStack.size() ), + compositeElementDefinition.getCollectionDefinition().getCollectionPersister().getRole() + ); + } + @Override public void finishingCollection(CollectionDefinition collectionDefinition) { // pop the current fetch owner, and make sure what we just popped represents this collection @@ -513,6 +553,11 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder return entityReference.getEntityPersister(); } + @Override + public boolean isNullable(Fetch fetch) { + return false; + } + @Override public IdentifierDescription getIdentifierDescription() { return entityReference.getIdentifierDescription(); @@ -596,6 +641,13 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder } } + protected static abstract class AbstractCompositeIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + + public AbstractCompositeIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + } + } + protected static class EncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { private final PropertyPath propertyPath; @@ -613,17 +665,76 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder ); } + @Override + public Type getType(Fetch fetch) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + } + @Override public PropertyPath getPropertyPath() { return propertyPath; } } - protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractIdentifierAttributeCollector { + protected static class EncapsulatedCompositeIdentifierAttributeCollector extends AbstractCompositeIdentifierAttributeCollector { private final PropertyPath propertyPath; + + public EncapsulatedCompositeIdentifierAttributeCollector(EntityReference entityReference) { + super( entityReference ); + this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath(); + } + + @Override + protected IdentifierDescription buildIdentifierDescription() { + return new IdentifierDescriptionImpl( + entityReference, + identifierFetches.toArray( new AbstractSingularAttributeFetch[ identifierFetches.size() ] ), + null + ); + } + + @Override + public PropertyPath getPropertyPath() { + return propertyPath; + } + + @Override + public Type getType(Fetch fetch) { + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { + throw new IllegalArgumentException( + String.format( + "Fetch owner property name [%s] is not the same as the identifier property name [%s].", + fetch.getOwnerPropertyName(), + entityReference.getEntityPersister().getIdentifierPropertyName() + ) + ); + } + return entityReference.getEntityPersister().getIdentifierType(); + } + + @Override + public String[] getColumnNames(Fetch fetch) { + return ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames(); + } + } + + protected static class NonEncapsulatedIdentifierAttributeCollector extends AbstractCompositeIdentifierAttributeCollector { + private final PropertyPath propertyPath; + private final FetchOwnerDelegate fetchOwnerDelegate; + public NonEncapsulatedIdentifierAttributeCollector(EntityReference entityReference) { super( entityReference ); this.propertyPath = ( (FetchOwner) entityReference ).getPropertyPath().append( "" ); + this.fetchOwnerDelegate = new CompositeFetchOwnerDelegate( + entityReference.getEntityPersister().getFactory(), + (CompositeType) entityReference.getEntityPersister().getIdentifierType(), + ( (Loadable) entityReference.getEntityPersister() ).getIdentifierColumnNames() + ); } @Override @@ -639,6 +750,24 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder public PropertyPath getPropertyPath() { return propertyPath; } + + + public Type getType(Fetch fetch) { + if ( !fetch.getOwnerPropertyName().equals( entityReference.getEntityPersister().getIdentifierPropertyName() ) ) { + throw new IllegalArgumentException( + String.format( + "Fetch owner property name [%s] is not the same as the identifier property name [%s].", + fetch.getOwnerPropertyName(), + entityReference.getEntityPersister().getIdentifierPropertyName() + ) + ); + } + return fetchOwnerDelegate.getType( fetch ); + } + + public String[] getColumnNames(Fetch fetch) { + return fetchOwnerDelegate.getColumnNames( fetch ); + } } private static class IdentifierDescriptionImpl implements IdentifierDescription { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 819ad1b0a4..aa1831b121 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -80,10 +80,14 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.Queryable; +import org.hibernate.persister.walking.internal.CompositionSingularSubAttributesHelper; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.sql.Alias; @@ -1941,7 +1945,6 @@ public abstract class AbstractCollectionPersister public abstract FilterAliasGenerator getFilterAliasGenerator(final String rootAlias); - // ColectionDefinition impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override @@ -2007,12 +2010,42 @@ public abstract class AbstractCollectionPersister } @Override - public CompositionDefinition toCompositeDefinition() { + public CompositionElementDefinition toCompositeElementDefinition() { + final String propertyName = role.substring( entityName.length() + 1 ); + final int propertyIndex = ownerPersister.getEntityMetamodel().getPropertyIndex( propertyName ); + if ( ! getType().isComponentType() ) { throw new IllegalStateException( "Cannot treat entity collection element type as composite" ); } - // todo : implement - throw new NotYetImplementedException(); + + return new CompositionElementDefinition() { + @Override + public String getName() { + return ""; + } + + @Override + public Type getType() { + return getElementType(); + } + + @Override + public AttributeSource getSource() { + // TODO: what if this is a collection w/in an encapsulated composition attribute? + // should return the encapsulated composition attribute instead??? + return getOwnerEntityPersister(); + } + + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getCompositionElementSubAttributes( this ); + } + + @Override + public CollectionDefinition getCollectionDefinition() { + return AbstractCollectionPersister.this; + } + }; } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 51062e1524..9552291f79 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -59,7 +59,6 @@ import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl; import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.StructuredCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; -import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.internal.StatefulPersistenceContext; @@ -111,12 +110,7 @@ import org.hibernate.metamodel.relational.DerivedValue; import org.hibernate.metamodel.relational.Value; import org.hibernate.persister.walking.internal.EntityIdentifierDefinitionHelper; import org.hibernate.persister.walking.spi.AttributeDefinition; -import org.hibernate.persister.walking.spi.AttributeSource; -import org.hibernate.persister.walking.spi.CompositionDefinition; -import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; -import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; -import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; import org.hibernate.sql.Alias; @@ -132,7 +126,6 @@ import org.hibernate.sql.Update; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.AssociationType; -import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java new file mode 100644 index 0000000000..8cac9860c0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/CompositionSingularSubAttributesHelper.java @@ -0,0 +1,212 @@ +package org.hibernate.persister.walking.internal; + +import java.util.Iterator; + +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchStyle; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.loader.PropertyPath; +import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.persister.entity.AbstractEntityPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.OuterJoinLoadable; +import org.hibernate.persister.spi.HydratedCompoundValueHandler; +import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; +import org.hibernate.persister.walking.spi.AssociationKey; +import org.hibernate.persister.walking.spi.AttributeDefinition; +import org.hibernate.persister.walking.spi.AttributeSource; +import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; +import org.hibernate.persister.walking.spi.EntityDefinition; +import org.hibernate.persister.walking.spi.WalkingException; +import org.hibernate.type.AssociationType; +import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; + +/** + * @author Gail Badner + */ +public class CompositionSingularSubAttributesHelper { + + public static Iterable getIdentifierSubAttributes( + final AbstractEntityPersister entityPersister) { + return getSingularSubAttributes( + entityPersister, + entityPersister, + (CompositeType) entityPersister.getIdentifierType(), + entityPersister.getTableName(), + entityPersister.getRootTableIdentifierColumnNames() + ); + } + + public static Iterable getCompositionElementSubAttributes( + CompositionElementDefinition compositionElementDefinition) { + final QueryableCollection collectionPersister = + (QueryableCollection) compositionElementDefinition.getCollectionDefinition().getCollectionPersister(); + return getSingularSubAttributes( + compositionElementDefinition.getSource(), + (OuterJoinLoadable) collectionPersister.getOwnerEntityPersister(), + (CompositeType) collectionPersister.getElementType(), + collectionPersister.getTableName(), + collectionPersister.getElementColumnNames() + ); + } + + private static Iterable getSingularSubAttributes( + final AttributeSource source, + final OuterJoinLoadable ownerEntityPersister, + final CompositeType compositeType, + final String lhsTableName, + final String[] lhsColumns) { + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final int numberOfAttributes = compositeType.getSubtypes().length; + private int currentSubAttributeNumber = 0; + private int currentColumnPosition = 0; + + @Override + public boolean hasNext() { + return currentSubAttributeNumber < numberOfAttributes; + } + + @Override + public AttributeDefinition next() { + final int subAttributeNumber = currentSubAttributeNumber; + currentSubAttributeNumber++; + + final String name = compositeType.getPropertyNames()[subAttributeNumber]; + final Type type = compositeType.getSubtypes()[subAttributeNumber]; + + final int columnPosition = currentColumnPosition; + final int columnSpan = type.getColumnSpan( ownerEntityPersister.getFactory() ); + final String[] subAttributeLhsColumns = ArrayHelper.slice( lhsColumns, columnPosition, columnSpan ); + + currentColumnPosition += columnSpan; + + if ( type.isAssociationType() ) { + final AssociationType aType = (AssociationType) type; + return new AssociationAttributeDefinition() { + @Override + public AssociationKey getAssociationKey() { + /* TODO: is this always correct? */ + //return new AssociationKey( + // joinable.getTableName(), + // JoinHelper.getRHSColumnNames( aType, getEntityPersister().getFactory() ) + //); + return new AssociationKey( + lhsTableName, + subAttributeLhsColumns + ); + } + + @Override + public boolean isCollection() { + return false; + } + + @Override + public EntityDefinition toEntityDefinition() { + return (EntityPersister) aType.getAssociatedJoinable( ownerEntityPersister.getFactory() ); + } + + @Override + public CollectionDefinition toCollectionDefinition() { + throw new WalkingException( "A collection cannot be mapped to a composite ID sub-attribute." ); + } + + @Override + public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { + return new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + } + + @Override + public CascadeStyle determineCascadeStyle() { + return CascadeStyles.NONE; + } + + @Override + public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + else if ( type.isComponentType() ) { + return new CompositionDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return this; + } + + @Override + public Iterable getAttributes() { + return CompositionSingularSubAttributesHelper.getSingularSubAttributes( + this, + ownerEntityPersister, + (CompositeType) type, + lhsTableName, + subAttributeLhsColumns + ); + } + }; + } + else { + return new AttributeDefinition() { + @Override + public String getName() { + return name; + } + + @Override + public Type getType() { + return type; + } + + @Override + public AttributeSource getSource() { + return source; + } + }; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException( "Remove operation not supported here" ); + } + }; + } + }; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java index abb5e5b958..bec4320dc2 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/internal/EntityIdentifierDefinitionHelper.java @@ -23,37 +23,16 @@ */ package org.hibernate.persister.walking.internal; -import java.util.Iterator; - -import org.hibernate.engine.FetchStrategy; -import org.hibernate.engine.FetchStyle; -import org.hibernate.engine.FetchTiming; -import org.hibernate.engine.spi.CascadeStyle; -import org.hibernate.engine.spi.CascadeStyles; -import org.hibernate.engine.spi.LoadQueryInfluencers; -import org.hibernate.loader.PropertyPath; import org.hibernate.persister.entity.AbstractEntityPersister; -import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.Joinable; -import org.hibernate.persister.spi.HydratedCompoundValueHandler; -import org.hibernate.persister.walking.spi.AssociationAttributeDefinition; -import org.hibernate.persister.walking.spi.AssociationKey; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.AttributeSource; -import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EncapsulatedEntityIdentifierDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.NonEncapsulatedEntityIdentifierDefinition; -import org.hibernate.persister.walking.spi.WalkingException; -import org.hibernate.type.AssociationType; -import org.hibernate.type.ComponentType; import org.hibernate.type.Type; -import static org.hibernate.engine.internal.JoinHelper.getLHSColumnNames; -import static org.hibernate.engine.internal.JoinHelper.getLHSTableName; - /** * @author Gail Badner */ @@ -78,12 +57,13 @@ public class EntityIdentifierDefinitionHelper { }; } - public static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition(final AbstractEntityPersister entityPersister) { + public static EntityIdentifierDefinition buildEncapsulatedCompositeIdentifierDefinition( + final AbstractEntityPersister entityPersister) { return new EncapsulatedEntityIdentifierDefinition() { @Override public AttributeDefinition getAttributeDefinition() { - return new CompositeAttributeDefinitionAdapter( entityPersister ); + return new CompositionDefinitionAdapter( entityPersister ); } @Override @@ -102,7 +82,7 @@ public class EntityIdentifierDefinitionHelper { return new NonEncapsulatedEntityIdentifierDefinition() { @Override public Iterable getAttributes() { - return new CompositeAttributeDefinitionAdapter( entityPersister ).getAttributes(); + return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( entityPersister ); } @Override @@ -154,160 +134,20 @@ public class EntityIdentifierDefinitionHelper { } } - private static class CompositeAttributeDefinitionAdapter extends AttributeDefinitionAdapter implements CompositionDefinition { + private static class CompositionDefinitionAdapter extends AttributeDefinitionAdapter implements CompositionDefinition { - CompositeAttributeDefinitionAdapter(AbstractEntityPersister entityPersister) { + CompositionDefinitionAdapter(AbstractEntityPersister entityPersister) { super( entityPersister ); } + @Override + public String toString() { + return ""; + } + @Override public Iterable getAttributes() { - return new Iterable() { - @Override - public Iterator iterator() { - final ComponentType componentType = (ComponentType) getType(); - return new Iterator() { - private final int numberOfAttributes = componentType.getSubtypes().length; - private int currentSubAttributeNumber = 0; - private int currentColumnPosition = 0; - - @Override - public boolean hasNext() { - return currentSubAttributeNumber < numberOfAttributes; - } - - @Override - public AttributeDefinition next() { - final int subAttributeNumber = currentSubAttributeNumber; - currentSubAttributeNumber++; - - final AttributeSource source = getSource(); - final String name = componentType.getPropertyNames()[subAttributeNumber]; - final Type type = componentType.getSubtypes()[subAttributeNumber]; - - final int columnPosition = currentColumnPosition; - currentColumnPosition += type.getColumnSpan( getEntityPersister().getFactory() ); - - if ( type.isAssociationType() ) { - final AssociationType aType = (AssociationType) type; - final Joinable joinable = aType.getAssociatedJoinable( getEntityPersister().getFactory() ); - return new AssociationAttributeDefinition() { - @Override - public AssociationKey getAssociationKey() { - /* TODO: is this always correct? */ - //return new AssociationKey( - // joinable.getTableName(), - // JoinHelper.getRHSColumnNames( aType, getEntityPersister().getFactory() ) - //); - return new AssociationKey( - getEntityPersister().getTableName(), - getLHSColumnNames( - aType, - -1, - columnPosition, - getEntityPersister(), - getEntityPersister().getFactory() - ) - ); - - } - - @Override - public boolean isCollection() { - return false; - } - - @Override - public EntityDefinition toEntityDefinition() { - return (EntityPersister) joinable; - } - - @Override - public CollectionDefinition toCollectionDefinition() { - throw new WalkingException( "A collection cannot be mapped to a composite ID sub-attribute." ); - } - - @Override - public FetchStrategy determineFetchPlan(LoadQueryInfluencers loadQueryInfluencers, PropertyPath propertyPath) { - return new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); - } - - @Override - public CascadeStyle determineCascadeStyle() { - return CascadeStyles.NONE; - } - - @Override - public HydratedCompoundValueHandler getHydratedCompoundValueExtractor() { - return null; - } - - @Override - public String getName() { - return name; - } - - @Override - public Type getType() { - return type; - } - - @Override - public AttributeSource getSource() { - return source; - } - }; - } - else if ( type.isComponentType() ) { - return new CompositionDefinition() { - @Override - public String getName() { - return name; - } - - @Override - public Type getType() { - return type; - } - - @Override - public AttributeSource getSource() { - return source; - } - - @Override - public Iterable getAttributes() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - }; - } - else { - return new AttributeDefinition() { - @Override - public String getName() { - return name; - } - - @Override - public Type getType() { - return type; - } - - @Override - public AttributeSource getSource() { - return source; - } - }; - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException( "Remove operation not supported here" ); - } - }; - } - }; + return CompositionSingularSubAttributesHelper.getIdentifierSubAttributes( getEntityPersister() ); } } } \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java index 285ec22270..6bf7d9a156 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/AssociationVisitationStrategy.java @@ -55,6 +55,9 @@ public interface AssociationVisitationStrategy { public void startingComposite(CompositionDefinition compositionDefinition); public void finishingComposite(CompositionDefinition compositionDefinition); + public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition); + public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition); + public boolean startingAttribute(AttributeDefinition attributeDefinition); public void finishingAttribute(AttributeDefinition attributeDefinition); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java index b42f8b0ddc..ff138f76f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CollectionElementDefinition.java @@ -35,5 +35,5 @@ public interface CollectionElementDefinition { public EntityDefinition toEntityDefinition(); - public CompositionDefinition toCompositeDefinition(); + public CompositionElementDefinition toCompositeElementDefinition(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java new file mode 100644 index 0000000000..cd073881c8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/CompositionElementDefinition.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.walking.spi; + +/** + * @author Gail Badner + */ +public interface CompositionElementDefinition extends CompositionDefinition{ + public CollectionDefinition getCollectionDefinition(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java index 3b5caa44ae..ffdccb2b99 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/walking/spi/MetadataDrivenModelGraphVisitor.java @@ -28,21 +28,12 @@ import java.util.Set; import org.jboss.logging.Logger; -import org.hibernate.engine.FetchStrategy; -import org.hibernate.engine.FetchStyle; -import org.hibernate.engine.FetchTiming; -import org.hibernate.engine.spi.CascadeStyle; -import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.PropertyPath; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.Joinable; -import org.hibernate.persister.spi.HydratedCompoundValueHandler; import org.hibernate.type.Type; -import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; - /** * Provides model graph visitation based on the defined metadata (as opposed to based on the incoming graph * as we see in cascade processing). In layman terms, we are walking the graph of the users model as defined by @@ -211,7 +202,7 @@ public class MetadataDrivenModelGraphVisitor { strategy.startingCollectionElements( elementDefinition ); if ( elementDefinition.getType().isComponentType() ) { - visitCompositeDefinition( elementDefinition.toCompositeDefinition() ); + visitCompositeElementDefinition( elementDefinition.toCompositeElementDefinition() ); } else if ( elementDefinition.getType().isEntityType() ) { visitEntityDefinition( elementDefinition.toEntityDefinition() ); @@ -220,6 +211,13 @@ public class MetadataDrivenModelGraphVisitor { strategy.finishingCollectionElements( elementDefinition ); } + private void visitCompositeElementDefinition(CompositionElementDefinition compositionElementDefinition) { + strategy.startingCompositeElement( compositionElementDefinition ); + + visitAttributes( compositionElementDefinition ); + + strategy.finishingCompositeElement( compositionElementDefinition ); + } private final Set visitedAssociationKeys = new HashSet(); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java index a225b8a05f..9dbcbc7558 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositeBasedAttribute.java @@ -39,7 +39,7 @@ public abstract class AbstractCompositeBasedAttribute private final int ownerAttributeNumber; public AbstractCompositeBasedAttribute( - AbstractCompositionDefinition source, + AbstractCompositionAttribute source, SessionFactoryImplementor sessionFactory, int attributeNumber, String attributeName, @@ -55,7 +55,7 @@ public abstract class AbstractCompositeBasedAttribute } @Override - public AbstractCompositionDefinition getSource() { - return (AbstractCompositionDefinition) super.getSource(); + public AbstractCompositionAttribute getSource() { + return (AbstractCompositionAttribute) super.getSource(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java similarity index 79% rename from hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java rename to hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java index 554a630ed6..e563a9dd9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/AbstractCompositionAttribute.java @@ -48,9 +48,9 @@ import static org.hibernate.engine.internal.JoinHelper.getRHSColumnNames; /** * @author Steve Ebersole */ -public abstract class AbstractCompositionDefinition extends AbstractNonIdentifierAttribute implements +public abstract class AbstractCompositionAttribute extends AbstractNonIdentifierAttribute implements CompositionDefinition { - protected AbstractCompositionDefinition( + protected AbstractCompositionAttribute( AttributeSource source, SessionFactoryImplementor sessionFactory, int attributeNumber, @@ -120,41 +120,41 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie } return new CompositeBasedAssociationAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), subAttributeNumber, name, (AssociationType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation(), - AbstractCompositionDefinition.this.attributeNumber(), + AbstractCompositionAttribute.this.attributeNumber(), associationKey ); } else if ( type.isComponentType() ) { return new CompositionBasedCompositionAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), subAttributeNumber, name, (CompositeType) type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() @@ -162,19 +162,19 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie } else { return new CompositeBasedBasicAttribute( - AbstractCompositionDefinition.this, + AbstractCompositionAttribute.this, sessionFactory(), subAttributeNumber, name, type, new BaselineAttributeInformation.Builder() - .setInsertable( AbstractCompositionDefinition.this.isInsertable() ) - .setUpdateable( AbstractCompositionDefinition.this.isUpdateable() ) - .setInsertGenerated( AbstractCompositionDefinition.this.isInsertGenerated() ) - .setUpdateGenerated( AbstractCompositionDefinition.this.isUpdateGenerated() ) + .setInsertable( AbstractCompositionAttribute.this.isInsertable() ) + .setUpdateable( AbstractCompositionAttribute.this.isUpdateable() ) + .setInsertGenerated( AbstractCompositionAttribute.this.isInsertGenerated() ) + .setUpdateGenerated( AbstractCompositionAttribute.this.isUpdateGenerated() ) .setNullable( getType().getPropertyNullability()[subAttributeNumber] ) .setDirtyCheckable( true ) - .setVersionable( AbstractCompositionDefinition.this.isVersionable() ) + .setVersionable( AbstractCompositionAttribute.this.isVersionable() ) .setCascadeStyle( getType().getCascadeStyle( subAttributeNumber ) ) .setFetchMode( getType().getFetchMode( subAttributeNumber ) ) .createInformation() @@ -196,7 +196,7 @@ public abstract class AbstractCompositionDefinition extends AbstractNonIdentifie return ( (EntityDefinition) getSource() ).getEntityPersister(); } else { - return ( (AbstractCompositionDefinition) getSource() ).locateOwningPersister(); + return ( (AbstractCompositionAttribute) getSource() ).locateOwningPersister(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java index 81c7f343f8..72eb5eac0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositeBasedAssociationAttribute.java @@ -55,7 +55,7 @@ public class CompositeBasedAssociationAttribute private Joinable joinable; public CompositeBasedAssociationAttribute( - AbstractCompositionDefinition source, + AbstractCompositionAttribute source, SessionFactoryImplementor factory, int attributeNumber, String attributeName, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java index 7beee25984..4a154f130d 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/component/CompositionBasedCompositionAttribute.java @@ -32,7 +32,7 @@ import org.hibernate.type.CompositeType; * @author Steve Ebersole */ public class CompositionBasedCompositionAttribute - extends AbstractCompositionDefinition + extends AbstractCompositionAttribute implements CompositionDefinition { public CompositionBasedCompositionAttribute( CompositionDefinition source, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java index e283cae94b..66a7242a5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityBasedCompositionAttribute.java @@ -25,7 +25,7 @@ package org.hibernate.tuple.entity; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.tuple.component.AbstractCompositionDefinition; +import org.hibernate.tuple.component.AbstractCompositionAttribute; import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.tuple.BaselineAttributeInformation; import org.hibernate.type.CompositeType; @@ -34,7 +34,7 @@ import org.hibernate.type.CompositeType; * @author Steve Ebersole */ public class EntityBasedCompositionAttribute - extends AbstractCompositionDefinition + extends AbstractCompositionAttribute implements CompositionDefinition { public EntityBasedCompositionAttribute( diff --git a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java index 3f76e0607b..ca0cd1aeab 100644 --- a/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/loader/EncapsulatedCompositeAttributeResultSetProcessorTest.java @@ -42,6 +42,7 @@ import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.Id; +import javax.persistence.ManyToOne; import org.junit.Test; @@ -60,6 +61,7 @@ import org.hibernate.loader.spi.LoadQueryAliasResolutionContext; import org.hibernate.loader.spi.NamedParameterContext; import org.hibernate.loader.spi.NoOpLoadPlanAdvisor; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.test.component.cascading.toone.PersonalInfo; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.ExtraAssertions; @@ -128,12 +130,15 @@ public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCo session.close(); } - /* @Test public void testNestedCompositeElementCollectionProcessing() throws Exception { // create some test data Session session = openSession(); session.beginTransaction(); + Person person = new Person(); + person.id = 1; + person.name = "Joe Blow"; + session.save( person ); Customer customer = new Customer(); customer.id = 1L; Investment investment1 = new Investment(); @@ -142,6 +147,7 @@ public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCo investment1.monetaryAmount = new MonetaryAmount(); investment1.monetaryAmount.currency = MonetaryAmount.CurrencyCode.USD; investment1.monetaryAmount.amount = BigDecimal.valueOf( 1234, 2 ); + investment1.performedBy = person; Investment investment2 = new Investment(); investment2.description = "bond"; investment2.date = new Date(); @@ -173,11 +179,11 @@ public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCo // clean up test data session = openSession(); session.beginTransaction(); - session.createQuery( "delete Customer" ).executeUpdate(); + session.delete( customerWork.investments.get( 0 ).performedBy ); + session.delete( customerWork ); session.getTransaction().commit(); session.close(); } - */ private List getResults(EntityPersister entityPersister ) { final SingleRootReturnLoadPlanBuilderStrategy strategy = new SingleRootReturnLoadPlanBuilderStrategy( @@ -288,6 +294,7 @@ public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCo private MonetaryAmount monetaryAmount; private String description; private Date date; + private Person performedBy; @Embedded public MonetaryAmount getMonetaryAmount() { @@ -308,6 +315,13 @@ public class EncapsulatedCompositeAttributeResultSetProcessorTest extends BaseCo public void setDate(Date date) { this.date = date; } + @ManyToOne + public Person getPerformedBy() { + return performedBy; + } + public void setPerformedBy(Person performedBy) { + this.performedBy = performedBy; + } } @Embeddable diff --git a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java index da8bc7f961..411a42f714 100644 --- a/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/persister/walking/BasicWalkingTest.java @@ -38,6 +38,7 @@ import org.hibernate.persister.walking.spi.CollectionDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; +import org.hibernate.persister.walking.spi.CompositionElementDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; import org.hibernate.persister.walking.spi.MetadataDrivenModelGraphVisitor; @@ -168,6 +169,28 @@ public class BasicWalkingTest extends BaseCoreFunctionalTestCase { ); } + @Override + public void startingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + System.out.println( + String.format( + "%s Starting composite (%s)", + StringHelper.repeat( ">>", ++depth ), + compositionElementDefinition.toString() + ) + ); + } + + @Override + public void finishingCompositeElement(CompositionElementDefinition compositionElementDefinition) { + System.out.println( + String.format( + "%s Finishing composite (%s)", + StringHelper.repeat( ">>", depth-- ), + compositionElementDefinition.toString() + ) + ); + } + @Override public boolean startingAttribute(AttributeDefinition attributeDefinition) { System.out.println( From 21ae220a4ce2da4f99a56b58a91639ce8d417956 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 17 May 2013 13:22:46 -0400 Subject: [PATCH 13/17] HHH-8254 throw HibernateException when transaction is rolledback by a reaper thread --- ...ynchronizationCallbackCoordinatorImpl.java | 8 ++-- .../transaction/TransactionJoiningTest.java | 48 ++++++++++++------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java index 6f3de2c254..3e5d40e0c3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/synchronization/internal/SynchronizationCallbackCoordinatorImpl.java @@ -25,6 +25,7 @@ package org.hibernate.engine.transaction.synchronization.internal; import javax.transaction.SystemException; +import org.hibernate.HibernateException; import org.hibernate.TransactionException; import org.hibernate.cfg.Settings; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; @@ -55,9 +56,9 @@ public class SynchronizationCallbackCoordinatorImpl implements SynchronizationCa private AfterCompletionAction afterCompletionAction; private ExceptionMapper exceptionMapper; - private long registrationThreadId; + private volatile long registrationThreadId; private final int NO_STATUS = -1; - private int delayedCompletionHandlingStatus; + private volatile int delayedCompletionHandlingStatus; public SynchronizationCallbackCoordinatorImpl(TransactionCoordinator transactionCoordinator) { this.transactionCoordinator = transactionCoordinator; @@ -153,11 +154,12 @@ public class SynchronizationCallbackCoordinatorImpl implements SynchronizationCa if ( delayedCompletionHandlingStatus != NO_STATUS ) { doAfterCompletion( delayedCompletionHandlingStatus ); delayedCompletionHandlingStatus = NO_STATUS; + throw new HibernateException("Transaction was rolled back in a different thread!"); } } private void doAfterCompletion(int status) { - LOG.tracev( "Transaction after completion callback [status={0}]", status ); + LOG.tracev( "Transaction afterCompletion callback [status={0}]", status ); try { afterCompletionAction.doAction( transactionCoordinator, status ); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java index eca6de48cb..896add1131 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionJoiningTest.java @@ -23,7 +23,6 @@ */ package org.hibernate.jpa.test.transaction; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,14 +30,17 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import javax.persistence.EntityManager; +import javax.persistence.PersistenceException; import javax.transaction.Status; import javax.transaction.Synchronization; +import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.transaction.internal.jta.CMTTransaction; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; +import org.hibernate.exception.GenericJDBCException; import org.hibernate.internal.SessionImpl; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -152,32 +154,46 @@ public class TransactionJoiningTest extends BaseEntityManagerFunctionalTestCase * See HHH-7910 */ @Test - @TestForIssue(jiraKey="HHH-7910") + @TestForIssue(jiraKey = "HHH-7910") public void testMultiThreadTransactionTimeout() throws Exception { TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin(); - + EntityManager em = entityManagerFactory().createEntityManager(); final SessionImpl sImpl = em.unwrap( SessionImpl.class ); - - final CountDownLatch latch = new CountDownLatch(1); - + + final CountDownLatch latch = new CountDownLatch( 1 ); + Thread thread = new Thread() { public void run() { - sImpl.getTransactionCoordinator().getSynchronizationCallbackCoordinator().afterCompletion( Status.STATUS_ROLLEDBACK ); + sImpl.getTransactionCoordinator().getSynchronizationCallbackCoordinator() + .afterCompletion( Status.STATUS_ROLLEDBACK ); latch.countDown(); } }; thread.start(); - - latch.await(); - - em.persist( new Book( "The Book of Foo", 1 ) ); - - // Ensure that the session was cleared by the background thread. - assertEquals( "The background thread did not clear the session as expected!", - 0, em.createQuery( "from Book" ).getResultList().size() ); - TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit(); + latch.await(); + + boolean caught = false; + try { + em.persist( new Book( "The Book of Foo", 1 ) ); + } + catch ( PersistenceException e ) { + caught = e.getCause().getClass().equals( HibernateException.class ); + } + assertTrue( caught ); + + // Ensure that the connection was closed by the background thread. + caught = false; + try { + em.createQuery( "from Book" ).getResultList(); + } + catch ( PersistenceException e ) { + caught = e.getCause().getClass().equals( GenericJDBCException.class ); + } + assertTrue( caught ); + + TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback(); em.close(); } From 14993a4637dddf3a6c7f4cd833dfe63c6efa9a03 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 17 May 2013 12:49:47 -0500 Subject: [PATCH 14/17] HHH-8223 - Implement @NamedEntityGraph binding --- .../org/hibernate/cfg/AnnotationBinder.java | 12 ++- .../java/org/hibernate/cfg/Configuration.java | 20 +++++ .../main/java/org/hibernate/cfg/Mappings.java | 10 +++ .../cfg/annotations/EntityBinder.java | 22 ++++++ .../NamedEntityGraphDefinition.java | 59 ++++++++++++++ .../jpa/graph/internal/AbstractGraphNode.java | 16 ++-- .../jpa/graph/internal/AttributeNodeImpl.java | 21 +++-- .../jpa/graph/internal/EntityGraphImpl.java | 16 ++-- .../jpa/graph/internal/SubgraphImpl.java | 16 ++-- .../internal/EntityManagerFactoryImpl.java | 76 ++++++++++++++++++- .../basic/BasicNamedEntityGraphTest.java | 48 ++++++++++++ .../jpa/test/graphs/named/basic/Person.java | 38 ++++++++++ 12 files changed, 320 insertions(+), 34 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 77034482f6..1ce6542a96 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -209,6 +209,9 @@ public final class AnnotationBinder { public static void bindDefaults(Mappings mappings) { Map defaults = mappings.getReflectionManager().getDefaults(); + + // id generators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { List anns = ( List ) defaults.get( SequenceGenerator.class ); if ( anns != null ) { @@ -231,6 +234,9 @@ public final class AnnotationBinder { } } } + + // queries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { List anns = ( List ) defaults.get( NamedQuery.class ); if ( anns != null ) { @@ -247,6 +253,9 @@ public final class AnnotationBinder { } } } + + // result-set-mappings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { List anns = ( List ) defaults.get( SqlResultSetMapping.class ); if ( anns != null ) { @@ -256,6 +265,8 @@ public final class AnnotationBinder { } } + // stored procs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { final List annotations = (List) defaults.get( NamedStoredProcedureQuery.class ); @@ -265,7 +276,6 @@ public final class AnnotationBinder { } } } - { final List annotations = (List) defaults.get( NamedStoredProcedureQueries.class ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index ea3f4b8a2b..671b0bbb28 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -81,6 +81,8 @@ import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; +import org.hibernate.cache.spi.GeneralDataRegion; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.cfg.annotations.NamedProcedureCallDefinition; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -217,6 +219,7 @@ public class Configuration implements Serializable { protected Map namedSqlQueries; protected Map namedProcedureCallMap; protected Map sqlResultSetMappings; + protected Map namedEntityGraphMap; protected Map typeDefs; protected Map filterDefinitions; @@ -299,6 +302,7 @@ public class Configuration implements Serializable { namedQueries = new HashMap(); namedSqlQueries = new HashMap(); sqlResultSetMappings = new HashMap(); + namedEntityGraphMap = new HashMap(); typeDefs = new HashMap(); filterDefinitions = new HashMap(); @@ -2619,6 +2623,12 @@ public class Configuration implements Serializable { } } + public java.util.Collection getNamedEntityGraphs() { + return namedEntityGraphMap == null + ? Collections.emptyList() + : namedEntityGraphMap.values(); + } + // Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2886,6 +2896,16 @@ public class Configuration implements Serializable { } } + @Override + public void addNamedEntityGraphDefintion(NamedEntityGraphDefinition definition) + throws DuplicateMappingException { + final String name = definition.getRegisteredName(); + final NamedEntityGraphDefinition previous = namedEntityGraphMap.put( name, definition ); + if ( previous != null ) { + throw new DuplicateMappingException( "NamedEntityGraph", name ); + } + } + public void addDefaultSQLQuery(String name, NamedSQLQueryDefinition query) { applySQLQuery( name, query ); defaultNamedNativeQueryNames.add( name ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java index 4b49851563..6ea34e60b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java @@ -37,6 +37,7 @@ import org.hibernate.MappingException; import org.hibernate.annotations.AnyMetaDef; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.cfg.annotations.NamedProcedureCallDefinition; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; @@ -348,6 +349,15 @@ public interface Mappings { */ public void addNamedProcedureCallDefinition(NamedProcedureCallDefinition definition) throws DuplicateMappingException; + /** + * Adds metadata for a named entity graph to this repository + * + * @param namedEntityGraphDefinition The procedure call information + * + * @throws DuplicateMappingException If an entity graph already exists with that name. + */ + public void addNamedEntityGraphDefintion(NamedEntityGraphDefinition namedEntityGraphDefinition); + /** * Get the metadata for a named SQL result set mapping. * diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index defd4096f7..0a919319ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -35,6 +35,8 @@ import javax.persistence.Access; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedEntityGraphs; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTables; @@ -162,8 +164,28 @@ public class EntityBinder { this.annotatedClass = annotatedClass; bindEjb3Annotation( ejb3Ann ); bindHibernateAnnotation( hibAnn ); + + processNamedEntityGraphs(); } + private void processNamedEntityGraphs() { + processNamedEntityGraph( annotatedClass.getAnnotation( NamedEntityGraph.class ) ); + final NamedEntityGraphs graphs = annotatedClass.getAnnotation( NamedEntityGraphs.class ); + if ( graphs != null ) { + for ( NamedEntityGraph graph : graphs.value() ) { + processNamedEntityGraph( graph ); + } + } + } + + private void processNamedEntityGraph(NamedEntityGraph annotation) { + if ( annotation == null ) { + return; + } + mappings.addNamedEntityGraphDefintion( new NamedEntityGraphDefinition( annotation, name, persistentClass.getEntityName() ) ); + } + + @SuppressWarnings("SimplifiableConditionalExpression") private void bindHibernateAnnotation(org.hibernate.annotations.Entity hibAnn) { { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java new file mode 100644 index 0000000000..4c10a940b1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/NamedEntityGraphDefinition.java @@ -0,0 +1,59 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.cfg.annotations; + +import javax.persistence.NamedEntityGraph; + +/** + * Models the definition of a {@link NamedEntityGraph} annotation + * + * @author Steve Ebersole + */ +public class NamedEntityGraphDefinition { + private final NamedEntityGraph annotation; + private final String jpaEntityName; + private final String entityName; + + public NamedEntityGraphDefinition(NamedEntityGraph annotation, String jpaEntityName, String entityName) { + this.annotation = annotation; + this.jpaEntityName = jpaEntityName; + this.entityName = entityName; + } + + public String getRegisteredName() { + return jpaEntityName; + } + + public String getJpaEntityName() { + return jpaEntityName; + } + + public String getEntityName() { + return entityName; + } + + public NamedEntityGraph getAnnotation() { + return annotation; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java index ff760bd2f1..e30c4f88fb 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AbstractGraphNode.java @@ -162,42 +162,42 @@ public abstract class AbstractGraphNode implements GraphNodeImplementor { } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return addAttribute( attribute ).makeSubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return addAttribute( attribute ).makeSubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return addAttribute( attributeName ).makeSubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return addAttribute( attributeName ).makeSubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return addAttribute( attribute ).makeKeySubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return addAttribute( attribute ).makeKeySubgraph( type ); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return addAttribute( attributeName ).makeKeySubgraph(); } @SuppressWarnings("unchecked") - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return addAttribute( attributeName ).makeKeySubgraph( type ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java index 55584cb426..d6cb4b6e68 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/AttributeNodeImpl.java @@ -108,16 +108,16 @@ public class AttributeNodeImpl implements AttributeNode, AttributeNodeImpl } @SuppressWarnings("unchecked") - public Subgraph makeSubgraph() { - return (Subgraph) internalMakeSubgraph( null ); + public SubgraphImpl makeSubgraph() { + return (SubgraphImpl) internalMakeSubgraph( null ); } @SuppressWarnings("unchecked") - public Subgraph makeSubgraph(Class type) { - return (Subgraph) internalMakeSubgraph( type ); + public SubgraphImpl makeSubgraph(Class type) { + return (SubgraphImpl) internalMakeSubgraph( type ); } - private Subgraph internalMakeSubgraph(Class type) { + private SubgraphImpl internalMakeSubgraph(Class type) { if ( attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED ) { throw new IllegalArgumentException( @@ -193,12 +193,17 @@ public class AttributeNodeImpl implements AttributeNode, AttributeNodeImpl return type.isAssignableFrom( entityPersister.getMappedClass() ); } - public Subgraph makeKeySubgraph() { - return (SubgraphImpl) makeKeySubgraph( null ); + @SuppressWarnings("unchecked") + public SubgraphImpl makeKeySubgraph() { + return (SubgraphImpl) internalMakeKeySubgraph( null ); } @SuppressWarnings("unchecked") - public Subgraph makeKeySubgraph(Class type) { + public SubgraphImpl makeKeySubgraph(Class type) { + return (SubgraphImpl) internalMakeKeySubgraph( type ); + } + + public SubgraphImpl internalMakeKeySubgraph(Class type) { if ( ! attribute.isCollection() ) { throw new IllegalArgumentException( String.format( "Non-collection attribute [%s] cannot be target of key subgraph", getAttributeName() ) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java index 4189b9144d..c41ed06b65 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/EntityGraphImpl.java @@ -83,42 +83,42 @@ public class EntityGraphImpl extends AbstractGraphNode implements EntityGr } @Override - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return super.addSubgraph( attribute ); } @Override - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return super.addSubgraph( attribute, type ); } @Override - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return super.addSubgraph( attributeName ); } @Override - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return super.addSubgraph( attributeName, type ); } @Override - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return super.addKeySubgraph( attribute ); } @Override - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return super.addKeySubgraph( attribute, type ); } @Override - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return super.addKeySubgraph( attributeName ); } @Override - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return super.addKeySubgraph( attributeName, type ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java index 1f206ff32f..f52c01dba1 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java @@ -68,42 +68,42 @@ public class SubgraphImpl extends AbstractGraphNode implements Subgraph } @Override - public Subgraph addSubgraph(Attribute attribute) { + public SubgraphImpl addSubgraph(Attribute attribute) { return super.addSubgraph( attribute ); } @Override - public Subgraph addSubgraph(Attribute attribute, Class type) { + public SubgraphImpl addSubgraph(Attribute attribute, Class type) { return super.addSubgraph( attribute, type ); } @Override - public Subgraph addSubgraph(String attributeName) { + public SubgraphImpl addSubgraph(String attributeName) { return super.addSubgraph( attributeName ); } @Override - public Subgraph addSubgraph(String attributeName, Class type) { + public SubgraphImpl addSubgraph(String attributeName, Class type) { return super.addSubgraph( attributeName, type ); } @Override - public Subgraph addKeySubgraph(Attribute attribute) { + public SubgraphImpl addKeySubgraph(Attribute attribute) { return super.addKeySubgraph( attribute ); } @Override - public Subgraph addKeySubgraph(Attribute attribute, Class type) { + public SubgraphImpl addKeySubgraph(Attribute attribute, Class type) { return super.addKeySubgraph( attribute, type ); } @Override - public Subgraph addKeySubgraph(String attributeName) { + public SubgraphImpl addKeySubgraph(String attributeName) { return super.addKeySubgraph( attributeName ); } @Override - public Subgraph addKeySubgraph(String attributeName, Class type) { + public SubgraphImpl addKeySubgraph(String attributeName, Class type) { return super.addKeySubgraph( attributeName, type ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java index 07da96f48b..6d0d26fb1e 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java @@ -27,12 +27,16 @@ import javax.persistence.Cache; import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.persistence.NamedAttributeNode; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedSubgraph; import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceException; import javax.persistence.PersistenceUnitUtil; import javax.persistence.Query; import javax.persistence.SynchronizationType; import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.spi.LoadState; @@ -42,6 +46,7 @@ import java.io.InvalidObjectException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -55,6 +60,7 @@ import org.hibernate.Hibernate; import org.hibernate.SessionFactory; import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.ejb.HibernateEntityManagerFactory; import org.hibernate.engine.spi.NamedQueryDefinition; import org.hibernate.engine.spi.NamedQueryDefinitionBuilder; @@ -64,12 +70,15 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.UUIDGenerator; import org.hibernate.internal.SessionFactoryImpl; +import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.HibernateQuery; import org.hibernate.jpa.boot.internal.SettingsImpl; import org.hibernate.jpa.criteria.CriteriaBuilderImpl; +import org.hibernate.jpa.graph.internal.AbstractGraphNode; import org.hibernate.jpa.graph.internal.EntityGraphImpl; +import org.hibernate.jpa.graph.internal.SubgraphImpl; import org.hibernate.jpa.internal.metamodel.EntityTypeImpl; import org.hibernate.jpa.internal.metamodel.MetamodelImpl; import org.hibernate.jpa.internal.util.PersistenceUtilHelper; @@ -161,7 +170,10 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { entityManagerFactoryName = (String) UUID_GENERATOR.generate(null, null); } this.entityManagerFactoryName = entityManagerFactoryName; - EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory(entityManagerFactoryName, this); + + applyNamedEntityGraphs( cfg.getNamedEntityGraphs() ); + + EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory( entityManagerFactoryName, this ); } private enum JpaMetaModelPopulationSetting { @@ -220,6 +232,68 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { } } + @SuppressWarnings("unchecked") + private void applyNamedEntityGraphs(Collection namedEntityGraphs) { + for ( NamedEntityGraphDefinition definition : namedEntityGraphs ) { + final EntityType entityType = metamodel.getEntityTypeByName( definition.getJpaEntityName() ); + final EntityGraphImpl entityGraph = new EntityGraphImpl( + definition.getRegisteredName(), + entityType, + this + ); + + final NamedEntityGraph namedEntityGraph = definition.getAnnotation(); + + if ( namedEntityGraph.includeAllAttributes() ) { + for ( Object attributeObject : entityType.getAttributes() ) { + entityGraph.addAttributeNodes( (Attribute) attributeObject ); + } + } + + if ( namedEntityGraph.attributeNodes() != null ) { + applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph ); + } + + entityGraphs.put( definition.getRegisteredName(), entityGraph ); + } + } + + private void applyNamedAttributeNodes( + NamedAttributeNode[] namedAttributeNodes, + NamedEntityGraph namedEntityGraph, + AbstractGraphNode graphNode) { + for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) { + if ( StringHelper.isNotEmpty( namedAttributeNode.subgraph() ) ) { + final SubgraphImpl subgraph = graphNode.addSubgraph( namedAttributeNode.value() ); + applyNamedSubgraphs( + namedEntityGraph, + namedAttributeNode.subgraph(), + subgraph + ); + } + if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) { + final SubgraphImpl subgraph = graphNode.addKeySubgraph( namedAttributeNode.value() ); + applyNamedSubgraphs( + namedEntityGraph, + namedAttributeNode.keySubgraph(), + subgraph + ); + } + } + } + + private void applyNamedSubgraphs(NamedEntityGraph namedEntityGraph, String subgraphName, SubgraphImpl subgraph) { + for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) { + if ( subgraphName.equals( namedSubgraph.name() ) ) { + applyNamedAttributeNodes( + namedSubgraph.attributeNodes(), + namedEntityGraph, + subgraph + ); + } + } + } + public EntityManager createEntityManager() { return createEntityManager( Collections.EMPTY_MAP ); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java new file mode 100644 index 0000000000..b6109b4a3a --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/BasicNamedEntityGraphTest.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.jpa.test.graphs.named.basic; + +import javax.persistence.EntityGraph; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +public class BasicNamedEntityGraphTest extends BaseEntityManagerFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class }; + } + + @Test + public void testIt() { + EntityGraph graph = getOrCreateEntityManager().getEntityGraph( "Person" ); + assertNotNull( graph ); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java new file mode 100644 index 0000000000..dc9a55bf35 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/named/basic/Person.java @@ -0,0 +1,38 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.jpa.test.graphs.named.basic; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.NamedEntityGraph; + +/** + * @author Steve Ebersole + */ +@Entity(name = "Person") +@NamedEntityGraph() +public class Person { + @Id + private Long id; +} From 7a1d2de542ac517edb65bb48b8afd8dfe0b02207 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 17 May 2013 14:29:33 -0500 Subject: [PATCH 15/17] HHH-8144 - Create a 'release' task that performs all tasks needed for doing a release --- release/release.gradle | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/release/release.gradle b/release/release.gradle index c0e885cb10..54affb609b 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -13,9 +13,7 @@ final String majorVersion = versionComponents[0]; final String majorMinorVersion = versionComponents[0] + '.' + versionComponents[1]; final File documentationDir = mkdir( new File( project.buildDir, 'documentation' ) ); -final File versionedDocumentationDir = mkdir( new File( new File( documentationDir, "orm" ), majorMinorVersion ) ); -final File javadocDir = mkdir( new File( versionedDocumentationDir, 'javadocs' ) ); - +final File javadocDir = mkdir( new File( documentationDir, 'javadocs' ) ); /** * Builds the JavaDocs aggregated (unified) across all the sub-projects @@ -71,7 +69,6 @@ task aggregateJavadocs(type: Javadoc) { } // apply standard config - description = "Build the aggregated JavaDocs for all modules" maxMemory = '512m' destinationDir = javadocDir configure( options ) { @@ -100,6 +97,9 @@ task aggregateJavadocs(type: Javadoc) { } } +final File documentationUploadStagingDir = mkdir( "${project.buildDir}/tmp/documentation" ); +final File versionedDocumentationDir = mkdir( new File( new File( documentationUploadStagingDir, "orm" ), majorMinorVersion ) ); + /** * Builds the complete documentation set in preparation for upload to the doc server. * @@ -110,13 +110,22 @@ task buildDocumentation(type: Task, dependsOn: [rootProject.project( 'documentat description = "Builds and consolidates all documentation" doLast { + // copy docbook outputs into target/documentation (javadocs are already there). this is used in + // building the dist bundles copy { from "${rootProject.project( 'documentation' ).buildDir}/docbook/publish" + into documentationDir + } + + // now prepare the upload staging directory + versionedDocumentationDir.mkdirs() + copy { + from documentationDir into versionedDocumentationDir } if ( ! version.endsWith( 'SNAPSHOT' ) ) { - final File currentSymLinkContainerDir = new File( documentationDir, 'current' ) + final File currentSymLinkContainerDir = new File( documentationUploadStagingDir, 'stable' ) currentSymLinkContainerDir.mkdirs(); ant.symlink( action: 'delete', @@ -140,7 +149,7 @@ task uploadDocumentation(type:Exec, dependsOn: buildDocumentation) { final String url = 'filemgmt.jboss.org:/docs_htdocs/hibernate/'; executable 'rsync' - args '-rv', '--links', '--protocol=28', "${documentationDir.absolutePath}/", url + args '-rv', '--links', '--protocol=28', "${documentationUploadStagingDir.absolutePath}/", url doFirst { if ( version.endsWith( "SNAPSHOT" ) ) { @@ -209,7 +218,7 @@ distributions { } into('documentation') { - from versionedDocumentationDir + from documentationDir } into( 'project' ) { From 8451c03ea565f8162f241d51496a46f9ed6c9e9b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sat, 18 May 2013 11:39:44 -0500 Subject: [PATCH 16/17] HHH-8211 - Checkstyle and FindBugs fix-ups --- .../org/hibernate/SharedSessionContract.java | 1 - .../internal/RegionFactoryInitiator.java | 16 ++- .../java/org/hibernate/dialect/Dialect.java | 11 +- .../internal/DialectResolverInitiator.java | 4 + .../jdbc/internal/JdbcServicesImpl.java | 37 +++--- .../jdbc/internal/TypeInfoExtracter.java | 105 ------------------ .../jdbc/spi/ConnectionObserverAdapter.java | 2 + .../jdbc/spi/ExtractedDatabaseMetaData.java | 15 ++- .../engine/jdbc/spi/JdbcConnectionAccess.java | 2 + .../engine/jdbc/spi/JdbcCoordinator.java | 18 ++- .../engine/jdbc/spi/JdbcServices.java | 4 +- .../spi/LogicalConnectionImplementor.java | 27 ++++- .../engine/jdbc/spi/ResultSetReturn.java | 73 ++++++------ .../engine/jdbc/spi/SchemaNameResolver.java | 1 + .../engine/jdbc/spi/SqlStatementLogger.java | 6 + .../engine/jdbc/spi/StatementPreparer.java | 20 ++-- .../jdbc/{internal => spi}/TypeInfo.java | 77 ++++++++++++- .../{internal => spi}/TypeNullability.java | 2 +- .../{internal => spi}/TypeSearchability.java | 2 +- .../hibernate/engine/jndi/JndiException.java | 10 +- .../engine/jndi/JndiNameException.java | 10 +- .../engine/jndi/internal/JndiServiceImpl.java | 73 ++++++++++-- .../jndi/internal/JndiServiceInitiator.java | 3 + .../engine/jndi/internal/package-info.java | 4 + .../hibernate/engine/jndi/package-info.java | 4 + .../engine/jndi/spi/package-info.java | 4 + .../internal/util/jndi/JndiHelper.java | 61 +++------- .../common/BasicTestingJdbcServiceImpl.java | 2 +- 28 files changed, 347 insertions(+), 247 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java rename hibernate-core/src/main/java/org/hibernate/engine/jdbc/{internal => spi}/TypeInfo.java (59%) rename hibernate-core/src/main/java/org/hibernate/engine/jdbc/{internal => spi}/TypeNullability.java (98%) rename hibernate-core/src/main/java/org/hibernate/engine/jdbc/{internal => spi}/TypeSearchability.java (98%) create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java create mode 100644 hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index d847c28a4a..b50a8c2477 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -26,7 +26,6 @@ package org.hibernate; import java.io.Serializable; import org.hibernate.procedure.ProcedureCall; -import org.hibernate.procedure.ProcedureCallMemento; /** * Contract methods shared between {@link Session} and {@link StatelessSession}. diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java index d2f7314f1b..7d061f6788 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/RegionFactoryInitiator.java @@ -60,14 +60,20 @@ public class RegionFactoryInitiator implements StandardServiceInitiator regionFactoryClass = registry.getService( StrategySelector.class ) + final Class regionFactoryClass = registry.getService( StrategySelector.class ) .selectStrategyImplementor( RegionFactory.class, setting ); try { regionFactory = regionFactoryClass.getConstructor( Properties.class ).newInstance( p ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 3a863cf289..5119f540ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -283,6 +283,12 @@ public abstract class Dialect implements ConversionContext { // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * Allows the Dialect to contribute additional types + * + * @param typeContributions Callback to contribute the types + * @param serviceRegistry The service registry + */ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { // by default, nothing to do } @@ -1346,7 +1352,7 @@ public abstract class Dialect implements ConversionContext { * @param lockOptions the lock options to apply * @return The appropriate FOR UPDATE OF column_list clause string. */ - @SuppressWarnings( {"unchecked"}) + @SuppressWarnings({"unchecked", "UnusedParameters"}) public String getForUpdateString(String aliases, LockOptions lockOptions) { LockMode lockMode = lockOptions.getLockMode(); final Iterator> itr = lockOptions.getAliasLockIterator(); @@ -1596,6 +1602,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems registering the param. */ + @SuppressWarnings("UnusedParameters") public int registerResultSetOutParameter(CallableStatement statement, String name) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + @@ -1628,6 +1635,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems extracting the result set. */ + @SuppressWarnings("UnusedParameters") public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + " does not support resultsets via stored procedures" @@ -1645,6 +1653,7 @@ public abstract class Dialect implements ConversionContext { * * @throws SQLException Indicates problems extracting the result set. */ + @SuppressWarnings("UnusedParameters") public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException { throw new UnsupportedOperationException( getClass().getName() + " does not support resultsets via stored procedures" diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java index 582d095a13..befec6fe51 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectResolverInitiator.java @@ -40,7 +40,11 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; * * @author Steve Ebersole */ +@SuppressWarnings("deprecation") public class DialectResolverInitiator implements StandardServiceInitiator { + /** + * Singleton access + */ public static final DialectResolverInitiator INSTANCE = new DialectResolverInitiator(); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java index 5fc2418861..c233c790d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java @@ -48,6 +48,7 @@ import org.hibernate.engine.jdbc.spi.ResultSetWrapper; import org.hibernate.engine.jdbc.spi.SchemaNameResolver; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.engine.jdbc.spi.TypeInfo; import org.hibernate.exception.internal.SQLExceptionTypeDelegate; import org.hibernate.exception.internal.SQLStateConversionDelegate; import org.hibernate.exception.internal.StandardSQLExceptionConverter; @@ -122,19 +123,29 @@ public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareServi try { final Connection connection = jdbcConnectionAccess.obtainConnection(); try { - DatabaseMetaData meta = connection.getMetaData(); - if(LOG.isDebugEnabled()) { - LOG.debugf( "Database ->\n" + " name : %s\n" + " version : %s\n" + " major : %s\n" + " minor : %s", - meta.getDatabaseProductName(), - meta.getDatabaseProductVersion(), - meta.getDatabaseMajorVersion(), - meta.getDatabaseMinorVersion() + final DatabaseMetaData meta = connection.getMetaData(); + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Database ->\n" + + " name : %s\n" + + " version : %s\n" + + " major : %s\n" + + " minor : %s", + meta.getDatabaseProductName(), + meta.getDatabaseProductVersion(), + meta.getDatabaseMajorVersion(), + meta.getDatabaseMinorVersion() ); - LOG.debugf( "Driver ->\n" + " name : %s\n" + " version : %s\n" + " major : %s\n" + " minor : %s", - meta.getDriverName(), - meta.getDriverVersion(), - meta.getDriverMajorVersion(), - meta.getDriverMinorVersion() + LOG.debugf( + "Driver ->\n" + + " name : %s\n" + + " version : %s\n" + + " major : %s\n" + + " minor : %s", + meta.getDriverName(), + meta.getDriverVersion(), + meta.getDriverMajorVersion(), + meta.getDriverMinorVersion() ); LOG.debugf( "JDBC version : %s.%s", meta.getJDBCMajorVersion(), meta.getJDBCMinorVersion() ); } @@ -149,7 +160,7 @@ public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareServi extraKeywordsString = meta.getSQLKeywords(); sqlStateType = meta.getSQLStateType(); lobLocatorUpdateCopy = meta.locatorsUpdateCopy(); - typeInfoSet.addAll( TypeInfoExtracter.extractTypeInfo( meta ) ); + typeInfoSet.addAll( TypeInfo.extractTypeInfo( meta ) ); dialect = dialectFactory.buildDialect( configValues, connection ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java deleted file mode 100644 index 8b690c8ea4..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfoExtracter.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.engine.jdbc.internal; - -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.LinkedHashSet; - -import org.jboss.logging.Logger; - -import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.collections.ArrayHelper; - -/** - * Helper to extract type information from {@link DatabaseMetaData JDBC metadata} - * - * @author Steve Ebersole - */ -public class TypeInfoExtracter { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, TypeInfoExtracter.class.getName()); - - private TypeInfoExtracter() { - } - - /** - * Perform the extraction - * - * @param metaData The JDBC metadata - * - * @return The extracted metadata - */ - public static LinkedHashSet extractTypeInfo(DatabaseMetaData metaData) { - LinkedHashSet typeInfoSet = new LinkedHashSet(); - try { - ResultSet resultSet = metaData.getTypeInfo(); - try { - while ( resultSet.next() ) { - typeInfoSet.add( - new TypeInfo( - resultSet.getString( "TYPE_NAME" ), - resultSet.getInt( "DATA_TYPE" ), - interpretCreateParams( resultSet.getString( "CREATE_PARAMS" ) ), - resultSet.getBoolean( "UNSIGNED_ATTRIBUTE" ), - resultSet.getInt( "PRECISION" ), - resultSet.getShort( "MINIMUM_SCALE" ), - resultSet.getShort( "MAXIMUM_SCALE" ), - resultSet.getBoolean( "FIXED_PREC_SCALE" ), - resultSet.getString( "LITERAL_PREFIX" ), - resultSet.getString( "LITERAL_SUFFIX" ), - resultSet.getBoolean( "CASE_SENSITIVE" ), - TypeSearchability.interpret( resultSet.getShort( "SEARCHABLE" ) ), - TypeNullability.interpret( resultSet.getShort( "NULLABLE" ) ) - ) - ); - } - } - catch ( SQLException e ) { - LOG.unableToAccessTypeInfoResultSet( e.toString() ); - } - finally { - try { - resultSet.close(); - } - catch ( SQLException e ) { - LOG.unableToReleaseTypeInfoResultSet(); - } - } - } - catch ( SQLException e ) { - LOG.unableToRetrieveTypeInfoResultSet( e.toString() ); - } - - return typeInfoSet; - } - - private static String[] interpretCreateParams(String value) { - if ( value == null || value.length() == 0 ) { - return ArrayHelper.EMPTY_STRING_ARRAY; - } - return value.split( "," ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java index 32408525b2..0682794954 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionObserverAdapter.java @@ -26,6 +26,8 @@ package org.hibernate.engine.jdbc.spi; import java.sql.Connection; /** + * A no-op adapter for ConnectionObserver. + * * @author Steve Ebersole */ public class ConnectionObserverAdapter implements ConnectionObserver { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ExtractedDatabaseMetaData.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ExtractedDatabaseMetaData.java index df2b6c699f..f33c096e13 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ExtractedDatabaseMetaData.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ExtractedDatabaseMetaData.java @@ -26,8 +26,6 @@ package org.hibernate.engine.jdbc.spi; import java.util.LinkedHashSet; import java.util.Set; -import org.hibernate.engine.jdbc.internal.TypeInfo; - /** * Information extracted from {@link java.sql.DatabaseMetaData} regarding what the JDBC driver reports as * being supported or not. Obviously {@link java.sql.DatabaseMetaData} reports many things, these are a few in @@ -37,10 +35,21 @@ import org.hibernate.engine.jdbc.internal.TypeInfo; */ @SuppressWarnings( {"UnusedDeclaration"}) public interface ExtractedDatabaseMetaData { - + /** + * Which specification do the reported SQLState codes follow? + */ public enum SQLStateType { + /** + * The reported codes follow the X/Open spec + */ XOpen, + /** + * The reported codes follow the SQL spec + */ SQL99, + /** + * It is unknown. Might follow another spec completely, or be a mixture. + */ UNKOWN } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java index cb19212967..654731310d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcConnectionAccess.java @@ -56,6 +56,8 @@ public interface JdbcConnectionAccess extends Serializable { * Does the underlying provider of connections support aggressive releasing of connections (and re-acquisition * of those connections later, if need be) in JTA environments? * + * @return true/false + * * @see org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#supportsAggressiveRelease() * @see org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider#supportsAggressiveRelease() */ diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java index e9c8a48d45..fa0d16e6a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java @@ -153,7 +153,8 @@ public interface JdbcCoordinator extends Serializable { * * @throws org.hibernate.TransactionException Indicates the time out period has already been exceeded. */ - public int determineRemainingTransactionTimeOutPeriod(); + public int determineRemainingTransactionTimeOutPeriod(); + /** * Register a JDBC statement. * @@ -199,9 +200,15 @@ public interface JdbcCoordinator extends Serializable { * Release all registered resources. */ public void releaseResources(); - + + /** + * Enable connection releases + */ public void enableReleases(); - + + /** + * Disable connection releases + */ public void disableReleases(); /** @@ -211,5 +218,10 @@ public interface JdbcCoordinator extends Serializable { */ public void registerLastQuery(Statement statement); + /** + * Can this coordinator be serialized? + * + * @return {@code true} indicates the coordinator can be serialized. + */ public boolean isReadyForSerialization(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java index 1580733fcb..0be6e893cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java @@ -23,8 +23,6 @@ */ package org.hibernate.engine.jdbc.spi; -import java.sql.ResultSet; - import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreator; @@ -88,7 +86,7 @@ public interface JdbcServices extends Service { public LobCreator getLobCreator(LobCreationContext lobCreationContext); /** - * Obtain service for wrapping a {@link ResultSet} in a "column name cache" wrapper. + * Obtain service for wrapping a {@link java.sql.ResultSet} in a "column name cache" wrapper. * @return The ResultSet wrapper. */ public ResultSetWrapper getResultSetWrapper(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java index 7d442e7616..6d7695503f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/LogicalConnectionImplementor.java @@ -78,14 +78,35 @@ public interface LogicalConnectionImplementor extends LogicalConnection { * with which to reconnect. It is an error to pass a connection in the other strategies. */ public void manualReconnect(Connection suppliedConnection); - + + /** + * Perform an aggressive release + */ public void aggressiveRelease(); - + + /** + * Release any held connection. + * + * @throws JDBCException Indicates a problem releasing the connection + */ public void releaseConnection() throws JDBCException; + /** + * Is this logical connection in auto-commit mode? + * + * @return {@code true} if auto-commit + */ public boolean isAutoCommit(); + /** + * Callback to notify all registered observers of a connection being prepared. + */ public void notifyObserversStatementPrepared(); - + + /** + * Does this logical connection wrap a user/application supplied connection? + * + * @return {@code true} if the underlying connection was user supplied. + */ public boolean isUserSuppliedConnection(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java index 2dbcc27ed1..af47caf49b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java @@ -34,75 +34,84 @@ import java.sql.Statement; * * TODO: This could eventually utilize the new Return interface. It would be * great to have a common API shared. + * + * Generally the methods here dealing with CallableStatement are extremely limited, relying on the legacy + * * * @author Brett Meyer + * @author Steve Ebersole */ public interface ResultSetReturn { /** - * Extract the ResultSet from the statement. If user passes {@link CallableStatement} - * reference, method calls {@link #extract(CallableStatement)} internally. + * Extract the ResultSet from the PreparedStatement. + *

+ * If user passes {@link CallableStatement} reference, this method calls {@link #extract(CallableStatement)} + * internally. Otherwise, generally speaking, {@link java.sql.PreparedStatement#executeQuery()} is called * - * @param statement + * @param statement The PreparedStatement from which to extract the ResultSet * - * @return the ResultSet + * @return The extracted ResultSet */ - public ResultSet extract( PreparedStatement statement ); + public ResultSet extract(PreparedStatement statement); /** - * Extract the ResultSet from the statement. + * Extract the ResultSet from the CallableStatement. Note that this is the limited legacy form which delegates to + * {@link org.hibernate.dialect.Dialect#getResultSet}. Better option is to integrate + * {@link org.hibernate.procedure.ProcedureCall}-like hooks * - * @param statement + * @param callableStatement The CallableStatement from which to extract the ResultSet * - * @return the ResultSet + * @return The extracted ResultSet */ - public ResultSet extract( CallableStatement statement ); + public ResultSet extract(CallableStatement callableStatement); /** - * Extract the ResultSet from the statement. + * Performs the given SQL statement, expecting a ResultSet in return * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The resulting ResultSet */ - public ResultSet extract( Statement statement, String sql ); + public ResultSet extract(Statement statement, String sql); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Execute the PreparedStatement return its first ResultSet, if any. If there is no ResultSet, returns {@code null} * - * @param statement + * @param statement The PreparedStatement to execute * - * @return the ResultSet + * @return The extracted ResultSet, or {@code null} */ - public ResultSet execute( PreparedStatement statement ); + public ResultSet execute(PreparedStatement statement); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Performs the given SQL statement, returning its first ResultSet, if any. If there is no ResultSet, + * returns {@code null} * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The extracted ResultSet, or {@code null} */ - public ResultSet execute( Statement statement, String sql ); + public ResultSet execute(Statement statement, String sql); /** - * Execute the Statement queryUpdate. + * Execute the PreparedStatement, returning its "affected row count". * - * @param statement + * @param statement The PreparedStatement to execute * - * @return int + * @return The {@link java.sql.PreparedStatement#executeUpdate()} result */ - public int executeUpdate( PreparedStatement statement ); + public int executeUpdate(PreparedStatement statement); /** - * Execute the Statement query and, if results in a ResultSet, extract it. + * Execute the given SQL statement returning its "affected row count". * - * @param statement - * @param sql + * @param statement The JDBC Statement object to use + * @param sql The SQL to execute * - * @return the ResultSet + * @return The {@link java.sql.PreparedStatement#executeUpdate(String)} result */ - public int executeUpdate( Statement statement, String sql ); + public int executeUpdate(Statement statement, String sql); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SchemaNameResolver.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SchemaNameResolver.java index 096d3e75e8..746c2d359e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SchemaNameResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SchemaNameResolver.java @@ -22,6 +22,7 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.engine.jdbc.spi; + import java.sql.Connection; /** diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java index a7acef85f8..ea9a2edf57 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/SqlStatementLogger.java @@ -95,6 +95,12 @@ public class SqlStatementLogger { logStatement( statement, FormatStyle.BASIC.getFormatter() ); } + /** + * Log a SQL statement string using the specified formatter + * + * @param statement The SQL statement. + * @param formatter The formatter to use. + */ public void logStatement(String statement, Formatter formatter) { if ( format ) { if ( logToStdout || LOG.isDebugEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java index bcefd9daa0..5c18468c73 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/StatementPreparer.java @@ -38,8 +38,6 @@ public interface StatementPreparer { /** * Create a statement. * - * @param sql The SQL the statement to be created - * * @return the statement */ public Statement createStatement(); @@ -64,14 +62,15 @@ public interface StatementPreparer { public PreparedStatement prepareStatement(String sql, boolean isCallable); /** - * Get a prepared statement to use for inserting using JDBC3 - * {@link java.sql.PreparedStatement#getGeneratedKeys getGeneratedKeys} processing. - * - * @param sql - the SQL for the statement to be prepared - * @param autoGeneratedKeys - a flag indicating whether auto-generated keys should be returned; one of

    + * Prepare an INSERT statement, specifying how auto-generated (by the database) keys should be handled. Really this + * is a boolean, but JDBC opted to define it instead using 2 int constants:
      *
    • {@link PreparedStatement#RETURN_GENERATED_KEYS}
    • *
    • {@link PreparedStatement#NO_GENERATED_KEYS}
    • - * + *
    + * Generated keys are accessed afterwards via {@link java.sql.PreparedStatement#getGeneratedKeys} + * + * @param sql The INSERT SQL + * @param autoGeneratedKeys The autoGeneratedKeys flag * * @return the prepared statement * @@ -79,10 +78,9 @@ public interface StatementPreparer { */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys); - /** - * Get a prepared statement to use for inserting using JDBC3 - * {@link java.sql.PreparedStatement#getGeneratedKeys getGeneratedKeys} processing. + * Prepare an INSERT statement, specifying columns which are auto-generated values to be returned. + * Generated keys are accessed afterwards via {@link java.sql.PreparedStatement#getGeneratedKeys} * * @param sql - the SQL for the statement to be prepared * @param columnNames The name of the columns to be returned in the generated keys result set. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java similarity index 59% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java index c87f1b0e7d..efcbc4e8c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeInfo.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeInfo.java @@ -21,15 +21,30 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedHashSet; + +import org.jboss.logging.Logger; + +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.collections.ArrayHelper; /** * Models type info extracted from {@link java.sql.DatabaseMetaData#getTypeInfo()} * * @author Steve Ebersole */ +@SuppressWarnings("UnusedDeclaration") public class TypeInfo { + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + TypeInfo.class.getName() + ); + private final String typeName; private final int jdbcTypeCode; private final String[] createParams; @@ -44,7 +59,7 @@ public class TypeInfo { private final TypeSearchability searchability; private final TypeNullability nullability; - public TypeInfo( + private TypeInfo( String typeName, int jdbcTypeCode, String[] createParams, @@ -73,6 +88,64 @@ public class TypeInfo { this.nullability = nullability; } + /** + * Extract the type information from the JDBC driver's DatabaseMetaData + * + * @param metaData The JDBC metadata + * + * @return The extracted type info + */ + public static LinkedHashSet extractTypeInfo(DatabaseMetaData metaData) { + final LinkedHashSet typeInfoSet = new LinkedHashSet(); + try { + final ResultSet resultSet = metaData.getTypeInfo(); + try { + while ( resultSet.next() ) { + typeInfoSet.add( + new TypeInfo( + resultSet.getString( "TYPE_NAME" ), + resultSet.getInt( "DATA_TYPE" ), + interpretCreateParams( resultSet.getString( "CREATE_PARAMS" ) ), + resultSet.getBoolean( "UNSIGNED_ATTRIBUTE" ), + resultSet.getInt( "PRECISION" ), + resultSet.getShort( "MINIMUM_SCALE" ), + resultSet.getShort( "MAXIMUM_SCALE" ), + resultSet.getBoolean( "FIXED_PREC_SCALE" ), + resultSet.getString( "LITERAL_PREFIX" ), + resultSet.getString( "LITERAL_SUFFIX" ), + resultSet.getBoolean( "CASE_SENSITIVE" ), + TypeSearchability.interpret( resultSet.getShort( "SEARCHABLE" ) ), + TypeNullability.interpret( resultSet.getShort( "NULLABLE" ) ) + ) + ); + } + } + catch ( SQLException e ) { + LOG.unableToAccessTypeInfoResultSet( e.toString() ); + } + finally { + try { + resultSet.close(); + } + catch ( SQLException e ) { + LOG.unableToReleaseTypeInfoResultSet(); + } + } + } + catch ( SQLException e ) { + LOG.unableToRetrieveTypeInfoResultSet( e.toString() ); + } + + return typeInfoSet; + } + + private static String[] interpretCreateParams(String value) { + if ( value == null || value.length() == 0 ) { + return ArrayHelper.EMPTY_STRING_ARRAY; + } + return value.split( "," ); + } + public String getTypeName() { return typeName; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java similarity index 98% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java index cdbfb04e7c..5f639117de 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeNullability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeNullability.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; import java.sql.DatabaseMetaData; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java similarity index 98% rename from hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java rename to hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java index 96aef2e42d..1df1573a0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/TypeSearchability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/TypeSearchability.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.jdbc.internal; +package org.hibernate.engine.jdbc.spi; import java.sql.DatabaseMetaData; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java index b7047ddbc1..9ab995ec4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiException.java @@ -30,7 +30,13 @@ import org.hibernate.HibernateException; * @author Steve Ebersole */ public class JndiException extends HibernateException { - public JndiException(String string, Throwable root) { - super( string, root ); + /** + * Constructs a JndiException + * + * @param message Message explaining the exception condition + * @param cause The underlying cause + */ + public JndiException(String message, Throwable cause) { + super( message, cause ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java index 6114c6dbfa..90a52148db 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/JndiNameException.java @@ -31,7 +31,13 @@ import org.hibernate.HibernateException; * @author Steve Ebersole */ public class JndiNameException extends HibernateException { - public JndiNameException(String string, Throwable root) { - super( string, root ); + /** + * Constructs a JndiNameException + * + * @param message Message explaining the exception condition + * @param cause The underlying cause. + */ + public JndiNameException(String message, Throwable cause) { + super( message, cause ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java index 86a0882450..1e64bd4697 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceImpl.java @@ -25,6 +25,8 @@ package org.hibernate.engine.jndi.internal; import java.util.Hashtable; import java.util.Map; +import java.util.Properties; +import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.InvalidNameException; @@ -36,8 +38,8 @@ import javax.naming.event.NamespaceChangeListener; import org.jboss.logging.Logger; +import org.hibernate.cfg.Environment; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.jndi.JndiHelper; import org.hibernate.engine.jndi.JndiException; import org.hibernate.engine.jndi.JndiNameException; import org.hibernate.engine.jndi.spi.JndiService; @@ -48,19 +50,66 @@ import org.hibernate.engine.jndi.spi.JndiService; * @author Steve Ebersole */ public class JndiServiceImpl implements JndiService { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JndiServiceImpl.class.getName()); + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + JndiServiceImpl.class.getName() + ); private final Hashtable initialContextSettings; + /** + * Constructs a JndiServiceImpl + * + * @param configurationValues Map of configuration settings, some of which apply to JNDI support. + */ public JndiServiceImpl(Map configurationValues) { - this.initialContextSettings = JndiHelper.extractJndiProperties( configurationValues ); + this.initialContextSettings = extractJndiProperties( configurationValues ); + } + + /** + * Given a hodgepodge of properties, extract out the ones relevant for JNDI interaction. + * + * @param configurationValues The map of config values + * + * @return The extracted JNDI specific properties. + */ + @SuppressWarnings({ "unchecked" }) + public static Properties extractJndiProperties(Map configurationValues) { + final Properties jndiProperties = new Properties(); + + for ( Map.Entry entry : (Set) configurationValues.entrySet() ) { + if ( !String.class.isInstance( entry.getKey() ) ) { + continue; + } + final String propertyName = (String) entry.getKey(); + final Object propertyValue = entry.getValue(); + if ( propertyName.startsWith( Environment.JNDI_PREFIX ) ) { + // write the IntialContextFactory class and provider url to the result only if they are + // non-null; this allows the environmental defaults (if any) to remain in effect + if ( Environment.JNDI_CLASS.equals( propertyName ) ) { + if ( propertyValue != null ) { + jndiProperties.put( Context.INITIAL_CONTEXT_FACTORY, propertyValue ); + } + } + else if ( Environment.JNDI_URL.equals( propertyName ) ) { + if ( propertyValue != null ) { + jndiProperties.put( Context.PROVIDER_URL, propertyValue ); + } + } + else { + final String passThruPropertyname = propertyName.substring( Environment.JNDI_PREFIX.length() + 1 ); + jndiProperties.put( passThruPropertyname, propertyValue ); + } + } + } + + return jndiProperties; } @Override public Object locate(String jndiName) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { return initialContext.lookup( name ); } @@ -104,8 +153,8 @@ public class JndiServiceImpl implements JndiService { @Override public void bind(String jndiName, Object value) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { bind( name, value, initialContext ); } @@ -172,8 +221,8 @@ public class JndiServiceImpl implements JndiService { @Override public void unbind(String jndiName) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { initialContext.unbind( name ); } @@ -187,8 +236,8 @@ public class JndiServiceImpl implements JndiService { @Override public void addListener(String jndiName, NamespaceChangeListener listener) { - InitialContext initialContext = buildInitialContext(); - Name name = parseName( jndiName, initialContext ); + final InitialContext initialContext = buildInitialContext(); + final Name name = parseName( jndiName, initialContext ); try { ( (EventContext) initialContext ).addNamingListener( name, EventContext.OBJECT_SCOPE, listener ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java index 3280c04b26..7836604507 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/JndiServiceInitiator.java @@ -35,6 +35,9 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; * @author Steve Ebersole */ public class JndiServiceInitiator implements StandardServiceInitiator { + /** + * Singleton access + */ public static final JndiServiceInitiator INSTANCE = new JndiServiceInitiator(); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java new file mode 100644 index 0000000000..5e4df30b72 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/internal/package-info.java @@ -0,0 +1,4 @@ +/** + * Internal contracts defining the JNDI support within Hibernate + */ +package org.hibernate.engine.jndi.internal; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java new file mode 100644 index 0000000000..4e5148287a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/package-info.java @@ -0,0 +1,4 @@ +/** + * Support for JNDI within Hibernate + */ +package org.hibernate.engine.jndi; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java b/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java new file mode 100644 index 0000000000..cb001f7500 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jndi/spi/package-info.java @@ -0,0 +1,4 @@ +/** + * The SPI contracts for Hibernate JNDI support + */ +package org.hibernate.engine.jndi.spi; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java index a93b83df39..38b9faae19 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/jndi/JndiHelper.java @@ -23,17 +23,16 @@ */ package org.hibernate.internal.util.jndi; -import java.util.Hashtable; -import java.util.Map; -import java.util.Properties; -import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.NameNotFoundException; import javax.naming.NamingException; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; -import org.hibernate.cfg.Environment; +import org.hibernate.engine.jndi.internal.JndiServiceImpl; /** * Helper for dealing with JNDI. @@ -54,42 +53,12 @@ public final class JndiHelper { */ @SuppressWarnings({ "unchecked" }) public static Properties extractJndiProperties(Map configurationValues) { - final Properties jndiProperties = new Properties(); - - for ( Map.Entry entry : (Set) configurationValues.entrySet() ) { - if ( !String.class.isInstance( entry.getKey() ) ) { - continue; - } - final String propertyName = (String) entry.getKey(); - final Object propertyValue = entry.getValue(); - if ( propertyName.startsWith( Environment.JNDI_PREFIX ) ) { - // write the IntialContextFactory class and provider url to the result only if they are - // non-null; this allows the environmental defaults (if any) to remain in effect - if ( Environment.JNDI_CLASS.equals( propertyName ) ) { - if ( propertyValue != null ) { - jndiProperties.put( Context.INITIAL_CONTEXT_FACTORY, propertyValue ); - } - } - else if ( Environment.JNDI_URL.equals( propertyName ) ) { - if ( propertyValue != null ) { - jndiProperties.put( Context.PROVIDER_URL, propertyValue ); - } - } - else { - final String passThruPropertyname = propertyName.substring( Environment.JNDI_PREFIX.length() + 1 ); - jndiProperties.put( passThruPropertyname, propertyValue ); - } - } - } - - return jndiProperties; + return JndiServiceImpl.extractJndiProperties( configurationValues ); } public static InitialContext getInitialContext(Properties props) throws NamingException { - Hashtable hash = extractJndiProperties(props); - return hash.size()==0 ? - new InitialContext() : - new InitialContext(hash); + final Hashtable hash = extractJndiProperties( props ); + return hash.size() == 0 ? new InitialContext() : new InitialContext( hash ); } /** @@ -106,26 +75,26 @@ public final class JndiHelper { ctx.rebind(name, val); } catch (Exception e) { - Name n = ctx.getNameParser("").parse(name); + Name n = ctx.getNameParser( "" ).parse( name ); while ( n.size() > 1 ) { - String ctxName = n.get(0); + final String ctxName = n.get( 0 ); - Context subctx=null; + Context subctx = null; try { - subctx = (Context) ctx.lookup(ctxName); + subctx = (Context) ctx.lookup( ctxName ); } catch (NameNotFoundException ignore) { } - if (subctx!=null) { + if ( subctx != null ) { ctx = subctx; } else { - ctx = ctx.createSubcontext(ctxName); + ctx = ctx.createSubcontext( ctxName ); } - n = n.getSuffix(1); + n = n.getSuffix( 1 ); } - ctx.rebind(n, val); + ctx.rebind( n, val ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java b/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java index c8e73cba51..22c7e77ea0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java +++ b/hibernate-core/src/test/java/org/hibernate/test/common/BasicTestingJdbcServiceImpl.java @@ -31,7 +31,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.internal.ResultSetWrapperImpl; -import org.hibernate.engine.jdbc.internal.TypeInfo; +import org.hibernate.engine.jdbc.spi.TypeInfo; import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.ResultSetWrapper; From 45d46b619b177e613afede3c2ec00a5cb7fe27bd Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Mon, 20 May 2013 11:18:46 -0400 Subject: [PATCH 17/17] HHH-6813 Corrected EntityType#getRHSUniqueKeyPropertyName() and added regression test. --- .../java/org/hibernate/type/EntityType.java | 4 +- .../OneToOneWithDerivedIdentityTest.java | 44 ++++++++++- .../bidirectional/Person.java | 77 +++++++++++++++++++ .../bidirectional/PersonInfo.java | 60 +++++++++++++++ 4 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java diff --git a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java index f6ae0946cf..6f873d2cfb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java @@ -200,7 +200,9 @@ public abstract class EntityType extends AbstractType implements AssociationType } public String getRHSUniqueKeyPropertyName() { - return uniqueKeyPropertyName; + // Return null if this type references a PK. This is important for + // associations' use of mappedBy referring to a derived ID. + return referenceToPrimaryKey ? null : uniqueKeyPropertyName; } public String getLHSPropertyName() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java index a63ca1e92e..1b35c15bc8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/OneToOneWithDerivedIdentityTest.java @@ -26,6 +26,9 @@ package org.hibernate.test.annotations.derivedidentities.bidirectional; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.util.List; + +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -80,11 +83,50 @@ public class OneToOneWithDerivedIdentityTest extends BaseCoreFunctionalTestCase s.close(); } + @Test + @TestForIssue(jiraKey = "HHH-6813") + // Regression test utilizing multiple types of queries. + public void testCase() { + Session s = openSession(); + s.getTransaction().begin(); + + Person p = new Person(); + p.setName( "Alfio" ); + PersonInfo pi = new PersonInfo(); + pi.setId( p ); + pi.setInfo( "Some information" ); + s.persist( p ); + s.persist( pi ); + + s.getTransaction().commit(); + s.clear(); + + s.getTransaction().begin(); + + Query q = s.getNamedQuery( "PersonQuery" ); + List persons = q.list(); + assertEquals( persons.size(), 1 ); + assertEquals( persons.get( 0 ).getName(), "Alfio" ); + + s.getTransaction().commit(); + s.clear(); + + s.getTransaction().begin(); + + p = (Person) s.get( Person.class, persons.get( 0 ).getId() ); + assertEquals( p.getName(), "Alfio" ); + + s.getTransaction().commit(); + s.close(); + } + @Override protected Class[] getAnnotatedClasses() { return new Class[] { Foo.class, - Bar.class + Bar.class, + Person.class, + PersonInfo.class }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java new file mode 100644 index 0000000000..77a0727489 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/Person.java @@ -0,0 +1,77 @@ +package org.hibernate.test.annotations.derivedidentities.bidirectional; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.NamedQuery; +import javax.persistence.OneToOne; + +@Entity +@NamedQuery(name="PersonQuery", query="SELECT p FROM Person p") +public class Person + implements Serializable +{ + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private Integer id; + + @Basic + private String name; + + @OneToOne(mappedBy="id") + private PersonInfo personInfo; + + public Integer getId() + { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int hashCode() + { + int hash = 0; + hash += (this.id != null ? this.id.hashCode() : 0); + return hash; + } + + public boolean equals(Object object) + { + if (!(object instanceof Person)) { + return false; + } + Person other = (Person)object; + + return ((this.id != null) || (other.id == null)) && ((this.id == null) || (this.id.equals(other.id))); + } + + public String toString() + { + return "nogroup.hibertest.Person[ id=" + this.id + " ]"; + } + + public PersonInfo getPersonInfo() + { + return this.personInfo; + } + + public void setPersonInfo(PersonInfo personInfo) + { + this.personInfo = personInfo; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java new file mode 100644 index 0000000000..08a1ff9714 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/derivedidentities/bidirectional/PersonInfo.java @@ -0,0 +1,60 @@ +package org.hibernate.test.annotations.derivedidentities.bidirectional; + +import java.io.Serializable; +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +@Entity +public class PersonInfo + implements Serializable +{ + private static final long serialVersionUID = 1L; + + @Id + @OneToOne + private Person id; + + @Basic + private String info; + + public Person getId() + { + return this.id; + } + + public void setId(Person id) { + this.id = id; + } + + public String getInfo() { + return this.info; + } + + public void setInfo(String info) { + this.info = info; + } + + public int hashCode() + { + int hash = 0; + hash += (this.id != null ? this.id.hashCode() : 0); + return hash; + } + + public boolean equals(Object object) + { + if (!(object instanceof PersonInfo)) { + return false; + } + PersonInfo other = (PersonInfo)object; + + return ((this.id != null) || (other.id == null)) && ((this.id == null) || (this.id.equals(other.id))); + } + + public String toString() + { + return "nogroup.hibertest.PersonInfo[ id=" + this.id + " ]"; + } +} \ No newline at end of file