diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/ColumnAliasExtractor.java b/hibernate-core/src/main/java/org/hibernate/dialect/ColumnAliasExtractor.java new file mode 100644 index 0000000000..e00d6522d3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/ColumnAliasExtractor.java @@ -0,0 +1,67 @@ +/* + * 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.dialect; + +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +/** + * Strategy for extracting the unique column alias out of a {@link ResultSetMetaData}. This is used during the + * "auto discovery" phase of native SQL queries. + *
+ * Generally this should be done via {@link ResultSetMetaData#getColumnLabel}, but not all drivers do this correctly. + * + * @author Steve Ebersole + */ +public interface ColumnAliasExtractor { + /** + * Extract the unique column alias. + * + * @param metaData The result set metadata + * @param position The column position + * + * @return The alias + */ + public String extractColumnAlias(ResultSetMetaData metaData, int position) throws SQLException; + + /** + * An extractor which uses {@link ResultSetMetaData#getColumnLabel} + */ + public static final ColumnAliasExtractor COLUMN_LABEL_EXTRACTOR = new ColumnAliasExtractor() { + @Override + public String extractColumnAlias(ResultSetMetaData metaData, int position) throws SQLException { + return metaData.getColumnLabel( position ); + } + }; + + /** + * An extractor which uses {@link ResultSetMetaData#getColumnName} + */ + public static final ColumnAliasExtractor COLUMN_NAME_EXTRACTOR = new ColumnAliasExtractor() { + @Override + public String extractColumnAlias(ResultSetMetaData metaData, int position) throws SQLException { + return metaData.getColumnName( position ); + } + }; +} 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 ea734e60aa..84349e6aad 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1691,6 +1691,10 @@ public abstract class Dialect { return " cross join "; } + public ColumnAliasExtractor getColumnAliasExtractor() { + return ColumnAliasExtractor.COLUMN_LABEL_EXTRACTOR; + } + // Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index c0d0cfc751..4d235473a2 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -576,7 +576,7 @@ public class CustomLoader extends Loader { public String getColumnName(int position) throws HibernateException { try { - return resultSetMetaData.getColumnName( position ); + return factory.getDialect().getColumnAliasExtractor().extractColumnAlias( resultSetMetaData, position ); } catch( SQLException e ) { throw new HibernateException( "Could not resolve column name [" + position + "]", e ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/AutoDiscoveryTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/AutoDiscoveryTest.java new file mode 100644 index 0000000000..87f67706da --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/AutoDiscoveryTest.java @@ -0,0 +1,108 @@ +/* + * 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.test.sql.autodiscovery; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.jdbc.Work; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Steve Ebersole + */ +public class AutoDiscoveryTest extends BaseCoreFunctionalTestCase { + private static final String QUERY_STRING = + "select u.name as username, g.name as groupname, m.joindate " + + "from t_membership m " + + " inner join t_user u on m.member_id = u.id " + + " inner join t_group g on m.group_id = g.id"; + + @Override + protected Class>[] getAnnotatedClasses() { + return new Class[] { Group.class, User.class, Membership.class }; + } + + @Test + public void testSqlQueryAutoDiscovery() throws Exception { + Session session = openSession(); + session.beginTransaction(); + User u = new User( "steve" ); + Group g = new Group( "developer" ); + Membership m = new Membership( u, g ); + session.save( u ); + session.save( g ); + session.save( m ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + List result = session.createSQLQuery( QUERY_STRING ).list(); + Object[] row = (Object[]) result.get( 0 ); + Assert.assertEquals( "steve", row[0] ); + Assert.assertEquals( "developer", row[1] ); + session.delete( m ); + session.delete( u ); + session.delete( g ); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testDialectGetColumnAliasExtractor() throws Exception { + Session session = openSession(); + session.beginTransaction(); + session.doWork( + new Work() { + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement ps = connection.prepareStatement( QUERY_STRING ); + ResultSet rs = ps.executeQuery(); + try { + ResultSetMetaData metadata = rs.getMetaData(); + String column1Alias = getDialect().getColumnAliasExtractor().extractColumnAlias( metadata, 1 ); + String column2Alias = getDialect().getColumnAliasExtractor().extractColumnAlias( metadata, 2 ); + Assert.assertFalse( "bad dialect.getColumnAliasExtractor impl", column1Alias.equals( column2Alias ) ); + } + finally { + rs.close(); + ps.close(); + } + } + } + ); + session.getTransaction().commit(); + session.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/Group.java b/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/Group.java new file mode 100644 index 0000000000..f672e707da --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/autodiscovery/Group.java @@ -0,0 +1,76 @@ +/* + * 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.test.sql.autodiscovery; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import java.util.Set; + +/** + * @author Steve Ebersole + */ +@Entity +@Table( name = "t_group") +public class Group { + private Long id; + private String name; + private Set