
org.hibernate.Hibernate Maven / Gradle / Ivy
/*
* 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 .
*/
package org.hibernate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Supplier;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.collection.spi.PersistentBag;
import org.hibernate.collection.spi.PersistentList;
import org.hibernate.collection.spi.PersistentMap;
import org.hibernate.collection.spi.PersistentSet;
import org.hibernate.collection.spi.PersistentSortedMap;
import org.hibernate.collection.spi.PersistentSortedSet;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
/**
* Various utility functions for working with proxies and lazy collection references.
*
* Operations like {@link #isInitialized(Object)} and {@link #initialize(Object)} are
* of general purpose. But {@link #createDetachedProxy(SessionFactory, Class, Object)}
* and {@link CollectionInterface#createDetachedProxy(SessionFactory, Class, Object)}
* are intended for use by generic code that must materialize an "amputated" graph of
* Hibernate entities. (For example, a library which deserializes entities from JSON.)
*
* Graphs of Hibernate entities obtained from a {@link Session} are usually in an
* amputated form, with associations and collections replaced by proxies and lazy
* collections. (That is, by instances of the internal types {@link HibernateProxy}
* and {@link PersistentCollection}.) These objects are fully serializable using
* Java serialization, but can cause discomfort when working with custom serialization
* libraries. Therefore, this class defines operations that may be used to write code
* that completely removes the amputated leaves of the graph (the proxies) during
* serialization, and rematerializes and reattaches them during deserialization. It's
* possible, in principle, to use these operations, together with reflection, or with
* the Hibernate metamodel, to write such generic code for any given serialization
* library, but the details depend on what facilities the library itself offers for
* the program to intervene in the process of serialization and of deserialization.
*
* @author Gavin King
*/
public final class Hibernate {
/**
* Cannot be instantiated.
*/
private Hibernate() {
throw new UnsupportedOperationException();
}
/**
* Force initialization of a proxy or persistent collection. In the case of a
* many-valued association, only the collection itself is initialized. It is not
* guaranteed that the associated entities held within the collection will be
* initialized.
*
* @param proxy a persistable object, proxy, persistent collection or {@code null}
* @throws HibernateException if the proxy cannot be initialized at this time,
* for example, if the {@code Session} was closed
*/
public static void initialize(Object proxy) throws HibernateException {
if ( proxy == null ) {
return;
}
if ( proxy instanceof HibernateProxy ) {
( (HibernateProxy) proxy ).getHibernateLazyInitializer().initialize();
}
else if ( proxy instanceof PersistentCollection ) {
( (PersistentCollection>) proxy ).forceInitialization();
}
else if ( proxy instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) proxy;
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( proxy, null );
}
}
}
/**
* Check if the proxy or persistent collection is initialized.
*
* @param proxy a persistable object, proxy, persistent collection or {@code null}
* @return true if the argument is already initialized, or is not a proxy or collection
*/
public static boolean isInitialized(Object proxy) {
if ( proxy instanceof HibernateProxy ) {
return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized();
}
else if ( proxy instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) proxy ).$$_hibernate_getInterceptor();
return !(interceptor instanceof EnhancementAsProxyLazinessInterceptor);
}
else if ( proxy instanceof PersistentCollection ) {
return ( (PersistentCollection>) proxy ).wasInitialized();
}
else {
return true;
}
}
/**
* Get the true, underlying class of a proxied persistent class. This operation
* will initialize a proxy by side effect.
*
* @param proxy an entity instance or proxy
* @return the true class of the instance
*/
@SuppressWarnings("unchecked")
public static Class extends T> getClass(T proxy) {
Class> result;
if ( proxy instanceof HibernateProxy ) {
result = ( (HibernateProxy) proxy ).getHibernateLazyInitializer()
.getImplementation()
.getClass();
}
else {
result = proxy.getClass();
}
return (Class extends T>) result;
}
/**
* Check if the property is initialized. If the named property does not exist or
* is not persistent, this method always returns {@code true}.
*
* @param proxy The potential proxy
* @param propertyName the name of a persistent attribute of the object
* @return true if the named property of the object is not listed as uninitialized;
* false otherwise
*/
public static boolean isPropertyInitialized(Object proxy, String propertyName) {
final Object entity;
if ( proxy instanceof HibernateProxy ) {
final LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
if ( li.isUninitialized() ) {
return false;
}
else {
entity = li.getImplementation();
}
}
else {
entity = proxy;
}
if ( entity instanceof PersistentAttributeInterceptable ) {
PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) {
return ( (BytecodeLazyAttributeInterceptor) interceptor ).isAttributeLoaded( propertyName );
}
}
return true;
}
/**
* If the given object is not a proxy, return it. But, if it is a proxy, ensure
* that the proxy is initialized, and return a direct reference to its proxied
* entity object.
*
* @param proxy an object which might be a proxy for an entity
* @return a reference that is never proxied
*
* @throws LazyInitializationException if this operation is called on an
* uninitialized proxy that is not associated with an open session.
*/
public static Object unproxy(Object proxy) {
if ( proxy instanceof HibernateProxy ) {
HibernateProxy hibernateProxy = (HibernateProxy) proxy;
LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer();
return initializer.getImplementation();
}
else {
return proxy;
}
}
/**
* If the given object is not a proxy, cast it to the given type, and return it.
* But, if it is a proxy, ensure that the proxy is initialized, and return a
* direct reference to its proxied entity object, after casting to the given type.
*
* @param proxy an object which might be a proxy for an entity
* @param entityClass an entity type to cast to
* @return a reference that is never proxied
*
* @throws LazyInitializationException if this operation is called on an
* uninitialized proxy that is not associated with an open session.
*/
public static T unproxy(T proxy, Class entityClass) {
return entityClass.cast( unproxy( proxy ) );
}
/**
* Obtain a detached, uninitialized reference (a proxy) for a persistent entity with
* the given identifier.
*
* The returned proxy is not associated with any session, and cannot be initialized
* by calling {@link #initialize(Object)}. It can be used to represent a reference to
* the entity when working with a detached object graph.
*
* @param sessionFactory the session factory with which the entity is associated
* @param entityClass the entity class
* @param id the id of the persistent entity instance
*
* @return a detached uninitialized proxy
*/
@SuppressWarnings("unchecked")
public static E createDetachedProxy(SessionFactory sessionFactory, Class entityClass, Object id) {
final EntityPersister persister = sessionFactory.unwrap(SessionFactoryImplementor.class)
.getRuntimeMetamodels()
.getMappingMetamodel()
.findEntityDescriptor(entityClass);
if (persister==null) {
throw new UnknownEntityTypeException("unknown entity type");
}
return (E) persister.createProxy(id, null);
}
/**
* Operations for obtaining references to persistent collections of a certain type.
*
* @param the type of collection, for example, {@code List<User>}
*/
public static final class CollectionInterface {
private final Supplier detached;
private final Supplier created;
private CollectionInterface(Supplier detached, Supplier created) {
this.detached = detached;
this.created = created;
}
/**
* Obtain a detached, uninitialized persistent collection of the type represented
* by this object.
*
* The returned wrapper object is not associated with any session, and cannot be
* initialized by calling {@link #initialize(Object)}. It can be used to represent
* an uninitialized collection when working with a detached object graph.
*
* @return an uninitialized persistent collection
*/
public C createDetachedInstance() {
return detached.get();
}
/**
* Instantiate an empty collection of the type represented by this object.
*
* @return a newly-instantiated empty collection
*/
public C createNewInstance() {
return created.get();
}
}
/**
* Obtain an instance of {@link CollectionInterface} representing persistent bags
* of a given element type.
*
* @param the element type
*/
public static CollectionInterface> bag() {
return new CollectionInterface<>(PersistentBag::new, ArrayList::new);
}
/**
* Obtain an instance of {@link CollectionInterface} representing persistent sets
* of a given element type.
*
* @param the element type
*/
public static CollectionInterface> set() {
return new CollectionInterface<>(PersistentSet::new, HashSet::new);
}
/**
* Obtain an instance of {@link CollectionInterface} representing persistent lists
* of a given element type.
*
* @param the element type
*/
public static CollectionInterface> list() {
return new CollectionInterface<>(PersistentList::new, ArrayList::new);
}
/**
* Obtain an instance of {@link CollectionInterface} representing persistent maps
* of a given key and value types.
*
* @param the key type
* @param the value type
*/
public static CollectionInterface