diff --git a/core/src/main/java/org/hibernate/criterion/AliasedProjection.java b/core/src/main/java/org/hibernate/criterion/AliasedProjection.java index fff191eb18..889a64e356 100755 --- a/core/src/main/java/org/hibernate/criterion/AliasedProjection.java +++ b/core/src/main/java/org/hibernate/criterion/AliasedProjection.java @@ -31,7 +31,7 @@ import org.hibernate.type.Type; /** * @author Gavin King */ -public class AliasedProjection implements Projection { +public class AliasedProjection implements EnhancedProjection { private final Projection projection; private final String alias; @@ -64,7 +64,13 @@ public class AliasedProjection implements Projection { return projection.getColumnAliases(loc); } - public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery) + public String[] getColumnAliases(int loc, Criteria criteria, CriteriaQuery criteriaQuery) { + return projection instanceof EnhancedProjection ? + ( ( EnhancedProjection ) projection ).getColumnAliases( loc, criteria, criteriaQuery ) : + getColumnAliases( loc ); + } + + public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { return this.alias.equals(alias) ? getTypes(criteria, criteriaQuery) : @@ -77,6 +83,12 @@ public class AliasedProjection implements Projection { null; } + public String[] getColumnAliases(String alias, int loc, Criteria criteria, CriteriaQuery criteriaQuery) { + return this.alias.equals(alias) ? + getColumnAliases( loc, criteria, criteriaQuery ) : + null; + } + public String[] getAliases() { return new String[]{ alias }; } diff --git a/core/src/main/java/org/hibernate/criterion/Distinct.java b/core/src/main/java/org/hibernate/criterion/Distinct.java index 9522c3e6f3..57c8ef0917 100755 --- a/core/src/main/java/org/hibernate/criterion/Distinct.java +++ b/core/src/main/java/org/hibernate/criterion/Distinct.java @@ -31,7 +31,7 @@ import org.hibernate.type.Type; /** * @author Gavin King */ -public class Distinct implements Projection { +public class Distinct implements EnhancedProjection { private final Projection projection; @@ -63,10 +63,22 @@ public class Distinct implements Projection { return projection.getColumnAliases(loc); } + public String[] getColumnAliases(int loc, Criteria criteria, CriteriaQuery criteriaQuery) { + return projection instanceof EnhancedProjection ? + ( ( EnhancedProjection ) projection ).getColumnAliases( loc, criteria, criteriaQuery ) : + getColumnAliases( loc ); + } + public String[] getColumnAliases(String alias, int loc) { return projection.getColumnAliases(alias, loc); } + public String[] getColumnAliases(String alias, int loc, Criteria criteria, CriteriaQuery criteriaQuery) { + return projection instanceof EnhancedProjection ? + ( ( EnhancedProjection ) projection ).getColumnAliases( alias, loc, criteria, criteriaQuery ) : + getColumnAliases( alias, loc ); + } + public String[] getAliases() { return projection.getAliases(); } diff --git a/core/src/main/java/org/hibernate/criterion/EnhancedProjection.java b/core/src/main/java/org/hibernate/criterion/EnhancedProjection.java new file mode 100644 index 0000000000..9c9be70ebe --- /dev/null +++ b/core/src/main/java/org/hibernate/criterion/EnhancedProjection.java @@ -0,0 +1,70 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. + * + * 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.criterion; + + +import java.io.Serializable; + +import org.hibernate.Criteria; +import org.hibernate.HibernateException; +import org.hibernate.type.Type; + +/** + * An "enhanced" Projection for a {@link Criteria} query. + * + * @author Gail Badner + * @see Projection + * @see Criteria + */ +public interface EnhancedProjection extends Projection { + + /** + * Get the SQL column aliases used by this projection for the columns it writes for inclusion into the + * SELECT clause ({@link #toSqlString}. Hibernate always uses column aliases to extract data from the + * JDBC {@link java.sql.ResultSet}, so it is important that these be implemented correctly in order for + * Hibernate to be able to extract these val;ues correctly. + * + * @param position Just as in {@link #toSqlString}, represents the number of columns rendered + * prior to this projection. + * @param criteria The local criteria to which this project is attached (for resolution). + * @param criteriaQuery The overall criteria query instance. + * @return The columns aliases. + */ + public String[] getColumnAliases(int position, Criteria criteria, CriteriaQuery criteriaQuery); + + /** + * Get the SQL column aliases used by this projection for the columns it writes for inclusion into the + * SELECT clause ({@link #toSqlString} for a particular criteria-level alias. + * + * @param alias The criteria-level alias + * @param position Just as in {@link #toSqlString}, represents the number of columns rendered + * prior to this projection. + * @param criteria The local criteria to which this project is attached (for resolution). + * @param criteriaQuery The overall criteria query instance. + * @return The columns aliases pertaining to a particular criteria-level alias; expected to return null if + * this projection does not understand this alias. + */ + public String[] getColumnAliases(String alias, int position, Criteria criteria, CriteriaQuery criteriaQuery); +} diff --git a/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java b/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java index e38157b3b9..7d78f5a9f5 100755 --- a/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java +++ b/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java @@ -63,6 +63,8 @@ public class IdentifierProjection extends SimpleProjection { .append(" as y") .append(position + i) .append('_'); + if (i < cols.length -1) + buf.append(", "); } return buf.toString(); } diff --git a/core/src/main/java/org/hibernate/criterion/ProjectionList.java b/core/src/main/java/org/hibernate/criterion/ProjectionList.java index afad69d4d5..091d8d7ea0 100755 --- a/core/src/main/java/org/hibernate/criterion/ProjectionList.java +++ b/core/src/main/java/org/hibernate/criterion/ProjectionList.java @@ -35,7 +35,7 @@ import org.hibernate.util.ArrayHelper; /** * @author Gavin King */ -public class ProjectionList implements Projection { +public class ProjectionList implements EnhancedProjection { private List elements = new ArrayList(); @@ -70,7 +70,7 @@ public class ProjectionList implements Projection { for ( int i=0; iLoader for Criteria queries. Note that criteria queries are @@ -127,8 +128,16 @@ public class CriteriaLoader extends OuterJoinLoader { Type[] types = translator.getProjectedTypes(); result = new Object[types.length]; String[] columnAliases = translator.getProjectedColumnAliases(); - for ( int i=0; i 1 ) { + String[] typeColumnAliases = ArrayHelper.slice( columnAliases, pos, numColumns ); + result[i] = types[i].nullSafeGet(rs, typeColumnAliases, session, null); + } + else { + result[i] = types[i].nullSafeGet(rs, columnAliases[pos], session, null); + } + pos += numColumns; } aliases = translator.getProjectedAliases(); } diff --git a/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java b/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java index b260aa2a8e..32cec0f46f 100755 --- a/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java +++ b/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java @@ -42,6 +42,7 @@ import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.LockOptions; +import org.hibernate.criterion.EnhancedProjection; import org.hibernate.hql.ast.util.SessionFactoryHelper; import org.hibernate.criterion.CriteriaQuery; import org.hibernate.criterion.Criterion; @@ -362,7 +363,9 @@ public class CriteriaQueryTranslator implements CriteriaQuery { } public String[] getProjectedColumnAliases() { - return rootCriteria.getProjection().getColumnAliases( 0 ); + return rootCriteria.getProjection() instanceof EnhancedProjection ? + ( ( EnhancedProjection ) rootCriteria.getProjection() ).getColumnAliases( 0, rootCriteria, this ) : + rootCriteria.getProjection().getColumnAliases( 0 ); } public String[] getProjectedAliases() { @@ -426,10 +429,13 @@ public class CriteriaQueryTranslator implements CriteriaQuery { //first look for a reference to a projection alias final Projection projection = rootCriteria.getProjection(); - String[] projectionColumns = projection == null ? - null : - projection.getColumnAliases( propertyName, 0 ); - + String[] projectionColumns = null; + if ( projection != null ) { + projectionColumns = ( projection instanceof EnhancedProjection ? + ( ( EnhancedProjection ) projection ).getColumnAliases( propertyName, 0, rootCriteria, this ) : + projection.getColumnAliases( propertyName, 0 ) + ); + } if ( projectionColumns == null ) { //it does not refer to an alias of a projection, //look for a property diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/CityState.java b/testsuite/src/test/java/org/hibernate/test/criteria/CityState.java new file mode 100644 index 0000000000..52eaa17685 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/criteria/CityState.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Middleware LLC 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 Middleware LLC. + * + * 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.test.criteria; + +/** + * @author Gail Badner + */ + +public class CityState { + private String city; + private String state; + + public CityState() {} + + public CityState(String city, String state) { + this.city = city; + this.state = state; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + +} diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/Course.java b/testsuite/src/test/java/org/hibernate/test/criteria/Course.java index 17ce8e5e75..5809171b5f 100755 --- a/testsuite/src/test/java/org/hibernate/test/criteria/Course.java +++ b/testsuite/src/test/java/org/hibernate/test/criteria/Course.java @@ -1,12 +1,17 @@ //$Id: Course.java 5686 2005-02-12 07:27:32Z steveebersole $ package org.hibernate.test.criteria; +import java.util.HashSet; +import java.util.Set; + /** * @author Gavin King */ public class Course { private String courseCode; private String description; + private Set courseMeetings = new HashSet(); + public String getCourseCode() { return courseCode; } @@ -19,4 +24,10 @@ public class Course { public void setDescription(String description) { this.description = description; } + public Set getCourseMeetings() { + return courseMeetings; + } + public void setCourseMeetings(Set courseMeetings) { + this.courseMeetings = courseMeetings; + } } diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeeting.java b/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeeting.java new file mode 100644 index 0000000000..28d032bc71 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeeting.java @@ -0,0 +1,29 @@ +package org.hibernate.test.criteria; + +/** + * @author Gail Badner + */ +public class CourseMeeting { + private CourseMeetingId id; + private Course course; + + public CourseMeeting() {} + + public CourseMeeting(Course course, String day, int period, String location) { + this.id = new CourseMeetingId( course, day, period, location ); + this.course = course; + } + + public CourseMeetingId getId() { + return id; + } + public void setId(CourseMeetingId id) { + this.id = id; + } + public Course getCourse() { + return course; + } + public void setCourse(Course course) { + this.course = course; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeetingId.java b/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeetingId.java new file mode 100644 index 0000000000..a5fb625a86 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/criteria/CourseMeetingId.java @@ -0,0 +1,47 @@ +package org.hibernate.test.criteria; + +import java.io.Serializable; + +/** + * @author Gail Badner + */ +public class CourseMeetingId implements Serializable { + private String courseCode; + private String day; + private int period; + private String location; + + public CourseMeetingId() {} + + public CourseMeetingId(Course course, String day, int period, String location) { + this.courseCode = course.getCourseCode(); + this.day = day; + this.period = period; + this.location = location; + } + + public String getCourseCode() { + return courseCode; + } + public void setCourseCode(String courseCode) { + this.courseCode = courseCode; + } + public String getDay() { + return day; + } + public void setDay(String day) { + this.day = day; + } + public int getPeriod() { + return period; + } + public void setPeriod(int period) { + this.period = period; + } + public String getLocation() { + return location; + } + public void setLocation(String location) { + this.location = location; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java b/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java index 5394988f49..f26dbbf99a 100755 --- a/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java +++ b/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java @@ -19,6 +19,7 @@ import org.hibernate.criterion.Example; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projection; +import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; @@ -742,6 +743,152 @@ public class CriteriaQueryTest extends FunctionalTestCase { s.close(); } + public void testProjectedEmbeddedCompositeId() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + Course course = new Course(); + course.setCourseCode("HIB"); + course.setDescription("Hibernate Training"); + s.save(course); + + Student gavin = new Student(); + gavin.setName("Gavin King"); + gavin.setStudentNumber(667); + s.save(gavin); + + Student xam = new Student(); + xam.setName("Max Rydahl Andersen"); + xam.setStudentNumber(101); + s.save(xam); + + Enrolment enrolment = new Enrolment(); + enrolment.setCourse(course); + enrolment.setCourseCode(course.getCourseCode()); + enrolment.setSemester((short) 1); + enrolment.setYear((short) 1999); + enrolment.setStudent(xam); + enrolment.setStudentNumber(xam.getStudentNumber()); + xam.getEnrolments().add(enrolment); + s.save(enrolment); + + enrolment = new Enrolment(); + enrolment.setCourse(course); + enrolment.setCourseCode(course.getCourseCode()); + enrolment.setSemester((short) 3); + enrolment.setYear((short) 1998); + enrolment.setStudent(gavin); + enrolment.setStudentNumber(gavin.getStudentNumber()); + gavin.getEnrolments().add(enrolment); + s.save(enrolment); + + s.flush(); + + List enrolments = ( List ) s.createCriteria( Enrolment.class).setProjection( Projections.id() ).list(); + t.rollback(); + s.close(); + } + + public void testProjectedCompositeId() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + Course course = new Course(); + course.setCourseCode("HIB"); + course.setDescription("Hibernate Training"); + course.getCourseMeetings().add( new CourseMeeting( course, "Monday", 1, "1313 Mockingbird Lane" ) ); + s.save(course); + s.flush(); + + List data = ( List ) s.createCriteria( CourseMeeting.class).setProjection( Projections.id() ).list(); + t.rollback(); + s.close(); + } + + public void testProjectedComponent() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + Student gaith = new Student(); + gaith.setName("Gaith Bell"); + gaith.setStudentNumber(123); + gaith.setCityState( new CityState( "Chicago", "Illinois" ) ); + s.save( gaith ); + s.flush(); + + List cityStates = ( List ) s.createCriteria( Student.class).setProjection( Projections.property( "cityState" )).list(); + t.rollback(); + s.close(); + } + + public void testProjectedListIncludesComponent() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + Student gaith = new Student(); + gaith.setName("Gaith Bell"); + gaith.setStudentNumber(123); + gaith.setCityState( new CityState( "Chicago", "Illinois" ) ); + s.save(gaith); + s.flush(); + List data = ( List ) s.createCriteria( Student.class) + .setProjection( Projections.projectionList() + .add( Projections.property( "cityState" ) ) + .add( Projections.property("name") ) ) + .list(); + t.rollback(); + s.close(); + } + + public void testProjectedListIncludesEmbeddedCompositeId() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + Course course = new Course(); + course.setCourseCode("HIB"); + course.setDescription("Hibernate Training"); + s.save(course); + + Student gavin = new Student(); + gavin.setName("Gavin King"); + gavin.setStudentNumber(667); + s.save(gavin); + + Student xam = new Student(); + xam.setName("Max Rydahl Andersen"); + xam.setStudentNumber(101); + s.save(xam); + + Enrolment enrolment = new Enrolment(); + enrolment.setCourse(course); + enrolment.setCourseCode(course.getCourseCode()); + enrolment.setSemester((short) 1); + enrolment.setYear((short) 1999); + enrolment.setStudent(xam); + enrolment.setStudentNumber(xam.getStudentNumber()); + xam.getEnrolments().add(enrolment); + s.save(enrolment); + + enrolment = new Enrolment(); + enrolment.setCourse(course); + enrolment.setCourseCode(course.getCourseCode()); + enrolment.setSemester((short) 3); + enrolment.setYear((short) 1998); + enrolment.setStudent(gavin); + enrolment.setStudentNumber(gavin.getStudentNumber()); + gavin.getEnrolments().add(enrolment); + s.save(enrolment); + s.flush(); + List data = ( List ) s.createCriteria( Enrolment.class) + .setProjection( Projections.projectionList() + .add( Projections.property( "semester" ) ) + .add( Projections.property("year") ) + .add( Projections.id() ) ) + .list(); + t.rollback(); + s.close(); + } + public void testSubcriteriaJoinTypes() { Session session = openSession(); Transaction t = session.beginTransaction(); diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml b/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml index c5cb136d30..d208c2c733 100755 --- a/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml +++ b/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml @@ -10,21 +10,41 @@ + + + + - + + + + + + + + + + + + + + + + + - + diff --git a/testsuite/src/test/java/org/hibernate/test/criteria/Student.java b/testsuite/src/test/java/org/hibernate/test/criteria/Student.java index ac9134e22f..5b6d69df31 100755 --- a/testsuite/src/test/java/org/hibernate/test/criteria/Student.java +++ b/testsuite/src/test/java/org/hibernate/test/criteria/Student.java @@ -10,6 +10,7 @@ import java.util.Set; public class Student { private long studentNumber; private String name; + private CityState cityState; private Course preferredCourse; private Set enrolments = new HashSet(); @@ -29,6 +30,14 @@ public class Student { this.studentNumber = studentNumber; } + public CityState getCityState() { + return cityState; + } + + public void setCityState(CityState cityState) { + this.cityState = cityState; + } + public Course getPreferredCourse() { return preferredCourse; }