HHH-14529 Implement the (opt-in) orm.xml handling using JAXB

This commit is contained in:
Yoann Rodière 2021-03-30 16:20:03 +02:00
parent 10aee2a9fa
commit ac4f4ff4ad
10 changed files with 1305 additions and 1093 deletions

View File

@ -0,0 +1,31 @@
/*
* 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.boot.jaxb.mapping.spi;
import java.io.Serializable;
import java.util.List;
import javax.persistence.LockModeType;
public interface NamedQuery extends Serializable {
String getDescription();
void setDescription(String value);
String getQuery();
void setQuery(String value);
LockModeType getLockMode();
void setLockMode(LockModeType value);
List<JaxbQueryHint> getHint();
String getName();
void setName(String value);
}

View File

@ -23,20 +23,19 @@ import javax.persistence.TableGenerator;
import org.hibernate.annotations.common.reflection.AnnotationReader;
import org.hibernate.annotations.common.reflection.MetadataProvider;
import org.hibernate.annotations.common.reflection.java.JavaMetadataProvider;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGenerator;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGenerator;
import org.hibernate.boot.jaxb.spi.XmlMappingOptions;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.ClassLoaderAccess;
import org.dom4j.Element;
/**
* MetadataProvider aware of the JPA Deployment descriptor (orm.xml, ...).
*
* @author Emmanuel Bernard
*/
// FIXME HHH-14529 Change this class to use JaxbEntityMappings instead of Document.
// I'm delaying this change in order to keep the commits simpler and easier to review.
@SuppressWarnings("unchecked")
public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider {
@ -112,28 +111,27 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider
}
}
defaults.put( EntityListeners.class, entityListeners );
for ( Element element : xmlContext.getAllDocuments() ) {
@SuppressWarnings( "unchecked" )
List<Element> elements = element.elements( "sequence-generator" );
for ( JaxbEntityMappings entityMappings : xmlContext.getAllDocuments() ) {
List<JaxbSequenceGenerator> jaxbSequenceGenerators = entityMappings.getSequenceGenerator();
List<SequenceGenerator> sequenceGenerators = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
if ( sequenceGenerators == null ) {
sequenceGenerators = new ArrayList<>();
defaults.put( SequenceGenerator.class, sequenceGenerators );
}
for ( Element subelement : elements ) {
sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) );
for ( JaxbSequenceGenerator element : jaxbSequenceGenerators ) {
sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( element ) );
}
elements = element.elements( "table-generator" );
List<JaxbTableGenerator> jaxbTableGenerators = entityMappings.getTableGenerator();
List<TableGenerator> tableGenerators = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
if ( tableGenerators == null ) {
tableGenerators = new ArrayList<>();
defaults.put( TableGenerator.class, tableGenerators );
}
for ( Element subelement : elements ) {
for ( JaxbTableGenerator element : jaxbTableGenerators ) {
tableGenerators.add(
JPAXMLOverriddenAnnotationReader.buildTableGeneratorAnnotation(
subelement, xmlDefaults
element, xmlDefaults
)
);
}
@ -144,8 +142,7 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider
defaults.put( NamedQuery.class, namedQueries );
}
List<NamedQuery> currentNamedQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries(
element,
false,
entityMappings.getNamedQuery(),
xmlDefaults,
classLoaderAccess
);
@ -156,9 +153,8 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider
namedNativeQueries = new ArrayList<>();
defaults.put( NamedNativeQuery.class, namedNativeQueries );
}
List<NamedNativeQuery> currentNamedNativeQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries(
element,
true,
List<NamedNativeQuery> currentNamedNativeQueries = JPAXMLOverriddenAnnotationReader.buildNamedNativeQueries(
entityMappings.getNamedNativeQuery(),
xmlDefaults,
classLoaderAccess
);
@ -172,7 +168,7 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider
defaults.put( SqlResultSetMapping.class, sqlResultSetMappings );
}
List<SqlResultSetMapping> currentSqlResultSetMappings = JPAXMLOverriddenAnnotationReader.buildSqlResultsetMappings(
element,
entityMappings.getSqlResultSetMapping(),
xmlDefaults,
classLoaderAccess
);
@ -185,7 +181,7 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider
}
List<NamedStoredProcedureQuery> currentNamedStoredProcedureQueries = JPAXMLOverriddenAnnotationReader
.buildNamedStoreProcedureQueries(
element,
entityMappings.getNamedStoredProcedureQuery(),
xmlDefaults,
classLoaderAccess
);

View File

@ -0,0 +1,214 @@
/*
* 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.cfg.annotations.reflection.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import org.hibernate.boot.jaxb.mapping.spi.AttributesContainer;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributes;
import org.hibernate.boot.jaxb.mapping.spi.JaxbBasic;
import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollection;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbedded;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedId;
import org.hibernate.boot.jaxb.mapping.spi.JaxbId;
import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToMany;
import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToOne;
import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToMany;
import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToOne;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPostLoad;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPostPersist;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPostRemove;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPostUpdate;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPrePersist;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPreRemove;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPreUpdate;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTransient;
import org.hibernate.boot.jaxb.mapping.spi.JaxbVersion;
import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback;
import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer;
import org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute;
/**
* Reproduces what we used to do with a {@code List<Element>} in {@link JPAXMLOverriddenAnnotationReader},
* with the following constraints:
* <ul>
* <li>Preserve type safety</li>
* <li>Only create lists if we actually have elements (most lists should be empty in most cases)</li>
* </ul>
*/
final class PropertyMappingElementCollector {
static final Function<PersistentAttribute, String> PERSISTENT_ATTRIBUTE_NAME = PersistentAttribute::getName;
static final Function<JaxbTransient, String> JAXB_TRANSIENT_NAME = JaxbTransient::getName;
static final Function<LifecycleCallback, String> LIFECYCLE_CALLBACK_NAME = LifecycleCallback::getMethodName;
private final String propertyName;
private List<JaxbId> id;
private List<JaxbEmbeddedId> embeddedId;
private List<JaxbBasic> basic;
private List<JaxbVersion> version;
private List<JaxbManyToOne> manyToOne;
private List<JaxbOneToMany> oneToMany;
private List<JaxbOneToOne> oneToOne;
private List<JaxbManyToMany> manyToMany;
private List<JaxbElementCollection> elementCollection;
private List<JaxbEmbedded> embedded;
private List<JaxbTransient> _transient;
private List<JaxbPrePersist> prePersist;
private List<JaxbPostPersist> postPersist;
private List<JaxbPreRemove> preRemove;
private List<JaxbPostRemove> postRemove;
private List<JaxbPreUpdate> preUpdate;
private List<JaxbPostUpdate> postUpdate;
private List<JaxbPostLoad> postLoad;
PropertyMappingElementCollector(String propertyName) {
this.propertyName = propertyName;
}
public boolean isEmpty() {
return allNullOrEmpty( id, embeddedId, basic, version, manyToOne, oneToMany, oneToOne, manyToMany,
elementCollection, embedded, _transient,
prePersist, postPersist, preRemove, postRemove, preUpdate, postUpdate, postLoad );
}
private boolean allNullOrEmpty(List<?>... lists) {
for ( List<?> list : lists ) {
if ( list != null && !list.isEmpty() ) {
return false;
}
}
return true;
}
private <T> List<T> defaultToEmpty(List<T> list) {
return list == null ? Collections.emptyList() : list;
}
public void collectPersistentAttributesIfMatching(AttributesContainer container) {
if ( container instanceof JaxbAttributes ) {
JaxbAttributes jaxbAttributes = (JaxbAttributes) container;
id = collectIfMatching( id, jaxbAttributes.getId(), PERSISTENT_ATTRIBUTE_NAME );
embeddedId = collectIfMatching( embeddedId, jaxbAttributes.getEmbeddedId(), PERSISTENT_ATTRIBUTE_NAME );
version = collectIfMatching( version, jaxbAttributes.getVersion(), PERSISTENT_ATTRIBUTE_NAME );
}
basic = collectIfMatching( basic, container.getBasic(), PERSISTENT_ATTRIBUTE_NAME );
manyToOne = collectIfMatching( manyToOne, container.getManyToOne(), PERSISTENT_ATTRIBUTE_NAME );
oneToMany = collectIfMatching( oneToMany, container.getOneToMany(), PERSISTENT_ATTRIBUTE_NAME );
oneToOne = collectIfMatching( oneToOne, container.getOneToOne(), PERSISTENT_ATTRIBUTE_NAME );
manyToMany = collectIfMatching( manyToMany, container.getManyToMany(), PERSISTENT_ATTRIBUTE_NAME );
elementCollection = collectIfMatching( elementCollection, container.getElementCollection(), PERSISTENT_ATTRIBUTE_NAME );
embedded = collectIfMatching( embedded, container.getEmbedded(), PERSISTENT_ATTRIBUTE_NAME );
_transient = collectIfMatching( _transient, container.getTransient(), JAXB_TRANSIENT_NAME );
}
public void collectLifecycleCallbacksIfMatching(LifecycleCallbackContainer container) {
prePersist = collectIfMatching( prePersist, container.getPrePersist(), LIFECYCLE_CALLBACK_NAME );
postPersist = collectIfMatching( postPersist, container.getPostPersist(), LIFECYCLE_CALLBACK_NAME );
preRemove = collectIfMatching( preRemove, container.getPreRemove(), LIFECYCLE_CALLBACK_NAME );
postRemove = collectIfMatching( postRemove, container.getPostRemove(), LIFECYCLE_CALLBACK_NAME );
preUpdate = collectIfMatching( preUpdate, container.getPreUpdate(), LIFECYCLE_CALLBACK_NAME );
postUpdate = collectIfMatching( postUpdate, container.getPostUpdate(), LIFECYCLE_CALLBACK_NAME );
postLoad = collectIfMatching( postLoad, container.getPostLoad(), LIFECYCLE_CALLBACK_NAME );
}
private <T> List<T> collectIfMatching(List<T> collected, List<T> candidates,
Function<? super T, String> nameGetter) {
List<T> result = collected;
for ( T candidate : candidates ) {
result = collectIfMatching( result, candidate, nameGetter );
}
return result;
}
private <T> List<T> collectIfMatching(List<T> collected, T candidate, Function<? super T, String> nameGetter) {
List<T> result = collected;
if ( candidate != null && propertyName.equals( nameGetter.apply( candidate ) ) ) {
if ( result == null ) {
result = new ArrayList<>();
}
result.add( candidate );
}
return result;
}
public List<JaxbId> getId() {
return defaultToEmpty( id );
}
public List<JaxbEmbeddedId> getEmbeddedId() {
return defaultToEmpty( embeddedId );
}
public List<JaxbBasic> getBasic() {
return defaultToEmpty( basic );
}
public List<JaxbVersion> getVersion() {
return defaultToEmpty( version );
}
public List<JaxbManyToOne> getManyToOne() {
return defaultToEmpty( manyToOne );
}
public List<JaxbOneToMany> getOneToMany() {
return defaultToEmpty( oneToMany );
}
public List<JaxbOneToOne> getOneToOne() {
return defaultToEmpty( oneToOne );
}
public List<JaxbManyToMany> getManyToMany() {
return defaultToEmpty( manyToMany );
}
public List<JaxbElementCollection> getElementCollection() {
return defaultToEmpty( elementCollection );
}
public List<JaxbEmbedded> getEmbedded() {
return defaultToEmpty( embedded );
}
public List<JaxbTransient> getTransient() {
return defaultToEmpty( _transient );
}
public List<JaxbPrePersist> getPrePersist() {
return defaultToEmpty( prePersist );
}
public List<JaxbPostPersist> getPostPersist() {
return defaultToEmpty( postPersist );
}
public List<JaxbPreRemove> getPreRemove() {
return defaultToEmpty( preRemove );
}
public List<JaxbPostRemove> getPostRemove() {
return defaultToEmpty( postRemove );
}
public List<JaxbPreUpdate> getPreUpdate() {
return defaultToEmpty( preUpdate );
}
public List<JaxbPostUpdate> getPostUpdate() {
return defaultToEmpty( postUpdate );
}
public List<JaxbPostLoad> getPostLoad() {
return defaultToEmpty( postLoad );
}
}

View File

@ -16,89 +16,81 @@ import javax.persistence.AttributeConverter;
import org.hibernate.AnnotationException;
import org.hibernate.boot.AttributeConverterInfo;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConverter;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListener;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListeners;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.mapping.spi.JaxbMappedSuperclass;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaults;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitMetadata;
import org.hibernate.boot.jaxb.mapping.spi.ManagedType;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.ClassLoaderAccess;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.dom4j.Document;
import org.dom4j.Element;
/**
* A helper for consuming orm.xml mappings.
*
* @author Emmanuel Bernard
* @author Brett Meyer
*/
// FIXME HHH-14529 Change this class to use JaxbEntityMappings instead of Document.
// I'm delaying this change in order to keep the commits simpler and easier to review.
public class XMLContext implements Serializable {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( XMLContext.class );
private final ClassLoaderAccess classLoaderAccess;
private Default globalDefaults;
private Map<String, Element> classOverriding = new HashMap<>();
private Map<String, Default> defaultsOverriding = new HashMap<>();
private List<Element> defaultElements = new ArrayList<>();
private List<String> defaultEntityListeners = new ArrayList<>();
private final Map<String, ManagedType> managedTypeOverride = new HashMap<>();
private final Map<String, JaxbEntityListener> entityListenerOverride = new HashMap<>();
private final Map<String, Default> defaultsOverride = new HashMap<>();
private final List<JaxbEntityMappings> defaultElements = new ArrayList<>();
private final List<String> defaultEntityListeners = new ArrayList<>();
private boolean hasContext = false;
XMLContext(ClassLoaderAccess classLoaderAccess) {
/**
* @deprecated Use {@link org.hibernate.cfg.annotations.reflection.XMLContext#XMLContext(BootstrapContext)} instead.
*/
@Deprecated
public XMLContext(ClassLoaderAccess classLoaderAccess) {
this.classLoaderAccess = classLoaderAccess;
}
// For tests only
public XMLContext(BootstrapContext bootstrapContext) {
this.classLoaderAccess = bootstrapContext.getClassLoaderAccess();
}
/**
* @param entityMappings The xml entity mappings to add
* @return Add an xml document to this context and return the list of added class names.
*/
public List<String> addDocument(JaxbEntityMappings entityMappings) {
throw new NotYetImplementedException("HHH-14529 Implementation in progress");
}
/**
* @param doc The xml document to add
* @param entityMappings The xml document to add
* @return Add an xml document to this context and return the list of added class names.
*/
@SuppressWarnings( "unchecked" )
public List<String> addDocument(Document doc) {
public List<String> addDocument(JaxbEntityMappings entityMappings) {
hasContext = true;
List<String> addedClasses = new ArrayList<>();
Element root = doc.getRootElement();
//global defaults
Element metadata = root.element( "persistence-unit-metadata" );
JaxbPersistenceUnitMetadata metadata = entityMappings.getPersistenceUnitMetadata();
if ( metadata != null ) {
if ( globalDefaults == null ) {
globalDefaults = new Default();
globalDefaults.setMetadataComplete(
metadata.element( "xml-mapping-metadata-complete" ) != null ?
metadata.getXmlMappingMetadataComplete() != null ?
Boolean.TRUE :
null
);
Element defaultElement = metadata.element( "persistence-unit-defaults" );
JaxbPersistenceUnitDefaults defaultElement = metadata.getPersistenceUnitDefaults();
if ( defaultElement != null ) {
Element unitElement = defaultElement.element( "schema" );
globalDefaults.setSchema( unitElement != null ? unitElement.getTextTrim() : null );
unitElement = defaultElement.element( "catalog" );
globalDefaults.setCatalog( unitElement != null ? unitElement.getTextTrim() : null );
unitElement = defaultElement.element( "access" );
setAccess( unitElement, globalDefaults );
unitElement = defaultElement.element( "cascade-persist" );
globalDefaults.setCascadePersist( unitElement != null ? Boolean.TRUE : null );
unitElement = defaultElement.element( "delimited-identifiers" );
globalDefaults.setDelimitedIdentifiers( unitElement != null ? Boolean.TRUE : null );
defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement, null, addedClasses ) );
globalDefaults.setSchema( defaultElement.getSchema() );
globalDefaults.setCatalog( defaultElement.getCatalog() );
globalDefaults.setAccess( defaultElement.getAccess() );
globalDefaults.setCascadePersist( defaultElement.getCascadePersist() != null ? Boolean.TRUE : null );
globalDefaults.setDelimitedIdentifiers( defaultElement.getDelimitedIdentifiers() != null ? Boolean.TRUE : null );
defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement.getEntityListeners(), null, addedClasses ) );
}
}
else {
@ -108,92 +100,61 @@ public class XMLContext implements Serializable {
//entity mapping default
Default entityMappingDefault = new Default();
Element unitElement = root.element( "package" );
String packageName = unitElement != null ? unitElement.getTextTrim() : null;
String packageName = entityMappings.getPackage();
entityMappingDefault.setPackageName( packageName );
unitElement = root.element( "schema" );
entityMappingDefault.setSchema( unitElement != null ? unitElement.getTextTrim() : null );
unitElement = root.element( "catalog" );
entityMappingDefault.setCatalog( unitElement != null ? unitElement.getTextTrim() : null );
unitElement = root.element( "access" );
setAccess( unitElement, entityMappingDefault );
defaultElements.add( root );
entityMappingDefault.setSchema( entityMappings.getSchema() );
entityMappingDefault.setCatalog( entityMappings.getCatalog() );
entityMappingDefault.setAccess( entityMappings.getAccess() );
defaultElements.add( entityMappings );
setLocalAttributeConverterDefinitions( root.elements( "converter" ) );
setLocalAttributeConverterDefinitions( entityMappings.getConverter() );
List<Element> entities = root.elements( "entity" );
addClass( entities, packageName, entityMappingDefault, addedClasses );
addClass( entityMappings.getEntity(), packageName, entityMappingDefault, addedClasses );
entities = root.elements( "mapped-superclass" );
addClass( entities, packageName, entityMappingDefault, addedClasses );
addClass( entityMappings.getMappedSuperclass(), packageName, entityMappingDefault, addedClasses );
addClass( entityMappings.getEmbeddable(), packageName, entityMappingDefault, addedClasses );
entities = root.elements( "embeddable" );
addClass( entities, packageName, entityMappingDefault, addedClasses );
return addedClasses;
}
private void setAccess(Element unitElement, Default defaultType) {
if ( unitElement != null ) {
String access = unitElement.getTextTrim();
setAccess( access, defaultType );
}
}
private void setAccess( String access, Default defaultType) {
AccessType type;
if ( access != null ) {
try {
type = AccessType.valueOf( access );
}
catch ( IllegalArgumentException e ) {
throw new AnnotationException( "Invalid access type " + access + " (check your xml configuration)" );
}
defaultType.setAccess( type );
}
}
private void addClass(List<Element> entities, String packageName, Default defaults, List<String> addedClasses) {
for (Element element : entities) {
String className = buildSafeClassName( element.attributeValue( "class" ), packageName );
if ( classOverriding.containsKey( className ) ) {
private void addClass(List<? extends ManagedType> managedTypes, String packageName, Default defaults, List<String> addedClasses) {
for (ManagedType element : managedTypes) {
String className = buildSafeClassName( element.getClazz(), packageName );
if ( managedTypeOverride.containsKey( className ) ) {
//maybe switch it to warn?
throw new IllegalStateException( "Duplicate XML entry for " + className );
}
addedClasses.add( className );
classOverriding.put( className, element );
managedTypeOverride.put( className, element );
Default localDefault = new Default();
localDefault.override( defaults );
String metadataCompleteString = element.attributeValue( "metadata-complete" );
if ( metadataCompleteString != null ) {
localDefault.setMetadataComplete( Boolean.parseBoolean( metadataCompleteString ) );
}
String access = element.attributeValue( "access" );
setAccess( access, localDefault );
defaultsOverriding.put( className, localDefault );
localDefault.setMetadataComplete( element.isMetadataComplete() );
localDefault.setAccess( element.getAccess() );
defaultsOverride.put( className, localDefault );
LOG.debugf( "Adding XML overriding information for %s", className );
addEntityListenerClasses( element, packageName, addedClasses );
if ( element instanceof JaxbEntity ) {
addEntityListenerClasses( ( (JaxbEntity) element ).getEntityListeners(), packageName, addedClasses );
}
else if ( element instanceof JaxbMappedSuperclass ) {
addEntityListenerClasses( ( (JaxbMappedSuperclass) element ).getEntityListeners(), packageName, addedClasses );
}
}
}
private List<String> addEntityListenerClasses(Element element, String packageName, List<String> addedClasses) {
private List<String> addEntityListenerClasses(JaxbEntityListeners listeners, String packageName, List<String> addedClasses) {
List<String> localAddedClasses = new ArrayList<>();
Element listeners = element.element( "entity-listeners" );
if ( listeners != null ) {
@SuppressWarnings( "unchecked" )
List<Element> elements = listeners.elements( "entity-listener" );
for (Element listener : elements) {
String listenerClassName = buildSafeClassName( listener.attributeValue( "class" ), packageName );
if ( classOverriding.containsKey( listenerClassName ) ) {
//maybe switch it to warn?
if ( "entity-listener".equals( classOverriding.get( listenerClassName ).getName() ) ) {
LOG.duplicateListener( listenerClassName );
continue;
}
throw new IllegalStateException("Duplicate XML entry for " + listenerClassName);
List<JaxbEntityListener> elements = listeners.getEntityListener();
for (JaxbEntityListener listener : elements) {
String listenerClassName = buildSafeClassName( listener.getClazz(), packageName );
if ( entityListenerOverride.containsKey( listenerClassName ) ) {
LOG.duplicateListener( listenerClassName );
continue;
}
localAddedClasses.add( listenerClassName );
classOverriding.put( listenerClassName, listener );
entityListenerOverride.put( listenerClassName, listener );
}
}
LOG.debugf( "Adding XML overriding information for listeners: %s", localAddedClasses );
@ -202,11 +163,10 @@ public class XMLContext implements Serializable {
}
@SuppressWarnings("unchecked")
private void setLocalAttributeConverterDefinitions(List<Element> converterElements) {
for ( Element converterElement : converterElements ) {
final String className = converterElement.attributeValue( "class" );
final String autoApplyAttribute = converterElement.attributeValue( "auto-apply" );
final boolean autoApply = autoApplyAttribute != null && Boolean.parseBoolean( autoApplyAttribute );
private void setLocalAttributeConverterDefinitions(List<JaxbConverter> converterElements) {
for ( JaxbConverter converterElement : converterElements ) {
final String className = converterElement.getClazz();
final boolean autoApply = Boolean.TRUE.equals( converterElement.isAutoApply() );
try {
final Class<? extends AttributeConverter> attributeConverterClass = classLoaderAccess.classForName(
@ -232,7 +192,7 @@ public class XMLContext implements Serializable {
return className;
}
public static String buildSafeClassName(String className, XMLContext.Default defaults) {
public static String buildSafeClassName(String className, Default defaults) {
return buildSafeClassName( className, defaults.getPackageName() );
}
@ -240,17 +200,21 @@ public class XMLContext implements Serializable {
Default xmlDefault = new Default();
xmlDefault.override( globalDefaults );
if ( className != null ) {
Default entityMappingOverriding = defaultsOverriding.get( className );
Default entityMappingOverriding = defaultsOverride.get( className );
xmlDefault.override( entityMappingOverriding );
}
return xmlDefault;
}
public Element getXMLTree(String className ) {
return classOverriding.get( className );
public ManagedType getManagedTypeOverride(String className) {
return managedTypeOverride.get( className );
}
public List<Element> getAllDocuments() {
public JaxbEntityListener getEntityListenerOverride(String className) {
return entityListenerOverride.get( className );
}
public List<JaxbEntityMappings> getAllDocuments() {
return defaultElements;
}

View File

@ -72,9 +72,9 @@
</bindings>
<bindings node="//xsd:simpleType[@name='constraint-mode']">
<javaType name="javax.persistence.ContraintMode"
parseMethod="org.hibernate.boot.jaxb.mapping.internal.ContraintModeMarshalling.fromXml"
printMethod="org.hibernate.boot.jaxb.mapping.internal.ContraintModeMarshalling.toXml" />
<javaType name="javax.persistence.ConstraintMode"
parseMethod="org.hibernate.boot.jaxb.mapping.internal.ConstraintModeMarshalling.fromXml"
printMethod="org.hibernate.boot.jaxb.mapping.internal.ConstraintModeMarshalling.toXml" />
</bindings>

View File

@ -0,0 +1,51 @@
/*
* 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.internal.util.xml;
import java.io.IOException;
import java.io.InputStream;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.jaxb.internal.MappingBinder;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.spi.Binding;
import org.hibernate.boot.jaxb.spi.XmlMappingOptions;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl;
import org.junit.Assert;
/**
* A small helper class for parsing XML mappings, to be used in unit tests.
*/
public final class XMLMappingHelper {
private final MappingBinder binder;
public XMLMappingHelper(XmlMappingOptions xmlMappingOptions) {
binder = new MappingBinder( ClassLoaderServiceTestingImpl.INSTANCE, true, xmlMappingOptions );
}
public JaxbEntityMappings readOrmXmlMappings(String name) throws IOException {
try (InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( name )) {
return readOrmXmlMappings( is, name );
}
}
public JaxbEntityMappings readOrmXmlMappings(InputStream is, String name) {
try {
Assert.assertNotNull( "Resource not found: " + name, is );
Binding<?> binding = binder.bind( is, new Origin( SourceType.JAR, name ) );
return (JaxbEntityMappings) binding.getRoot();
}
catch (RuntimeException e) {
throw new IllegalStateException( "Could not parse orm.xml mapping '" + name + "': " + e.getMessage(), e );
}
}
}

View File

@ -9,12 +9,15 @@ package org.hibernate.test.annotations.reflection;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.hibernate.annotations.Columns;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.spi.XmlMappingOptions;
import org.hibernate.cfg.EJB3DTDEntityResolver;
import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader;
import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader;
import org.hibernate.cfg.annotations.reflection.internal.XMLContext;
import org.hibernate.internal.util.xml.ErrorLogger;
import org.hibernate.internal.util.xml.XMLHelper;
import org.hibernate.internal.util.xml.XMLMappingHelper;
import org.hibernate.testing.boot.BootstrapContextImpl;
import org.hibernate.testing.junit4.BaseUnitTestCase;
@ -401,32 +404,16 @@ public class JPAXMLOverriddenAnnotationReaderTest extends BaseUnitTestCase {
assertEquals( OtherLogListener.class.getName(), context.getDefaultEntityListeners().get( 0 ) );
}
private XMLContext buildContext(String ormfile) throws SAXException, DocumentException, IOException {
XMLHelper xmlHelper = new XMLHelper();
InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( ormfile );
assertNotNull( "ORM.xml not found: " + ormfile, is );
private XMLContext buildContext(String ormfile) throws IOException {
XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() {
@Override
public boolean isPreferJaxb() {
return true;
}
} );
JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( ormfile );
XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE );
ErrorLogger errorLogger = new ErrorLogger();
SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE );
//saxReader.setValidation( false );
try {
saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
}
catch ( SAXNotSupportedException e ) {
saxReader.setValidation( false );
}
org.dom4j.Document doc;
try {
doc = saxReader.read( new InputSource( new BufferedInputStream( is ) ) );
}
finally {
is.close();
}
if ( errorLogger.hasErrors() ) {
System.out.println( errorLogger.getErrors().get( 0 ) );
}
assertFalse( errorLogger.hasErrors() );
context.addDocument( doc );
context.addDocument( mappings );
return context;
}
}

View File

@ -6,24 +6,14 @@
*/
package org.hibernate.test.annotations.reflection;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.dom4j.io.SAXReader;
import org.junit.Assert;
import org.junit.Test;
import org.xml.sax.InputSource;
import org.xml.sax.SAXNotSupportedException;
import org.hibernate.cfg.EJB3DTDEntityResolver;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.spi.XmlMappingOptions;
import org.hibernate.cfg.annotations.reflection.internal.XMLContext;
import org.hibernate.internal.util.xml.ErrorLogger;
import org.hibernate.internal.util.xml.XMLHelper;
import org.hibernate.internal.util.xml.XMLMappingHelper;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.boot.BootstrapContextImpl;
import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl;
import org.junit.Test;
/**
* Tests the new {@link XMLContext},
@ -38,36 +28,15 @@ import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl;
public class XMLContextTest {
@Test
public void testAll() throws Exception {
final XMLHelper xmlHelper = new XMLHelper();
XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() {
@Override
public boolean isPreferJaxb() {
return true;
}
} );
final XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE );
InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream(
"org/hibernate/test/annotations/reflection/orm.xml"
);
Assert.assertNotNull( "ORM.xml not found", is );
final ErrorLogger errorLogger = new ErrorLogger();
final SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE );
try {
saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
}
catch ( SAXNotSupportedException e ) {
saxReader.setValidation( false );
}
org.dom4j.Document doc;
try {
doc = saxReader.read( new InputSource( new BufferedInputStream( is ) ) );
}
finally {
try {
is.close();
}
catch ( IOException ioe ) {
//log.warn( "Could not close input stream", ioe );
}
}
Assert.assertFalse( errorLogger.hasErrors() );
context.addDocument( doc );
JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( "org/hibernate/test/annotations/reflection/orm.xml" );
context.addDocument( mappings );
}
}

View File

@ -10,11 +10,11 @@ import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings;
import org.hibernate.boot.jaxb.spi.XmlMappingOptions;
import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader;
import org.hibernate.cfg.annotations.reflection.internal.XMLContext;
import org.hibernate.internal.util.xml.XMLMappingHelper;
import org.hibernate.testing.boot.BootstrapContextImpl;
import org.hibernate.testing.junit4.BaseUnitTestCase;
@ -29,8 +29,12 @@ import static org.junit.Assert.assertTrue;
* database is used. Thus, no schema generation or cleanup will be performed.
*/
public abstract class Ejb3XmlTestCase extends BaseUnitTestCase {
protected JPAXMLOverriddenAnnotationReader reader;
protected Ejb3XmlTestCase() {
}
protected void assertAnnotationPresent(Class<? extends Annotation> annotationType) {
assertTrue(
"Expected annotation " + annotationType.getSimpleName() + " was not present",
@ -59,17 +63,19 @@ public abstract class Ejb3XmlTestCase extends BaseUnitTestCase {
protected XMLContext getContext(String resourceName) throws Exception {
InputStream is = getClass().getResourceAsStream( resourceName );
assertNotNull( "Could not load resource " + resourceName, is );
return getContext( is );
return getContext( is, resourceName );
}
protected XMLContext getContext(InputStream is) throws Exception {
XMLContext xmlContext = new XMLContext( BootstrapContextImpl.INSTANCE );
SAXReader reader = new SAXReader();
reader.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
reader.setFeature( "http://xml.org/sax/features/external-general-entities", false );
reader.setFeature( "http://xml.org/sax/features/external-parameter-entities", false );
Document doc = reader.read( is );
xmlContext.addDocument( doc );
return xmlContext;
protected XMLContext getContext(InputStream is, String resourceName) throws Exception {
XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() {
@Override
public boolean isPreferJaxb() {
return true;
}
} );
JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( is, resourceName );
XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE );
context.addDocument( mappings );
return context;
}
}