HHH-16710 allow Map and List instantiation for native SQL queries
This commit is contained in:
parent
ed75e24d94
commit
1557a66e6e
|
@ -12,6 +12,7 @@ import java.io.ObjectOutputStream;
|
|||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
@ -44,6 +45,8 @@ import org.hibernate.id.uuid.StandardRandomStrategy;
|
|||
import org.hibernate.jdbc.ReturningWork;
|
||||
import org.hibernate.jdbc.Work;
|
||||
import org.hibernate.jdbc.WorkExecutorVisitable;
|
||||
import org.hibernate.jpa.spi.NativeQueryListTransformer;
|
||||
import org.hibernate.jpa.spi.NativeQueryMapTransformer;
|
||||
import org.hibernate.jpa.spi.NativeQueryTupleTransformer;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.procedure.ProcedureCall;
|
||||
|
@ -849,7 +852,12 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
|||
if ( Tuple.class.equals( resultClass ) ) {
|
||||
query.setTupleTransformer( new NativeQueryTupleTransformer() );
|
||||
}
|
||||
// TODO: handle Map, List as well
|
||||
else if ( Map.class.equals( resultClass ) ) {
|
||||
query.setTupleTransformer( new NativeQueryMapTransformer() );
|
||||
}
|
||||
else if ( List.class.equals( resultClass ) ) {
|
||||
query.setTupleTransformer( new NativeQueryListTransformer() );
|
||||
}
|
||||
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.jpa.spi;
|
||||
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link TupleTransformer} for handling {@link List} results from native queries.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class NativeQueryListTransformer implements TupleTransformer<List<Object>> {
|
||||
@Override
|
||||
public List<Object> transformTuple(Object[] tuple, String[] aliases) {
|
||||
return List.of( tuple );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.jpa.spi;
|
||||
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Locale.ROOT;
|
||||
|
||||
/**
|
||||
* A {@link TupleTransformer} for handling {@link Map} results from native queries.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class NativeQueryMapTransformer implements TupleTransformer<Map<String,Object>> {
|
||||
@Override
|
||||
public Map<String,Object> transformTuple(Object[] tuple, String[] aliases) {
|
||||
Map<String,Object> map = new HashMap<>( aliases.length );
|
||||
for ( int i = 0; i < aliases.length; i++ ) {
|
||||
map.put( aliases[i].toLowerCase(ROOT), tuple[i] );
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.query.TypedTupleTransformer;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
|
||||
import static java.util.Locale.ROOT;
|
||||
|
||||
/**
|
||||
* A {@link ResultTransformer} for handling JPA {@link Tuple} results from native queries.
|
||||
*
|
||||
|
@ -81,7 +83,7 @@ public class NativeQueryTupleTransformer implements ResultTransformer<Tuple>, Ty
|
|||
final String alias = aliases[i];
|
||||
if ( alias != null ) {
|
||||
aliasToValue.put( alias, tuple[i] );
|
||||
aliasReferences.put( alias.toLowerCase(), alias );
|
||||
aliasReferences.put( alias.toLowerCase(ROOT), alias );
|
||||
}
|
||||
}
|
||||
size = tuple.length;
|
||||
|
@ -96,7 +98,7 @@ public class NativeQueryTupleTransformer implements ResultTransformer<Tuple>, Ty
|
|||
|
||||
@Override
|
||||
public Object get(String alias) {
|
||||
final String aliasReference = aliasReferences.get( alias.toLowerCase() );
|
||||
final String aliasReference = aliasReferences.get( alias.toLowerCase(ROOT) );
|
||||
if ( aliasReference != null && aliasToValue.containsKey( aliasReference ) ) {
|
||||
return aliasToValue.get( aliasReference );
|
||||
}
|
||||
|
|
|
@ -128,6 +128,52 @@ public class ImplicitInstantiationTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSqlTupleInstantiationWithAlias(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.persist(new Thing(1L, "thing"));
|
||||
Tuple result = session.createNativeQuery("select id as id, upper(name) as name from thingy_table", Tuple.class)
|
||||
.addSynchronizedEntityClass(Thing.class)
|
||||
.getSingleResult();
|
||||
assertEquals( result.get("id"), 1L );
|
||||
assertEquals( result.get("name"), "THING" );
|
||||
session.getTransaction().setRollbackOnly();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSqlMapInstantiationWithAlias(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.persist(new Thing(1L, "thing"));
|
||||
Map result = session.createNativeQuery("select id as id, upper(name) as name from thingy_table", Map.class)
|
||||
.addSynchronizedEntityClass(Thing.class)
|
||||
.getSingleResult();
|
||||
assertEquals( result.get("id"), 1L );
|
||||
assertEquals( result.get("name"), "THING" );
|
||||
session.getTransaction().setRollbackOnly();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSqlListInstantiationWithoutAlias(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.persist(new Thing(1L, "thing"));
|
||||
List result = session.createNativeQuery("select id as id, upper(name) as name from thingy_table", List.class)
|
||||
.addSynchronizedEntityClass(Thing.class)
|
||||
.getSingleResult();
|
||||
assertEquals( result.get(0), 1L );
|
||||
assertEquals( result.get(1), "THING" );
|
||||
session.getTransaction().setRollbackOnly();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "Thing")
|
||||
@Table(name = "thingy_table")
|
||||
public class Thing {
|
||||
|
|
Loading…
Reference in New Issue