HHH-17948 add Session.findAll(), StatelessSession.getAll()
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
250a59a1c7
commit
526e282c4c
|
@ -126,8 +126,8 @@ public interface MultiIdentifierLoadAccess<T> {
|
|||
|
||||
/**
|
||||
* Should {@link #multiLoad} return entity instances that have been
|
||||
* {@link Session#remove(Object) marked for removal} in the current
|
||||
* session, but not yet {@code delete}d in the database?
|
||||
* {@linkplain Session#remove(Object) marked for removal} in the
|
||||
* current session, but not yet {@code delete}d in the database?
|
||||
* <p>
|
||||
* By default, instances marked for removal are replaced by null in
|
||||
* the returned list of entities when {@link #enableOrderedReturn}
|
||||
|
|
|
@ -691,6 +691,26 @@ public interface Session extends SharedSessionContract, EntityManager {
|
|||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Return the persistent instances of the given entity class with the given identifiers
|
||||
* as a list. The position of an instance in the list matches the position of its identifier
|
||||
* in the given array, and the list contains a null value if there is no persistent instance
|
||||
* matching a given identifier. If an instance is already associated with the session, that
|
||||
* instance is returned. This method never returns an uninitialized instance.
|
||||
* <p>
|
||||
* Every object returned by {@code findAll()} is either an unproxied instance of the given
|
||||
* entity class, or a fully-fetched proxy object.
|
||||
*
|
||||
* @param entityType the entity type
|
||||
* @param ids the identifiers
|
||||
*
|
||||
* @return an ordered list of persistent instances, with null elements representing missing
|
||||
* entities
|
||||
*
|
||||
* @since 6.5
|
||||
*/
|
||||
<E> List<E> findAll(Class<E> entityType, Object... ids);
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* or null if there is no such persistent instance. If the instance is already associated
|
||||
|
@ -699,8 +719,8 @@ public interface Session extends SharedSessionContract, EntityManager {
|
|||
* <p>
|
||||
* This operation is very similar to {@link #find(Class, Object)}.
|
||||
* <p>
|
||||
* The object returned by {@code get()} or {@code find() } is either an unproxied instance
|
||||
* of the given entity class, of a fully-fetched proxy object.
|
||||
* The object returned by {@code get()} or {@code find()} is either an unproxied instance
|
||||
* of the given entity class, or a fully-fetched proxy object.
|
||||
* <p>
|
||||
* This operation requests {@link LockMode#NONE}, that is, no lock, allowing the object
|
||||
* to be retrieved from the cache without the cost of database access. However, if it is
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate;
|
|||
import jakarta.persistence.EntityGraph;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A command-oriented API often used for performing bulk operations against
|
||||
* the database. A stateless session has no persistence context, and always
|
||||
|
@ -250,6 +252,23 @@ public interface StatelessSession extends SharedSessionContract {
|
|||
*/
|
||||
<T> T get(EntityGraph<T> graph, GraphSemantic graphSemantic, Object id, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Retrieve multiple rows, returning entity instances in a
|
||||
* list where the position of an instance in the list matches
|
||||
* the position of its identifier in the given array, and the
|
||||
* list contains a null value if there is no persistent
|
||||
* instance matching a given identifier.
|
||||
*
|
||||
* @param entityClass The class of the entity to retrieve
|
||||
* @param ids The ids of the entities to retrieve
|
||||
*
|
||||
* @return an ordered list of detached entity instances, with
|
||||
* null elements representing missing entities
|
||||
*
|
||||
* @since 6.5
|
||||
*/
|
||||
<T> List<T> getAll(Class<T> entityClass, Object... ids);
|
||||
|
||||
/**
|
||||
* Refresh the entity instance state from the database.
|
||||
*
|
||||
|
|
|
@ -953,6 +953,11 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
|
|||
delegate.detach( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> findAll(Class<E> entityType, Object... ids) {
|
||||
return delegate.findAll( entityType, ids );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> theClass, Object id) {
|
||||
return delegate.get( theClass, id );
|
||||
|
|
|
@ -263,6 +263,11 @@ public class SessionLazyDelegator implements Session {
|
|||
this.lazySession.get().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> findAll(Class<E> entityType, Object... ids) {
|
||||
return this.lazySession.get().findAll( entityType, ids );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> entityType, Object id) {
|
||||
return this.lazySession.get().get( entityType, id );
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.sql.Connection;
|
|||
import java.sql.NClob;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
@ -946,6 +947,11 @@ public class SessionImpl
|
|||
return this.byId( entityName ).getReference( id );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> findAll(Class<E> entityType, Object... ids) {
|
||||
return this.byMultipleIds( entityType ).multiLoad( ids );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> entityClass, Object id) throws HibernateException {
|
||||
return this.byId( entityClass ).load( id );
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
*/
|
||||
package org.hibernate.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
|
@ -60,12 +63,15 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.query.criteria.JpaCriteriaQuery;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import jakarta.persistence.EntityGraph;
|
||||
import jakarta.transaction.SystemException;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
|
||||
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
|
||||
import static org.hibernate.engine.internal.Versioning.incrementVersion;
|
||||
|
@ -510,6 +516,30 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAll(Class<T> entityClass, Object... ids) {
|
||||
for (Object id : ids) {
|
||||
if ( id == null ) {
|
||||
throw new IllegalArgumentException("Null id");
|
||||
}
|
||||
}
|
||||
final EntityPersister persister = getEntityPersister( entityClass.getName() );
|
||||
final JpaCriteriaQuery<T> query = getCriteriaBuilder().createQuery(entityClass);
|
||||
final JpaRoot<T> from = query.from(entityClass);
|
||||
query.where( from.get(persister.getIdentifierPropertyName()).in(ids) );
|
||||
final List<T> resultList = createSelectionQuery(query).getResultList();
|
||||
final List<Object> idList = new ArrayList<>(resultList.size());
|
||||
for (T entity : resultList) {
|
||||
idList.add( persister.getIdentifier(entity, this) );
|
||||
}
|
||||
final List<T> list = new ArrayList<>(ids.length);
|
||||
for (Object id : ids) {
|
||||
final int pos = idList.indexOf(id);
|
||||
list.add( pos < 0 ? null : resultList.get(pos) );
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private EntityPersister getEntityPersister(String entityName) {
|
||||
return getFactory().getMappingMetamodel().getEntityDescriptor( entityName );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package org.hibernate.orm.test.loading.multiLoad;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = FindAllTest.Record.class)
|
||||
public class FindAllTest {
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
scope.inTransaction(s-> {
|
||||
s.persist(new Record(123L,"hello earth"));
|
||||
s.persist(new Record(456L,"hello mars"));
|
||||
});
|
||||
scope.inTransaction(s-> {
|
||||
List<Record> all = s.findAll(Record.class, 456L, 123L, 2L);
|
||||
assertEquals("hello mars",all.get(0).message);
|
||||
assertEquals("hello earth",all.get(1).message);
|
||||
assertNull(all.get(2));
|
||||
});
|
||||
}
|
||||
@Entity
|
||||
static class Record {
|
||||
@Id Long id;
|
||||
String message;
|
||||
|
||||
Record(Long id, String message) {
|
||||
this.id = id;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
Record() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.hibernate.orm.test.stateless;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = GetAllTest.Record.class)
|
||||
public class GetAllTest {
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
scope.inStatelessTransaction(s-> {
|
||||
s.insert(new Record(123L,"hello earth"));
|
||||
s.insert(new Record(456L,"hello mars"));
|
||||
});
|
||||
scope.inStatelessTransaction(s-> {
|
||||
List<Record> all = s.getAll(Record.class, 456L, 123L, 2L);
|
||||
assertEquals("hello mars",all.get(0).message);
|
||||
assertEquals("hello earth",all.get(1).message);
|
||||
assertNull(all.get(2));
|
||||
});
|
||||
}
|
||||
@Entity
|
||||
static class Record {
|
||||
@Id Long id;
|
||||
String message;
|
||||
|
||||
Record(Long id, String message) {
|
||||
this.id = id;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
Record() {
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue