Skip to content

Commit bba1503

Browse files
committed
HHH-19528 Read and apply the version from XML
1 parent 5d2d500 commit bba1503

File tree

7 files changed

+266
-1
lines changed

7 files changed

+266
-1
lines changed

hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/AttributeProcessor.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationOverrideImpl;
1111
import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributeOverrideImpl;
1212
import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributesContainer;
13+
import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributesContainerImpl;
1314
import org.hibernate.boot.jaxb.mapping.spi.JaxbBaseAttributesContainer;
1415
import org.hibernate.boot.jaxb.mapping.spi.JaxbBasicImpl;
1516
import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollectionImpl;
@@ -22,6 +23,7 @@
2223
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistentAttribute;
2324
import org.hibernate.boot.jaxb.mapping.spi.JaxbPluralAnyMappingImpl;
2425
import org.hibernate.boot.jaxb.mapping.spi.JaxbTransientImpl;
26+
import org.hibernate.boot.jaxb.mapping.spi.JaxbVersionImpl;
2527
import org.hibernate.boot.models.HibernateAnnotations;
2628
import org.hibernate.boot.models.xml.internal.attr.AnyMappingAttributeProcessing;
2729
import org.hibernate.boot.models.xml.internal.attr.BasicAttributeProcessing;
@@ -151,12 +153,21 @@ public interface MemberAdjuster {
151153
}
152154

153155
public static void processAttributes(
154-
JaxbAttributesContainer attributesContainer,
156+
JaxbAttributesContainerImpl attributesContainer,
155157
MutableClassDetails mutableClassDetails,
156158
AccessType classAccessType,
157159
XmlDocumentContext xmlDocumentContext) {
158160
processAttributes( attributesContainer, mutableClassDetails, classAccessType, null, xmlDocumentContext );
159161
}
162+
public static void processAttributes(
163+
JaxbAttributesContainerImpl attributesContainer,
164+
MutableClassDetails mutableClassDetails,
165+
AccessType classAccessType,
166+
MemberAdjuster memberAdjuster,
167+
XmlDocumentContext xmlDocumentContext) {
168+
processAttributes( (JaxbAttributesContainer) attributesContainer, mutableClassDetails, classAccessType, memberAdjuster, xmlDocumentContext );
169+
processVersionAttribute( attributesContainer.getVersion(), mutableClassDetails, classAccessType, xmlDocumentContext );
170+
}
160171

161172
public static void processAttributes(
162173
JaxbAttributesContainer attributesContainer,
@@ -265,4 +276,17 @@ public static void processAssociationOverrides(
265276
xmlDocumentContext
266277
);
267278
}
279+
280+
public static void processVersionAttribute(
281+
JaxbVersionImpl version,
282+
MutableClassDetails mutableClassDetails, AccessType classAccessType,
283+
XmlDocumentContext xmlDocumentContext
284+
) {
285+
XmlAnnotationHelper.applyVersion(
286+
version,
287+
mutableClassDetails,
288+
classAccessType,
289+
xmlDocumentContext
290+
);
291+
}
268292
}

hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.UUID;
2525
import java.util.function.Consumer;
2626

27+
import jakarta.persistence.AccessType;
2728
import org.hibernate.AnnotationException;
2829
import org.hibernate.annotations.CascadeType;
2930
import org.hibernate.annotations.NotFoundAction;
@@ -73,6 +74,7 @@
7374
import org.hibernate.boot.jaxb.mapping.spi.JaxbUniqueConstraintImpl;
7475
import org.hibernate.boot.jaxb.mapping.spi.JaxbUserTypeImpl;
7576
import org.hibernate.boot.jaxb.mapping.spi.JaxbUuidGeneratorImpl;
77+
import org.hibernate.boot.jaxb.mapping.spi.JaxbVersionImpl;
7678
import org.hibernate.boot.models.HibernateAnnotations;
7779
import org.hibernate.boot.models.JpaAnnotations;
7880
import org.hibernate.boot.models.XmlAnnotations;
@@ -81,6 +83,7 @@
8183
import org.hibernate.boot.models.annotations.spi.DatabaseObjectDetails;
8284
import org.hibernate.boot.models.JpaEventListenerStyle;
8385
import org.hibernate.boot.models.spi.JpaEventListener;
86+
import org.hibernate.boot.models.xml.internal.attr.CommonAttributeProcessing;
8487
import org.hibernate.boot.models.xml.internal.db.ForeignKeyProcessing;
8588
import org.hibernate.boot.models.xml.internal.db.JoinColumnProcessing;
8689
import org.hibernate.boot.models.xml.internal.db.TableProcessing;
@@ -136,6 +139,7 @@
136139
import static org.hibernate.boot.models.JpaAnnotations.UNIQUE_CONSTRAINT;
137140
import static org.hibernate.boot.models.xml.internal.UserTypeCasesMapKey.MAP_KEY_USER_TYPE_CASES;
138141
import static org.hibernate.boot.models.xml.internal.UserTypeCasesStandard.STANDARD_USER_TYPE_CASES;
142+
import static org.hibernate.internal.util.NullnessHelper.coalesce;
139143
import static org.hibernate.internal.util.StringHelper.isEmpty;
140144
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
141145
import static org.hibernate.internal.util.StringHelper.unqualify;
@@ -1701,4 +1705,32 @@ public static void applyCollectionClassification(
17011705
);
17021706
collectionClassification.value( classification );
17031707
}
1708+
1709+
public static void applyVersion(JaxbVersionImpl version, MutableClassDetails mutableClassDetails, AccessType classAccessType, XmlDocumentContext xmlDocumentContext) {
1710+
if ( version != null ) {
1711+
final AccessType accessType = coalesce( version.getAccess(), classAccessType );
1712+
final String versionAttributeName = version.getName();
1713+
1714+
final MutableMemberDetails memberDetails = XmlProcessingHelper.getAttributeMember(
1715+
versionAttributeName,
1716+
accessType,
1717+
mutableClassDetails
1718+
);
1719+
memberDetails.applyAnnotationUsage(
1720+
JpaAnnotations.VERSION,
1721+
xmlDocumentContext.getModelBuildingContext()
1722+
);
1723+
CommonAttributeProcessing.applyAccess( accessType, memberDetails, xmlDocumentContext );
1724+
CommonAttributeProcessing.applyAttributeAccessor( version, memberDetails, xmlDocumentContext );
1725+
XmlAnnotationHelper.applyTemporal( version.getTemporal(), memberDetails, xmlDocumentContext );
1726+
if ( version.getColumn() != null ) {
1727+
final ColumnJpaAnnotation columnAnn = (ColumnJpaAnnotation) memberDetails.applyAnnotationUsage(
1728+
JpaAnnotations.COLUMN,
1729+
xmlDocumentContext.getModelBuildingContext()
1730+
);
1731+
columnAnn.apply( version.getColumn(), xmlDocumentContext );
1732+
XmlAnnotationHelper.applyColumnTransformation( version.getColumn(), memberDetails, xmlDocumentContext );
1733+
}
1734+
}
1735+
}
17041736
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.jpa.xml;
6+
7+
8+
9+
public class BaseShop {
10+
11+
private int id;
12+
13+
private int version;
14+
15+
public int getId() {
16+
return id;
17+
}
18+
19+
public void setId(int id) {
20+
this.id = id;
21+
}
22+
23+
public int getVersion() {
24+
return version;
25+
}
26+
27+
public void setVersion(int version) {
28+
this.version = version;
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.jpa.xml;
6+
7+
import org.hibernate.testing.jdbc.SQLStatementInspector;
8+
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
9+
import org.hibernate.testing.orm.junit.JiraKey;
10+
import org.hibernate.testing.orm.junit.Jpa;
11+
import org.junit.jupiter.api.BeforeEach;
12+
import org.junit.jupiter.api.Test;
13+
14+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
15+
16+
17+
@JiraKey(value = "HHH-19528")
18+
@Jpa(
19+
xmlMappings = {"org/hibernate/orm/test/jpa/xml/ExplicitOptimisticLockAnnotationOnCollectionXmlOnlyTest.xml"},
20+
useCollectingStatementInspector = true
21+
)
22+
class ExplicitOptimisticLockAnnotationOnCollectionXmlOnlyTest {
23+
24+
private static SQLStatementInspector statementInspector;
25+
private static int consumerId;
26+
27+
28+
@BeforeEach
29+
public void setUp(EntityManagerFactoryScope scope) {
30+
statementInspector = scope.getCollectingStatementInspector();
31+
32+
scope.inTransaction( em -> {
33+
Consumer consumer = new Consumer();
34+
em.persist( consumer );
35+
consumerId = consumer.getId();
36+
37+
ConsumerItem item1 = new ConsumerItem();
38+
item1.setConsumer( consumer );
39+
em.persist( item1 );
40+
41+
ConsumerItem item2 = new ConsumerItem();
42+
item2.setConsumer( consumer );
43+
em.persist( item2 );
44+
} );
45+
}
46+
47+
@Test
48+
void test(EntityManagerFactoryScope scope) {
49+
statementInspector.clear();
50+
51+
scope.inTransaction( em -> {
52+
Consumer consumer = em.find( Consumer.class, consumerId );
53+
ConsumerItem inventory = new ConsumerItem();
54+
inventory.setConsumer( consumer );
55+
consumer.getConsumerItems().add( inventory );
56+
} );
57+
statementInspector.assertUpdate();
58+
statementInspector.assertInsert();
59+
}
60+
61+
@Test
62+
void testVersionOnMappedSupertype(EntityManagerFactoryScope scope) {
63+
var shop = scope.fromTransaction( em -> {
64+
Supermarket supermarket = new Supermarket();
65+
supermarket.setName( "Tesco" );
66+
em.persist( supermarket );
67+
return supermarket;
68+
} );
69+
70+
statementInspector.clear();
71+
scope.inTransaction( em -> {
72+
Supermarket supermarket = em.find( Supermarket.class, shop.getId() );
73+
supermarket.setName( "Leclerc" );
74+
} );
75+
scope.inTransaction( em -> {
76+
Supermarket supermarket = em.find( Supermarket.class, shop.getId() );
77+
assertThat( shop.getVersion() ).isNotEqualTo( supermarket.getVersion() );
78+
} );
79+
80+
statementInspector.assertHasQueryMatching( "update.*version.*" );
81+
}
82+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.jpa.xml;
6+
7+
public class Supermarket extends BaseShop {
8+
9+
private String name;
10+
11+
public String getName() {
12+
return name;
13+
}
14+
15+
public void setName(String name) {
16+
this.name = name;
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ SPDX-License-Identifier: Apache-2.0
4+
~ Copyright Red Hat Inc. and Hibernate Authors
5+
-->
6+
<entity-mappings xmlns="http://www.hibernate.org/xsd/orm/mapping" version="7.0">
7+
8+
<package>org.hibernate.orm.test.jpa.xml</package>
9+
10+
<mapped-superclass class="BaseShop" metadata-complete="true" access="FIELD">
11+
<attributes>
12+
<id name="id">
13+
<column name="id_entity"/>
14+
<generated-value strategy="AUTO"/>
15+
</id>
16+
<version name="version">
17+
<column name="version"/>
18+
</version>
19+
</attributes>
20+
</mapped-superclass>
21+
22+
<entity class="Consumer" name="Consumer" metadata-complete="true">
23+
<table name="consumer"/>
24+
<attributes>
25+
<id name="id">
26+
<column name="id_entity"/>
27+
<generated-value strategy="AUTO"/>
28+
</id>
29+
<version name="version">
30+
<column name="version"/>
31+
</version>
32+
<one-to-many name="consumerItems" mapped-by="consumer" fetch="LAZY" optimistic-lock="true">
33+
<cascade>
34+
<cascade-all/>
35+
</cascade>
36+
</one-to-many>
37+
</attributes>
38+
</entity>
39+
40+
<entity class="ConsumerItem" name="ConsumerInventory" metadata-complete="true">
41+
<table name="consumer_item"/>
42+
<attributes>
43+
<id name="id">
44+
<column name="id_entity"/>
45+
<generated-value strategy="AUTO"/>
46+
</id>
47+
<version name="version">
48+
<column name="version"/>
49+
</version>
50+
<many-to-one name="consumer" target-entity="Consumer">
51+
<join-column name="consumer_id"/>
52+
<cascade>
53+
<cascade-merge/>
54+
</cascade>
55+
</many-to-one>
56+
</attributes>
57+
</entity>
58+
59+
<entity class="Supermarket" name="Supermarket" metadata-complete="true">
60+
<attributes>
61+
<basic name="name"/>
62+
</attributes>
63+
</entity>
64+
65+
</entity-mappings>
66+
67+

hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SQLStatementInspector.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,19 @@ public void assertUpdate() {
127127
.anySatisfy( sql -> Assertions.assertThat( sql.toLowerCase( Locale.ROOT ) ).startsWith( "update" ) );
128128
}
129129

130+
public void assertInsert() {
131+
Assertions.assertThat( sqlQueries )
132+
.isNotEmpty()
133+
.anySatisfy( sql -> Assertions.assertThat( sql.toLowerCase( Locale.ROOT ) ).startsWith( "insert" ) );
134+
}
135+
130136
public static SQLStatementInspector extractFromSession(SessionImplementor session) {
131137
return (SQLStatementInspector) session.getJdbcSessionContext().getStatementInspector();
132138
}
139+
140+
public void assertHasQueryMatching(String queryPattern) {
141+
Assertions.assertThat( sqlQueries )
142+
.isNotEmpty()
143+
.anySatisfy( sql -> Assertions.assertThat( sql.toLowerCase( Locale.ROOT ) ).matches( queryPattern ) );
144+
}
133145
}

0 commit comments

Comments
 (0)