Merge remote-tracking branch 'upstream/master' into wip/6.0

This commit is contained in:
Andrea Boriero 2020-10-23 10:31:29 +01:00
commit 4e22c5cabc
17 changed files with 590 additions and 14 deletions

4
design/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.asciidoctor
*.png
*.html
*.pdf

424
design/mapping-model.adoc Normal file
View File

@ -0,0 +1,424 @@
:link-parallelArrays: https://en.wikipedia.org/wiki/Parallel_array["parallel arrays"]
= Domain Model Mappings
:toc2:
:toclevels: 3
:sectanchors:
:numbered:
Describes Hibernate's handling of domain model metadata
== Mapping sources
Mapping sources include `hbm.xml` files, `orm.xml` files and annotated classes. There are
other specialized forms of sources, but they all come back to locating annotated classes
and XML mappings.
The main actors in managing the sources include:
`MetadataSources`::
Used to collect mapping sources to be processed together
`JaxbHbmHibernateMapping`::
Main JAXB binding for a `hbm.xml` mapping document
`Binding`::
Represents an XML mapping within the `MetadataSources`. Wraps either a
`JaxbHbmHibernateMapping` or DOM `Document` representing a JPA `orm.xml`.
`MappingBinder`::
Responsible for generating `Binding` instances.
The handling for XML mappings is as follows:
[plantuml,hbm,png]
.hbm.xml processing
----
@startuml
skinparam handwritten true
Application -> MetadataSources : add(hbm.xml)
MetadataSources -> MappingBinder : parse(hbm.xml)
MappingBinder -> JAXB : bind(hbm.xml)
MappingBinder <- JAXB : JaxbHbmHibernateMapping
MetadataSources <- MappingBinder : Binding<JaxbHbmHibernateMapping>
@enduml
----
[plantuml,orm,png]
.orm.xml processing
----
@startuml
skinparam handwritten true
Application -> MetadataSources : add(orm.xml)
MetadataSources -> MappingBinder : parse(orm.xml)
MappingBinder -> DOM : bind(orm.xml)
MappingBinder <- DOM : Document
MetadataSources <- MappingBinder : Binding<Document>
@enduml
----
NOTE: `MetadataSources` receives XML files without any knowledge of whether the file
is a Hibernate mapping (`hbm.xml`) or a JPA mapping (`orm.xml`). `MappingBinder` makes
that distinction based on doctype, schema, etc.
== Boot-time metamodel
The `org.hibernate.mapping` package defines most of the boot-time model.
[plantuml,bootmodel,png]
.Boot model actors
----
@startmindmap
skinparam handwritten true
+ Boot model
++ PersistentClass
++ Property
++ Value
++ IdGenerator
++ TypeDef
-- Table
-- Selectable
-- PrimaryKey
-- Constraint
@endmindmap
----
=== PersistentClass
Models an entity
[plantuml,persistentclass,png]
.PersistentClass hierarchy
----
@startuml
interface Value
class Property
Property -- Value : value >
class PersistentClass {
entityName : String
}
PersistentClass *- Property : properties >
class RootClass {
table : Table
}
class JoinedSubclass {
table : Table
}
class UnionSubclass {
table : Table
}
PersistentClass <|-- RootClass
PersistentClass <|-- Subclass
Subclass <|-- JoinedSubclass
Subclass <|-- SingleTableSubclass
Subclass <|-- UnionSubclass
@enduml
----
=== Value
Models a value. A value ultimately corresponds to a `org.hibernate.type.Type`. We will discuss
this "simple" distinction when we talk about Types in the run-time metamodel section.
[plantuml,value,png]
.Value hierarchy
----
@startuml
class SimpleValue
note left of SimpleValue : By itself represents\na basic value
class OneToMany
note top of OneToMany : Used as element descriptor for\none-to-many collections
Value <|-- KeyValue
Value <|-- OneToMany
KeyValue <|-- SimpleValue
SimpleValue <|-- DependentValue
SimpleValue <|-- Component
SimpleValue <|-- Any
SimpleValue <|-- ToOne
ToOne <|-- ManyToOne
ToOne <|-- OneToOne
Value <|-- Collection
Collection <|-- Bag
Collection <|-- Set
Collection <|-- IdentifierCollection
IdentifierCollection <|-- IdentifierBag
Collection <|-- IndexedCollection
IndexedCollection <|-- List
List <|-- Array
IndexedCollection <|-- Map
@enduml
----
=== Database model
[plantuml,db,png]
.Database model
----
@startuml
class Identifier
Identifier : String text
Identifier : boolean quoted
Selectable <|-- Column
Column : Identifider name
Selectable <|-- Formula
Formula : String fragment
Constraint <|-- PrimaryKey
Constraint <|-- UniqueKey
Constraint <|-- ForeignKey
class Table
Table : Identifier name
Table : Identifier schema
Table : Identifier catalog
Table : PrimaryKey : primaryKey
Table : Selectable : selectables
class Index
class Sequence
interface Exportable
Exportable <|-- Table
Exportable <|-- Constraint
Exportable <|-- Index
Exportable <|-- Sequence
Exportable <|-- AuxilaryDatabaseObject
interface TableOwner
TableOwner : Table table
TableOwner <|-- RootClass
TableOwner <|-- JoinedSubclass
TableOwner <|-- UnionSubclass
@enduml
----
=== Transition from sources to boot-time model
The boot-time metamodel is built iteratively. The general paradigm in this transition is to
instantiate one of these boot-time objects which are then populated in multiple later steps (via
setters, additions, etc).
The main actors in this process are `HbmMetadataSourceProcessorImpl` and `AnnotationMetadataSourceProcessorImpl`.
[plantuml,source2boot,png]
.Transition sources to boot-time model
----
@startuml
skinparam handwritten true
autonumber
Application -> MetadataBuilder : build()
MetadataBuilder -> MetadataBuildingProcess : build()
MetadataBuildingProcess -> MetadataSourceProcessor
MetadataSourceProcessor -> HbmMetadataSourceProcessorImpl : process hbm.xml Bindings
MetadataSourceProcessor -> AnnotationMetadataSourceProcessorImpl : process annotations + orm.xml Bindings
MetadataBuilder <- MetadataBuildingProcess : Metadata
Application <- MetadataBuilder : Metadata
@enduml
----
== Run-time metamodel
[plantuml,runtimemodel,png]
.Run-time model actors
----
@startmindmap
skinparam handwritten true
+ Boot model
++ EntityPersister
++ CollectionPersister
++ Tuplizer
-- Type
-- IdentifierGenerator
@endmindmap
----
=== EntityPersister
Manages persistence of an entity to/from its defined table(s). Maintains flattened
state regarding various aspects of the entity's value mappings as {link-parallelArrays}.
An entity's value mappings include:
* identifier
* attribute state
* (discriminator)
* (version)
[plantuml,entitypersister,png]
.EntityPersister hierarchy
----
@startuml
skinparam handwritten true
interface EntityPersister
abstract class AbstractEntityPersister
EntityPersister <|-- AbstractEntityPersister
AbstractEntityPersister <|-- SingleTableEntityPersister
AbstractEntityPersister <|-- JoinedEntityPersister
AbstractEntityPersister <|-- UnionEntityPersister
@enduml
----
=== CollectionPersister
Manages persistence of a collection to its defined table(s). Maintains flattened
state as {link-parallelArrays} regarding various aspects of the value mappings making
up the collection. These aspects include:
* key -- the FK
* element
* (identifier) -- @IdBag
* (list-index | map-key)
[plantuml,collectionpersister,png]
.CollectionPersister hierarchy
----
@startuml
skinparam handwritten true
interface CollectionPersister
abstract class AbstractCollectionPersister
CollectionPersister <|-- CollectionPersister
AbstractCollectionPersister <|-- BasicCollectionPersister
AbstractCollectionPersister <|-- OneToManyCollectionPersister
note left of BasicCollectionPersister : collection mappings\nwith a collection table
@enduml
----
=== Type
Describes a value mapping which is some form of non-identified state.
[plantuml,type,png]
.Type hierarchy
----
@startuml
skinparam handwritten true
interface Type
interface IdentifierType
Type <|-- IdentifierType
interface DiscriminatorType
IdentifierType <|-- DiscriminatorType
interface VersionType
Type <|-- VersionType
interface BasicType
Type <|-- BasicType
interface CompositeType
Type <|-- CompositeType
CompositeType *- Type : subtypes
interface AssociationType
Type <|-- AssociationType
interface AnyType {
discriminatorType : DiscriminatorType
identifierType : IdentifierType
}
AssociationType <|-- AnyType
CompositeType <|-- AnyType
interface UserType
interface CustomType
CustomType -- UserType : wrappedUserType
Type <|-- CustomType
@enduml
----
`IdentifierType`::
Specialized Type contract for types that can be used as an identifier
`DiscriminatorType`::
Specialized Type contract for types that can be used as a discriminator
`VersionType`::
Specialized Type contract for types that can be used as a version
`BasicType`::
Mapping to a single column
`CompositeType`::
Mapping to one or more columns
`AssociationType`::
Mapping to an entity association
`AnyType`::
Models a discriminated association which is similar to an association referencing a
discriminated-subclass entity in that the mapping involves a discriminator. However,
in an ANY mapping the discriminator is on the referring side. This will map to at least
2 columns - one for the discriminator plus one or more identifier columns.
`EntityType`::
Models a foreign-key, which "from this side" is a to-one. Could map to a single column or multiple.
`CollectionType`::
Models a foreign-key, which "from this side" is a to-many. Will map to at
=== Transition from boot-time model to run-time model
This transition involves processing the boot model objects (`PersistentClass`, `Value`, etc) and building
the corresponding run-time counterparts (`EntityPersister`, `Type`, etc).
The main actors in this transition are the `SessionFactory` itself, `MetamodelImplementor` and
`TypeConfiguration`:
[plantuml,boot2run,png]
.Transition boot-time model to run-time model
----
@startuml
skinparam handwritten true
Application -> SessionFactoryBuilder : build()
SessionFactoryBuilder -> SessionFactory : new
SessionFactory -> TypeConfiguration : scope
...
@enduml
----
##

View File

@ -684,7 +684,7 @@ Sets the associated collection cache concurrency strategy for the designated reg
=== Infinispan properties
For more details about how to customize the Infinispan second-level cache provider, check out the
http://infinispan.org/docs/stable/user_guide/user_guide.html#configuration_properties[Infinispan User Guide].
http://infinispan.org/docs/stable/titles/integrating/integrating.html#configuration_properties[Infinispan User Guide].
[[configurations-transactions]]
=== Transactions properties

View File

@ -707,4 +707,4 @@ Infinispan is a distributed in-memory key/value data store, available as a cache
It supports advanced functionality such as transactions, events, querying, distributed processing, off-heap and geographical failover.
For more details, check out the
http://infinispan.org/docs/stable/user_guide/user_guide.html#integrations_jpa_hibernate[Infinispan User Guide].
http://infinispan.org/docs/stable/titles/integrating/integrating.html#integrating_jpa_hibernate[Infinispan User Guide].

View File

@ -8,7 +8,7 @@ package org.hibernate.boot.archive.internal;
import java.net.URL;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.AssertionFailure;
import org.hibernate.boot.archive.spi.ArchiveContext;
import org.hibernate.boot.archive.spi.ArchiveDescriptor;
import org.hibernate.boot.archive.spi.ArchiveDescriptorFactory;

View File

@ -86,6 +86,8 @@ import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;

View File

@ -1083,7 +1083,7 @@ public class BinderHelper {
if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
if ( inheritanceState == null ) {
throw new org.hibernate.annotations.common.AssertionFailure(
throw new AssertionFailure(
"Declaring class is not found in the inheritance state hierarchy: " + declaringClass
);
}

View File

@ -12,7 +12,7 @@ import javax.persistence.Convert;
import javax.persistence.Converts;
import javax.persistence.JoinTable;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;

View File

@ -31,6 +31,7 @@ import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.BatchSize;
@ -64,7 +65,6 @@ import org.hibernate.annotations.SortNatural;
import org.hibernate.annotations.SortType;
import org.hibernate.annotations.Where;
import org.hibernate.annotations.WhereJoinTable;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;

View File

@ -13,6 +13,7 @@ import javax.persistence.Id;
import javax.persistence.Lob;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.annotations.AttributeAccessor;
import org.hibernate.annotations.Generated;
@ -20,7 +21,6 @@ import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;

View File

@ -12,8 +12,8 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.validation.Path;
import javax.validation.TraversableResolver;
import org.hibernate.AssertionFailure;
import org.hibernate.Hibernate;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CollectionType;

View File

@ -10,7 +10,6 @@ import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.boot.registry.selector.spi.StrategySelectionException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings;
@ -21,6 +20,7 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;

View File

@ -17,11 +17,10 @@ import javax.naming.event.NamingExceptionEvent;
import javax.naming.spi.ObjectFactory;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.engine.jndi.JndiException;
import org.hibernate.engine.jndi.JndiNameException;
import org.hibernate.engine.jndi.spi.JndiService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
/**
* A registry of all {@link SessionFactory} instances for the same classloader as this class.

View File

@ -17,10 +17,10 @@ import java.util.List;
import java.util.Locale;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.internal.util.StringHelper;
/**
* A relational constraint.

View File

@ -16,8 +16,8 @@ import javax.persistence.OneToOne;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Type;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.AssertionFailure;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.mapping.Collection;

View File

@ -23,7 +23,8 @@ import javax.persistence.metamodel.Type;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.internal.util.ReflectHelper;

View File

@ -0,0 +1,146 @@
/*
* 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.test.criteria.size;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Root;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertThat;
/**
* @author Andrea Boriero
*/
@TestForIssue(jiraKey = "HHH014245")
public class CriteriaSelectSizeCollectionTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Customer.class, Alias.class };
}
@Before
public void setUp() {
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = new Customer( "1", "Phil" );
Alias alias = new Alias( "2", "p" );
customer.addAlias( alias );
entityManager.persist( customer );
}
);
}
@Test
public void testSelectCollectionSize() {
doInJPA( this::entityManagerFactory, entityManager -> {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Integer> query = criteriaBuilder.createQuery( Integer.class );
Root<Customer> customer = query.from( Customer.class );
Expression<Integer> aliases = criteriaBuilder.size( customer.get( "aliases" ) );
query.select( aliases );
query.where( criteriaBuilder.equal( customer.get( "id" ), "1" ) );
TypedQuery<Integer> tq = entityManager.createQuery( query );
Integer size = tq.getSingleResult();
assertThat( size, is( 1 ) );
}
);
}
@Entity(name = "Customer")
@Table(name = "CUSTOMER_TABLE")
public static class Customer {
@Id
private String id;
private String name;
@ManyToMany(cascade = CascadeType.ALL)
private Collection<Alias> aliases = new ArrayList<>();
public Customer() {
}
public Customer(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addAlias(Alias alias) {
aliases.add( alias );
}
}
@Entity(name = "Alias")
@Table(name = "ALIAS_TABLE")
public static class Alias implements java.io.Serializable {
@Id
private String id;
private String alias;
public Alias() {
}
public Alias(String id, String alias) {
this.id = id;
this.alias = alias;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
}
}