HHH-18072 - Transform hbm.xml not-found

This commit is contained in:
Steve Ebersole 2024-05-05 21:31:45 -05:00
parent 9f58dc1b98
commit 6db0987a2c
8 changed files with 236 additions and 7 deletions

View File

@ -7,12 +7,14 @@
package org.hibernate.boot.jaxb.hbm.transform;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.PolymorphismType;
import org.hibernate.boot.MappingException;
@ -155,10 +157,14 @@ import org.hibernate.internal.util.collections.CollectionHelper;
import org.jboss.logging.Logger;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.FetchType;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.TemporalType;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import static org.hibernate.boot.jaxb.hbm.transform.HbmTransformationLogging.TRANSFORMATION_LOGGER;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
@ -234,9 +240,26 @@ public class HbmXmlTransformer {
transferFetchProfiles();
transferDatabaseObjects();
if ( TRANSFORMATION_LOGGER.isDebugEnabled() ) {
dumpTransformed( origin, ormRoot );
}
return ormRoot;
}
private static void dumpTransformed(Origin origin, JaxbEntityMappingsImpl ormRoot) {
try {
JAXBContext ctx = JAXBContext.newInstance( JaxbEntityMappingsImpl.class );
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
final StringWriter stringWriter = new StringWriter();
marshaller.marshal( ormRoot, stringWriter );
TRANSFORMATION_LOGGER.debugf( "Transformed hbm.xml (%s):\n%s", origin, stringWriter.toString() );
}
catch (JAXBException e) {
throw new RuntimeException( e );
}
}
private <T> void transfer(Supplier<T> source, Consumer<T> target) {
final T value = source.get();
if ( value != null ) {
@ -1479,15 +1502,18 @@ public class HbmXmlTransformer {
}
private JaxbManyToOneImpl transformManyToOne(final JaxbHbmManyToOneType hbmNode) {
if ( hbmNode.getNotFound() != null ) {
handleUnsupported( "`not-found` not supported for transformation" );
}
final JaxbManyToOneImpl m2o = new JaxbManyToOneImpl();
m2o.setAttributeAccessor( hbmNode.getAccess() );
m2o.setCascade( convertCascadeType( hbmNode.getCascade() ) );
m2o.setForeignKeys( new JaxbForeignKeyImpl() );
m2o.getForeignKeys().setName( hbmNode.getForeignKey() );
if ( hbmNode.getForeignKey() != null ) {
m2o.setForeignKeys( new JaxbForeignKeyImpl() );
if ( "none".equalsIgnoreCase( hbmNode.getForeignKey() ) ) {
m2o.getForeignKeys().setConstraintMode( ConstraintMode.NO_CONSTRAINT );
}
else {
m2o.getForeignKeys().setName( hbmNode.getForeignKey() );
}
}
transferColumnsAndFormulas(
new ColumnAndFormulaSource() {
@ -1543,9 +1569,21 @@ public class HbmXmlTransformer {
m2o.setTargetEntity( hbmNode.getClazz() );
}
transferFetchable( hbmNode.getLazy(), hbmNode.getFetch(), hbmNode.getOuterJoin(), null, m2o );
if ( hbmNode.getNotFound() != null ) {
m2o.setNotFound( interpretNotFoundAction( hbmNode.getNotFound() ) );
}
return m2o;
}
private NotFoundAction interpretNotFoundAction(JaxbHbmNotFoundEnum hbmNotFound) {
return switch ( hbmNotFound ) {
case EXCEPTION -> NotFoundAction.EXCEPTION;
case IGNORE -> NotFoundAction.IGNORE;
};
}
private JaxbAnyMappingImpl transformAnyAttribute(JaxbHbmAnyAssociationType source) {
final JaxbAnyMappingImpl target = new JaxbAnyMappingImpl();

View File

@ -15,7 +15,7 @@ import org.jboss.logging.Logger;
*/
@Internal
public interface XmlProcessLogging {
String NAME = "org.hibernate.models.xml";
String NAME = "org.hibernate.orm.models.xml";
Logger XML_PROCESS_LOGGER = Logger.getLogger( NAME );
}

View File

@ -41,6 +41,7 @@ import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.ModelsException;
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.internal.ModelsClassLogging;
import org.hibernate.models.internal.RenderingCollectorImpl;
import org.hibernate.models.internal.dynamic.DynamicClassDetails;
import org.hibernate.models.internal.dynamic.DynamicFieldDetails;
import org.hibernate.models.spi.AnnotationUsage;
@ -61,6 +62,7 @@ import jakarta.persistence.Cacheable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import static org.hibernate.boot.models.xml.XmlProcessLogging.XML_PROCESS_LOGGER;
import static org.hibernate.internal.util.NullnessHelper.coalesce;
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
@ -571,6 +573,18 @@ public class ManagedTypeProcessor {
classDetails,
xmlDocumentContext
);
renderClass( classDetails, xmlDocumentContext );
}
private static void renderClass(MutableClassDetails classDetails, XmlDocumentContext xmlDocumentContext) {
if ( !XML_PROCESS_LOGGER.isDebugEnabled() ) {
return;
}
final RenderingCollectorImpl renderingCollector = new RenderingCollectorImpl();
classDetails.render( renderingCollector );
XML_PROCESS_LOGGER.debugf( "Class annotations from XML for %s:\n%s", classDetails.getName(), renderingCollector.toString() );
}
private static void applyAccessAnnotation(
@ -826,6 +840,8 @@ public class ManagedTypeProcessor {
}
processEntityOrMappedSuperclass( jaxbMappedSuperclass, classDetails, xmlDocumentContext );
renderClass( classDetails, xmlDocumentContext );
}
public static void processOverrideMappedSuperclass(List<XmlProcessingResult.OverrideTuple<JaxbMappedSuperclassImpl>> mappedSuperclassesOverrides) {

View File

@ -0,0 +1,39 @@
/*
* 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.boot.models.hbm.notfound;
/**
* @author Steve Ebersole
*/
public class Employee2 {
private Integer id;
private String name;
public Employee2() {
}
public Employee2(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.boot.models.hbm.notfound;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.orm.test.unconstrained.UnconstrainedTest;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @see UnconstrainedTest
*
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
@ServiceRegistry(settings = @Setting(name= MappingSettings.TRANSFORM_HBM_XML, value = "true"))
@DomainModel(xmlMappings = "mappings/models/hbm/notfound/Person2.hbm.xml")
@SessionFactory
public class HbmNotFoundTransformationTests {
@Test
void testNotFoundTransformation(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final Employee2 employee = new Employee2( 1, "employee" );
final Person2 person = new Person2( 1, "person", employee );
session.persist( employee );
session.persist( person );
} );
scope.inTransaction( (session) -> session.createMutationQuery( "delete Employee2" ).executeUpdate() );
scope.inTransaction( (session) -> {
final Person2 loaded = session.find( Person2.class, 1 );
assertThat( loaded ).isNotNull();
assertThat( loaded.getEmployee() ).isNull();
} );
}
@AfterEach
void tearDown(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createMutationQuery( "delete Person2" ).executeUpdate();
session.createMutationQuery( "delete Employee2" ).executeUpdate();
} );
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.boot.models.hbm.notfound;
/**
* @author Steve Ebersole
*/
public class Person2 {
private Integer id;
private String name;
private Employee2 employee;
public Person2() {
}
public Person2(Integer id, String name, Employee2 employee) {
this.id = id;
this.name = name;
this.employee = employee;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee2 getEmployee() {
return employee;
}
public void setEmployee(Employee2 employee) {
this.employee = employee;
}
}

View File

@ -55,6 +55,11 @@ logger.sql-result-ast.name=org.hibernate.orm.sql.results.graph.AST
logger.sqm-tree.name=org.hibernate.orm.query.sqm.ast
#logger.sqm-tree.level=debug
logger.hbm-xml.name=org.hibernate.orm.boot.jaxb.hbm-transform
#logger.hbm-xml.level=debug
logger.xml.name=org.hibernate.orm.models.xml
#logger.xml.level=debug
###############################################################################

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="org.hibernate.orm.test.boot.models.hbm.notfound"
default-access="field">
<class name="Person2">
<id name="id"/>
<property name="name"/>
<many-to-one name="employee" column="employee_fk" not-found="ignore" cascade="all"/>
</class>
<class name="Employee2">
<id name="id"/>
<property name="name"/>
</class>
</hibernate-mapping>