HHH-15001 Hibernate.size(), Hibernate.contains(), Hibernate.get()

add @since tags
This commit is contained in:
Gavin King 2022-06-07 19:41:14 +01:00
parent 3cf6f2e3ef
commit 6c461a3674
5 changed files with 234 additions and 6 deletions

View File

@ -119,6 +119,68 @@ public final class Hibernate {
}
}
/**
* Obtain the {@linkplain Collection#size() size} of a persistent collection,
* without fetching its state from the database.
*
* @param collection a persistent collection associated with an open session
* @return the size of the collection
*
* @since 6.1.1
*/
public static int size(Collection<?> collection) {
return collection instanceof PersistentCollection
? ((PersistentCollection<?>) collection).getSize()
: collection.size();
}
/**
* Determine if the given persistent collection {@linkplain Collection#contains(Object) contains}
* the given element, without fetching its state from the database.
*
* @param collection a persistent collection associated with an open session
* @return true if the collection does contain the given element
*
* @since 6.1.1
*/
public static <T> boolean contains(Collection<? super T> collection, T element) {
return collection instanceof PersistentCollection
? ((PersistentCollection<?>) collection).elementExists(element)
: collection.contains(element);
}
/**
* Obtain the value associated with the given key by the given persistent
* map, without fetching the state of the map from the database.
*
* @param map a persistent map associated with an open session
* @param key a key belonging to the map
* @return the value associated by the map with the given key
*
* @since 6.1.1
*/
public static <K,V> V get(Map<? super K, V> map, K key) {
return map instanceof PersistentCollection
? (V) ((PersistentCollection<?>) map).elementByIndex(key)
: map.get(key);
}
/**
* Obtain the element of the given persistent list with the given index,
* without fetching the state of the list from the database.
*
* @param list a persistent list associated with an open session
* @param key an index belonging to the list
* @return the element of the list with the given index
*
* @since 6.1.1
*/
public static <T> T get(List<T> list, int key) {
return list instanceof PersistentCollection
? (T) ((PersistentCollection<?>) list).elementByIndex(key)
: list.get(key);
}
/**
* Get the true, underlying class of a proxied persistent class. This operation
* will initialize a proxy by side effect.
@ -225,6 +287,8 @@ public final class Hibernate {
* @param id the id of the persistent entity instance
*
* @return a detached uninitialized proxy
*
* @since 6.0
*/
@SuppressWarnings("unchecked")
public static <E> E createDetachedProxy(SessionFactory sessionFactory, Class<E> entityClass, Object id) {
@ -242,6 +306,8 @@ public final class Hibernate {
* Operations for obtaining references to persistent collections of a certain type.
*
* @param <C> the type of collection, for example, {@code List&lt;User&gt;}
*
* @since 6.0
*/
public static final class CollectionInterface<C> {
private final Supplier<C> detached;
@ -281,6 +347,8 @@ public final class Hibernate {
* of a given element type.
*
* @param <U> the element type
*
* @since 6.0
*/
public static <U> CollectionInterface<Collection<U>> bag() {
return new CollectionInterface<>(PersistentBag::new, ArrayList::new);
@ -291,6 +359,8 @@ public final class Hibernate {
* of a given element type.
*
* @param <U> the element type
*
* @since 6.0
*/
public static <U> CollectionInterface<Set<U>> set() {
return new CollectionInterface<>(PersistentSet::new, HashSet::new);
@ -301,6 +371,8 @@ public final class Hibernate {
* of a given element type.
*
* @param <U> the element type
*
* @since 6.0
*/
public static <U> CollectionInterface<List<U>> list() {
return new CollectionInterface<>(PersistentList::new, ArrayList::new);
@ -312,6 +384,8 @@ public final class Hibernate {
*
* @param <U> the key type
* @param <V> the value type
*
* @since 6.0
*/
public static <U,V> CollectionInterface<Map<U,V>> map() {
return new CollectionInterface<>(PersistentMap::new, HashMap::new);
@ -322,6 +396,8 @@ public final class Hibernate {
* of a given element type.
*
* @param <U> the element type
*
* @since 6.0
*/
public static <U> CollectionInterface<SortedSet<U>> sortedSet() {
return new CollectionInterface<>(PersistentSortedSet::new, TreeSet::new);
@ -334,6 +410,7 @@ public final class Hibernate {
* @param <U> the key type
* @param <V> the value type
*
* @since 6.0
*/
public static <U,V> CollectionInterface<Map<U,V>> sortedMap() {
return new CollectionInterface<>(PersistentSortedMap::new, TreeMap::new);
@ -344,6 +421,8 @@ public final class Hibernate {
* of the given type.
*
* @param collectionClass the Java class object representing the collection type
*
* @since 6.0
*/
@SuppressWarnings("unchecked")
public static <C> CollectionInterface<C> collection(Class<C> collectionClass) {

View File

@ -145,9 +145,10 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
return true;
}
else {
final boolean isExtraLazy = withTemporarySessionIfNeeded(
return withTemporarySessionIfNeeded(
() -> {
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
final CollectionEntry entry = session.getPersistenceContextInternal()
.getCollectionEntry(this);
if ( entry != null ) {
final CollectionPersister persister = entry.getLoadedPersister();
@ -162,18 +163,36 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
read();
}
}
else{
else {
throwLazyInitializationExceptionIfNotConnected();
}
return false;
}
);
return isExtraLazy;
}
}
return false;
}
public int getSize() {
if ( cachedSize>=0 ) {
return cachedSize;
}
CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry(this);
if ( entry == null ) {
throwLazyInitializationExceptionIfNotConnected();
throwLazyInitializationException("collection not associated with session");
throw new AssertionFailure("impossible");
}
else {
if ( hasQueuedOperations() ) {
session.flush();
}
cachedSize = entry.getLoadedPersister().getSize( entry.getLoadedKey(), session );
return cachedSize;
}
}
/**
* TBH not sure why this is public
*
@ -332,6 +351,22 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
return null;
}
@Override
public boolean elementExists(Object element) {
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
if ( entry == null ) {
throwLazyInitializationExceptionIfNotConnected();
throwLazyInitializationException("collection not associated with session");
throw new AssertionFailure("impossible");
}
else {
if ( hasQueuedOperations() ) {
session.flush();
}
return entry.getLoadedPersister().elementExists( entry.getLoadedKey(), element, session );
}
}
protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
protected Object readElementByIndex(final Object index) {
@ -368,6 +403,22 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
}
@Override
public Object elementByIndex(Object index) {
final CollectionEntry entry = session.getPersistenceContextInternal().getCollectionEntry( AbstractPersistentCollection.this );
if ( entry == null ) {
throwLazyInitializationExceptionIfNotConnected();
throwLazyInitializationException("collection not associated with session");
throw new AssertionFailure("impossible");
}
else {
if ( hasQueuedOperations() ) {
session.flush();
}
return entry.getLoadedPersister().getElementByIndex( entry.getLoadedKey(), index, session, owner );
}
}
protected int getCachedSize() {
return cachedSize;
}

View File

@ -452,6 +452,21 @@ public interface PersistentCollection<E> {
*/
Collection<E> getOrphans(Serializable snapshot, String entityName);
/**
* Obtain the size of this collection without initializing it
*/
int getSize();
/**
* Determine if the given element belongs to this collection without initializing it
*/
boolean elementExists(Object element);
/**
* Obtain the element os this collection associated with the given index without initializing it
*/
Object elementByIndex(Object index);
void initializeEmptyCollection(CollectionPersister persister);
/**

View File

@ -0,0 +1,69 @@
/*
* 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.orm.test.collection.basic;
import org.hibernate.Hibernate;
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.HashSet;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@DomainModel(
annotatedClasses = {
Contact.class, EmailAddress.class, User.class
}
)
@SessionFactory
public class CollectionSizeTest {
@Test
public void prepareData(SessionFactoryScope scope) {
Set<EmailAddress> emailAddresses = new HashSet<>();
emailAddresses.add( new EmailAddress( "test1@test.com" ) );
emailAddresses.add( new EmailAddress( "test2@test.com" ) );
emailAddresses.add( new EmailAddress( "test3@test.com" ) );
User user = new User();
user.setName( "john" );
Contact contact = new Contact();
contact.setName( "John Doe" );
contact.setEmailAddresses( emailAddresses );
emailAddresses.forEach( address -> contact.getContactsByEmail().put(address, contact) );
scope.inTransaction(
session -> {
user.setContact( contact );
session.persist( user );
session.persist( contact );
session.flush();
}
);
scope.inTransaction(
session -> {
Contact cont = session.get(Contact.class, contact.getId());
Set<EmailAddress> addresses = cont.getEmailAddresses();
assertEquals( Hibernate.size(addresses), 3 );
assertTrue( Hibernate.contains( addresses, new EmailAddress( "test1@test.com" ) ) );
assertFalse( Hibernate.contains( addresses, new EmailAddress( "test9@test.com" ) ) );
assertFalse( Hibernate.isInitialized(addresses) );
Map<EmailAddress, Contact> contactsByEmail = cont.getContactsByEmail();
assertEquals( cont, Hibernate.get(contactsByEmail, new EmailAddress( "test1@test.com" ) ) );
assertNull( Hibernate.get(contactsByEmail, new EmailAddress( "test9@test.com" ) ) );
assertFalse( Hibernate.isInitialized(contactsByEmail) );
}
);
}
}

View File

@ -7,7 +7,9 @@
package org.hibernate.orm.test.collection.basic;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.Basic;
import jakarta.persistence.CollectionTable;
@ -17,6 +19,7 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
@Entity
@ -26,8 +29,9 @@ public class Contact implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private Set<EmailAddress> emailAddresses = new HashSet<EmailAddress>();
private Set<EmailAddress> emailAddresses2 = new HashSet<EmailAddress>();
private Set<EmailAddress> emailAddresses = new HashSet<>();
private Set<EmailAddress> emailAddresses2 = new HashSet<>();
private Map<EmailAddress,Contact> contactsByEmail = new HashMap<>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@ -68,6 +72,16 @@ public class Contact implements Serializable {
this.emailAddresses = emailAddresses;
}
@ManyToMany
@CollectionTable(name="contact_email_addresses")
public Map<EmailAddress, Contact> getContactsByEmail() {
return contactsByEmail;
}
public void setContactsByEmail(Map<EmailAddress, Contact> contactsByEmail) {
this.contactsByEmail = contactsByEmail;
}
@Override
public int hashCode() {
int hash = 0;