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.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -44,6 +45,8 @@ import org.hibernate.id.uuid.StandardRandomStrategy;
|
||||||
import org.hibernate.jdbc.ReturningWork;
|
import org.hibernate.jdbc.ReturningWork;
|
||||||
import org.hibernate.jdbc.Work;
|
import org.hibernate.jdbc.Work;
|
||||||
import org.hibernate.jdbc.WorkExecutorVisitable;
|
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.jpa.spi.NativeQueryTupleTransformer;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.procedure.ProcedureCall;
|
import org.hibernate.procedure.ProcedureCall;
|
||||||
|
@ -849,7 +852,12 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
if ( Tuple.class.equals( resultClass ) ) {
|
if ( Tuple.class.equals( resultClass ) ) {
|
||||||
query.setTupleTransformer( new NativeQueryTupleTransformer() );
|
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 ) ) {
|
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
||||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
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.query.TypedTupleTransformer;
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
|
|
||||||
|
import static java.util.Locale.ROOT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ResultTransformer} for handling JPA {@link Tuple} results from native queries.
|
* 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];
|
final String alias = aliases[i];
|
||||||
if ( alias != null ) {
|
if ( alias != null ) {
|
||||||
aliasToValue.put( alias, tuple[i] );
|
aliasToValue.put( alias, tuple[i] );
|
||||||
aliasReferences.put( alias.toLowerCase(), alias );
|
aliasReferences.put( alias.toLowerCase(ROOT), alias );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
size = tuple.length;
|
size = tuple.length;
|
||||||
|
@ -96,7 +98,7 @@ public class NativeQueryTupleTransformer implements ResultTransformer<Tuple>, Ty
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(String alias) {
|
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 ) ) {
|
if ( aliasReference != null && aliasToValue.containsKey( aliasReference ) ) {
|
||||||
return aliasToValue.get( 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")
|
@Entity(name = "Thing")
|
||||||
@Table(name = "thingy_table")
|
@Table(name = "thingy_table")
|
||||||
public class Thing {
|
public class Thing {
|
||||||
|
|
Loading…
Reference in New Issue