impl the new overload of createNativeQuery()

- and add a second new overload
- tolerate non-entity classes as arguments to these methods
- the overloads accept a result class, and return a typed Query<R>
This commit is contained in:
Gavin King 2021-12-15 00:09:45 +01:00
parent 4ea59b4961
commit ac845bca31
9 changed files with 143 additions and 33 deletions

View File

@ -511,11 +511,21 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
return queryDelegate().createNativeQuery( sqlString, resultClass );
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass, String tableAlias) {
return queryDelegate().createNativeQuery( sqlString, resultClass, tableAlias );
}
@Override @SuppressWarnings("rawtypes")
public NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName) {
return queryDelegate().createNativeQuery( sqlString, resultSetMappingName );
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName, Class resultClass) {
return queryDelegate().createNativeQuery( sqlString, resultSetMappingName, resultClass );
}
@Override
public ProcedureCall createNamedStoredProcedureQuery(String name) {
return delegate.createNamedStoredProcedureQuery( name );

View File

@ -692,59 +692,69 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
}
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
//note: we're doing something a bit funny here to work around
// the classing signatures declared by the supertypes
public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();
try {
NativeQueryImplementor query = createNativeQuery( sqlString );
if ( Tuple.class.equals( resultClass ) ) {
query.setTupleTransformer( new NativeQueryTupleTransformer() );
}
else {
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
}
return query;
}
catch (RuntimeException he) {
throw getExceptionConverter().convert( he );
}
}
@Override @SuppressWarnings("rawtypes")
public NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();
final NativeQueryImplementor<Object> query;
try {
if ( StringHelper.isNotEmpty( resultSetMappingName ) ) {
if ( StringHelper.isNotEmpty(resultSetMappingName) ) {
final NamedResultSetMappingMemento resultSetMappingMemento = getFactory().getQueryEngine()
.getNamedObjectRepository()
.getResultSetMappingMemento( resultSetMappingName );
.getResultSetMappingMemento(resultSetMappingName);
if ( resultSetMappingMemento == null ) {
throw new HibernateException( "Could not resolve specified result-set mapping name : " + resultSetMappingName );
throw new HibernateException( "Could not resolve specified result-set mapping name : "
+ resultSetMappingName);
}
query = new NativeQueryImpl<>( sqlString, resultSetMappingMemento, this );
return new NativeQueryImpl<>(sqlString, resultSetMappingMemento, this);
}
else {
query = new NativeQueryImpl<>( sqlString, this );
return new NativeQueryImpl<>(sqlString, this);
}
//TODO: why no applyQuerySettingsAndHints( query ); ???
}
catch (RuntimeException he) {
throw getExceptionConverter().convert( he );
}
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
//note: we're doing something a bit funny here to work around
// the clashing signatures declared by the supertypes
public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass) {
NativeQueryImplementor query = createNativeQuery( sqlString );
if ( Tuple.class.equals(resultClass) ) {
query.setTupleTransformer( new NativeQueryTupleTransformer() );
}
else if ( getFactory().getMetamodel().findEntityDescriptor(resultClass)!=null ) {
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
}
return query;
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass, String tableAlias) {
NativeQueryImplementor query = createNativeQuery( sqlString );
if ( Tuple.class.equals(resultClass) ) {
query.setTupleTransformer( new NativeQueryTupleTransformer() );
}
else if ( getFactory().getMetamodel().findEntityDescriptor(resultClass)!=null ) {
query.addEntity( tableAlias, resultClass.getName(), LockMode.READ );
}
return query;
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName, Class resultClass) {
final NativeQueryImplementor query = createNativeQuery( sqlString, resultSetMappingName );
if ( Tuple.class.equals( resultClass ) ) {
query.setTupleTransformer( new NativeQueryTupleTransformer() );
}
return query;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// named query handling

View File

@ -98,6 +98,9 @@ public interface QueryProducer {
/**
* Create a NativeQuery instance for the given native (SQL) query using
* implicit mapping to the specified Java type.
* <p>
* If the given class is an entity class, this method is equivalent to
* {@code createNativeQuery(sqlString).addEntity("alias1", resultClass)}.
*
* @param sqlString Native (SQL) query string
* @param resultClass The Java entity type to map results to
@ -108,6 +111,23 @@ public interface QueryProducer {
*/
<R> NativeQuery<R> createNativeQuery(String sqlString, Class<R> resultClass);
/**
* Create a NativeQuery instance for the given native (SQL) query using
* implicit mapping to the specified Java type.
* <p>
* If the given class is an entity class, this method is equivalent to
* {@code createNativeQuery(sqlString).addEntity(tableAlias, resultClass)}.
*
* @param sqlString Native (SQL) query string
* @param resultClass The Java entity type to map results to
* @param tableAlias The table alias for columns in the result set
*
* @return The NativeQuery instance for manipulation and execution
*
* @see jakarta.persistence.EntityManager#createNativeQuery(String,Class)
*/
<R> NativeQuery<R> createNativeQuery(String sqlString, Class<R> resultClass, String tableAlias);
/**
* Create a NativeQuery instance for the given native (SQL) query using
* implicit mapping to the specified Java type.

View File

@ -50,9 +50,15 @@ public interface QueryProducerImplementor extends QueryProducer {
@Override
<R> NativeQueryImplementor<R> createNativeQuery(String sqlString, Class<R> resultClass);
@Override
<R> NativeQueryImplementor<R> createNativeQuery(String sqlString, Class<R> resultClass, String tableAlias);
@Override @SuppressWarnings("rawtypes")
NativeQueryImplementor createNativeQuery(String sqlString, String resultSetMappingName);
@Override
<R> NativeQueryImplementor<R> createNativeQuery(String sqlString, String resultSetMappingName, Class<R> resultClass);
@Override @SuppressWarnings("rawtypes")
NativeQueryImplementor getNamedNativeQuery(String name);

View File

@ -99,11 +99,11 @@ public class QueryAndSQLTest {
.currentDate();
String sql = String.format(
"select t.TABLE_NAME as {t.tableName}, %s as {t.daysOld} from ALL_TABLES t where t.TABLE_NAME = 'AUDIT_ACTIONS' ",
"select t.TABLE_NAME as {t.tableName}, %s as {t.daysOld} from ALL_TABLES t where t.TABLE_NAME = 'AUDIT_ACTIONS' ",
dateFunctionRendered
);
String sql2 = String.format(
"select TABLE_NAME as t_name, %s as t_time from ALL_TABLES where TABLE_NAME = 'AUDIT_ACTIONS' ",
"select TABLE_NAME as t_name, %s as t_time from ALL_TABLES where TABLE_NAME = 'AUDIT_ACTIONS' ",
dateFunctionRendered
);
@ -111,7 +111,9 @@ public class QueryAndSQLTest {
scope.inTransaction(
session -> {
session.createNativeQuery( sql ).addEntity( "t", AllTables.class ).list();
List<AllTables> allTables = session.createNativeQuery( sql, AllTables.class, "t" ).list();
session.createNativeQuery( sql2, "all" ).list();
List<String> allTableNames = session.createNativeQuery( sql2, "all", String.class ).list();
NativeQuery q = session.createNativeQuery( sql2 );
q.addRoot( "t", AllTables.class ).addProperty( "tableName", "t_name" ).addProperty(
"daysOld",

View File

@ -81,6 +81,19 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
} );
}
public void testNativeQueryResultWithResultClass() {
inTransaction( (session) -> {
final NativeQueryImplementor<Object[]> query = session.createNativeQuery( "select id, salary from EMP", "emp_id_salary", Object[].class );
final List<Object[]> results = query.list();
assertThat( results ).hasSize( 1 );
final Object[] values = results.get( 0 );
assertThat( values[0] ).isEqualTo( 1 );
assertThat( values[1] ).isEqualTo( SALARY );
} );
}
@Test
@FailureExpected( jiraKey = "HHH-14975", message = "Not yet implemented" )
@NotImplementedYet

View File

@ -75,7 +75,7 @@ public class FormulaNativeQueryTest {
public void testNativeQueryWithAllFields(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Query query = session.createNativeQuery(
Query<Foo> query = session.createNativeQuery(
"SELECT ft.*, abs(locationEnd - locationStart) as distance FROM foo_table ft",
Foo.class
);

View File

@ -171,6 +171,28 @@ public class EntityResultTests extends AbstractUsageTest {
);
}
@Test
public void testExplicitDiscriminatedMappingWithResultClass(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String qryString =
"select id as id_alias,"
+ " type_code as type_code_alias,"
+ " root_name as root_name_alias,"
+ " subtype1_name as sub_type1_name_alias,"
+ " subtype2_name as sub_type2_name_alias"
+ " from discriminated_entity";
final List<DiscriminatedRoot> results = session.createNativeQuery( qryString, "root-explicit", DiscriminatedRoot.class ).list();
assertThat( results.size(), is( 4 ) );
final Set<Integer> idsFound = new HashSet<>();
results.forEach( result -> idsFound.add( result.getId() ) );
assertThat( idsFound, containsInAnyOrder( 1, 2, 3, 4 ) );
}
);
}
@Test
public void testConvertedAttributes(SessionFactoryScope scope) {
scope.inTransaction(

View File

@ -49,7 +49,6 @@ import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import jakarta.persistence.PersistenceException;
@ -260,6 +259,34 @@ public class NativeSQLQueriesTest {
);
}
@Test
public void testResultSetMappingDefinitionWithResultClass(SessionFactoryScope scope) {
Organization ifa = new Organization("IFA");
Organization jboss = new Organization("JBoss");
Person gavin = new Person("Gavin");
Employment emp = new Employment(gavin, jboss, "AU");
scope.inTransaction(
session -> {
session.persist(ifa);
session.persist(jboss);
session.persist(gavin);
session.persist(emp);
List<Object[]> l = session.createNativeQuery( getOrgEmpRegionSQL(), "org-emp-regionCode", Object[].class ).list();
assertEquals( l.size(), 2 );
l = session.createNativeQuery( getOrgEmpPersonSQL(), "org-emp-person", Object[].class ).list();
assertEquals( l.size(), 1 );
session.delete(emp);
session.delete(gavin);
session.delete(ifa);
session.delete(jboss);
}
);
}
@Test
public void testScalarValues(SessionFactoryScope scope) throws Exception {
Organization ifa = new Organization( "IFA" );