diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/criteria/expression/function/AggregationFunction.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/criteria/expression/function/AggregationFunction.java index 63495255ee..ce3defc30d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/criteria/expression/function/AggregationFunction.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/criteria/expression/function/AggregationFunction.java @@ -23,7 +23,9 @@ */ package org.hibernate.ejb.criteria.expression.function; import java.io.Serializable; +import java.util.List; import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Root; import org.hibernate.ejb.criteria.CriteriaBuilderImpl; import org.hibernate.ejb.criteria.CriteriaQueryCompiler; import org.hibernate.ejb.criteria.expression.LiteralExpression; @@ -92,11 +94,27 @@ public class AggregationFunction } @Override - protected void renderArguments(StringBuilder buffer, CriteriaQueryCompiler.RenderingContext renderingContext) { - if ( isDistinct() ) { - buffer.append( "distinct " ); + protected void renderArguments( StringBuilder buffer, + CriteriaQueryCompiler.RenderingContext renderingContext ) { + if (isDistinct()) buffer.append("distinct "); + else { + // If function specifies a single non-distinct entity with ID, its alias would normally be rendered, which ends up + // converting to the column(s) associated with the entity's ID in the rendered SQL. However, some DBs don't support + // the multiple columns that would end up here for entities with composite IDs. So, since we modify the query to + // instead specify star since that's functionally equivalent and supported by all DBs. + List> argExprs = getArgumentExpressions(); + if (argExprs.size() == 1) { + Expression argExpr = argExprs.get(0); + if (argExpr instanceof Root) { + Root root = (Root)argExpr; + if (root.getModel().getIdType() != null) { + buffer.append('*'); + return; + } + } + } } - super.renderArguments( buffer, renderingContext ); + super.renderArguments(buffer, renderingContext); } public boolean isDistinct() { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CompositeId.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CompositeId.java new file mode 100644 index 0000000000..86f0967278 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CompositeId.java @@ -0,0 +1,48 @@ +package org.hibernate.ejb.test; + +import java.io.Serializable; +import javax.persistence.Embeddable; + +/** + * + */ +@Embeddable +public class CompositeId implements Serializable { + + private int id1; + private int id2; + + public int getId1() { + return id1; + } + + public void setId1( int id1 ) { + this.id1 = id1; + } + + public int getId2() { + return id2; + } + + public void setId2( int id2 ) { + this.id2 = id2; + } + + @Override + public boolean equals( Object obj ) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final CompositeId other = (CompositeId)obj; + if (this.id1 != other.id1) return false; + if (this.id2 != other.id2) return false; + return true; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 73 * hash + this.id1; + hash = 73 * hash + this.id2; + return hash; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CountEntityWithCompositeIdTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CountEntityWithCompositeIdTest.java new file mode 100644 index 0000000000..544dbd285c --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/CountEntityWithCompositeIdTest.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, 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.ejb.test; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.core.Is.is; +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import org.junit.Test; + +/** + * + */ +public class CountEntityWithCompositeIdTest extends BaseEntityManagerFunctionalTestCase { + + @Test + public void shouldCount() { + EntityManager em = getOrCreateEntityManager(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root r = cq.from(EntityWithCompositeId.class); + cq.multiselect(cb.count(r)); + assertThat(em.createQuery(cq).getSingleResult().intValue(), is(0)); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {EntityWithCompositeId.class, CompositeId.class}; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/EntityWithCompositeId.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/EntityWithCompositeId.java new file mode 100644 index 0000000000..f0fa54f8f7 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/EntityWithCompositeId.java @@ -0,0 +1,32 @@ +package org.hibernate.ejb.test; + +import java.io.Serializable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +/** + * + */ +@Entity +public class EntityWithCompositeId implements Serializable { + + @EmbeddedId + private CompositeId id; + private String description; + + public CompositeId getId() { + return id; + } + + public void setId(CompositeId id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +}