Skip to content

Commit 4488e88

Browse files
author
wonsang.choi
committed
HHH-19952: Merge hibernate/main into RealTake/main
2 parents 9cf5ca0 + fee7ed7 commit 4488e88

File tree

77 files changed

+2569
-1676
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2569
-1676
lines changed

changelog.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,51 @@
11
Hibernate 7 Changelog
22
=======================
33

4+
Changes in 7.2.0.CR3 (November 25, 2025)
5+
------------------------------------------------------------------------------------------------------------------------
6+
7+
https://hibernate.atlassian.net/projects/HHH/versions/36014
8+
9+
10+
** Bug
11+
* HHH-19939 broken caching for MERGE and REFRESH load plans
12+
* HHH-19937 duplicate version check after refresh
13+
* HHH-19936 Parameter casts for by-id lookups don't take column definition into account
14+
* HHH-19932 NullPointerException in SqmInterpretationsKey::toString
15+
* HHH-19926 NullPointerException when executing JPQL IN clause with null parameter on entity association
16+
* HHH-19925 Locking root(s) should be based on select-clause, not from-clause
17+
* HHH-19924 Session#find with LockMode.UPGRADE_NOWAIT casues AssetionError when no entity with the provided id exists in the database
18+
* HHH-19922 org.hibernate.orm:hibernate-platform:pom:7.1.7.Final is missing
19+
* HHH-19918 Avoid reflection when instantiating known FormatMapper
20+
* HHH-19910 EntityInitializer#resolveInstance wrongly initializes existing detached instance
21+
* HHH-19906 JsonGeneratingVisitor#visit doesn't handle plural types correctly
22+
* HHH-19905 Implicit join re-use with nested inner and left joins causes ParsingException
23+
* HHH-19895 hibernate-core 6.6.30.Final breaks compatibility on entities with composite keys for multiple variants of DB2
24+
* HHH-19758 HQL parse failure with SLL can lead to wrong parse
25+
* HHH-19749 [Oracle] Merge with @SecondaryTable may generate invalid NUMERIC type casts
26+
* HHH-19739 Exceptions during load of entity with different persistent fields with same name
27+
* HHH-19240 Significant increase in heap allocation for queries after migrating Hibernate ORM 6.5 to 6.6
28+
* HHH-19038 Hibernate.get does not work on detached entities
29+
* HHH-18860 Updates on SecondaryTable cause incorrect cast for BigDecimal
30+
* HHH-16991 EnhancedUserType cannot be used when defining relations
31+
* HHH-14032 Locales with scripts are not round-tripped properly
32+
33+
** Deprecation
34+
* HHH-19941 deprecate Session.contains(String, Object)
35+
36+
** Improvement
37+
* HHH-19953 Relax scopes of methods in EntityInitializerImpl (for Hibernate Reactive)
38+
* HHH-19827 Apply the unified Hibernate Documentation theme
39+
40+
** New Feature
41+
* HHH-19931 SchemaManager.truncateTable()
42+
43+
** Task
44+
* HHH-19944 Upgrade MS SQL JDBC driver to remediate CVE-2025-59250
45+
* HHH-19940 Include maven plugin in the release staging directory
46+
* HHH-19921 Drop last JUnit 4 usages in Hibernate Envers
47+
* HHH-19916 More drop JUnit 4 usage work
48+
449
Note: Please refer to JIRA to learn more about each issue.
550

651
Changes in 7.2.0.CR2 (November 10, 2025)

ci/release/Jenkinsfile

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,15 +222,9 @@ pipeline {
222222
withEnv([
223223
"DISABLE_REMOTE_GRADLE_CACHE=true"
224224
]) {
225-
def notesFiles = findFiles(glob: 'release_notes.md')
226-
if ( notesFiles.length < 1 ) {
227-
throw new IllegalStateException( "Could not locate `release_notes.md`" )
228-
}
229-
if ( notesFiles.length > 1 ) {
230-
throw new IllegalStateException( "Located more than 1 `release_notes.md`" )
231-
}
225+
def ghReleaseNote = sh(script: 'realpath -e release_notes.md 2>/dev/null', returnStdout: true).trim()
232226

233-
sh ".release/scripts/publish.sh -j --notes=${notesFiles[0].path} ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH} "
227+
sh ".release/scripts/publish.sh -j ${ghReleaseNote != '' ? '--notes=' + ghReleaseNote : ''} ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH} "
234228
}
235229
}
236230
}

documentation/src/main/asciidoc/introduction/Advanced.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,17 @@ On the other hand, the following annotations specify how a collection should be
886886

887887
Under the covers, Hibernate uses a `TreeSet` or `TreeMap` to maintain the collection in sorted order.
888888

889+
[CAUTION]
890+
====
891+
The unowned (`mappedBy`) side of a bidirectional association is not responsible for specifying column mappings.
892+
So it's wrong in principle to use `@OrderColumn` or `@MapKeyColumn` on the unowned side of an association mapping.
893+
But for unowned collections, we may use `@OrderBy` or `@MapKey` instead.
894+
That is:
895+
896+
- You can use `@OrderColumn` or `@MapKeyColumn` with an `@ElementCollection`, owned `@ManyToMany`, or owned `@OneToMany`.
897+
- But use `@OrderBy` or `@MapKey` when it's an _unowned_ `@ManyToMany` or `@OneToMany`.
898+
====
899+
889900
[[any]]
890901
=== Any mappings
891902

documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,9 @@ include::{extrasdir}/collections-unidirectional-ordered-list-order-column-select
683683
----
684684
====
685685

686-
With the `order_id` column in place, Hibernate can order the list in-memory after it's being fetched from the database.
686+
With the `order_id` column in place, Hibernate can order the list in-memory after it's fetched from the database.
687+
688+
The <<collection-list-indexbase-ex,`@ListIndexBase` annotation>> may be used to choose between 0-based and 1-based indexing on the database side.
687689

688690
[[collections-bidirectional-ordered-list]]
689691
===== Bidirectional ordered lists
@@ -701,54 +703,6 @@ include::{example-dir-collection}/BidirectionalOrderByListTest.java[tags=collect
701703

702704
Just like with the unidirectional `@OrderBy` list, the `number` column is used to order the statement on the SQL level.
703705

704-
When using the `@OrderColumn` annotation, the `order_id` column is going to be embedded in the child table:
705-
706-
[[collections-bidirectional-ordered-list-order-column-example]]
707-
.Bidirectional `@OrderColumn` list
708-
====
709-
[source,java]
710-
----
711-
include::{example-dir-collection}/BidirectionalOrderColumnListTest.java[tags=collections-bidirectional-ordered-list-order-column-example,indent=0]
712-
----
713-
714-
[source,sql]
715-
----
716-
include::{extrasdir}/collections-bidirectional-ordered-list-order-column-example.sql[]
717-
----
718-
====
719-
720-
When fetching the collection, Hibernate will use the fetched ordered columns to sort the elements according to the `@OrderColumn` mapping.
721-
722-
[[collections-customizing-ordered-list-ordinal]]
723-
===== Customizing ordered list ordinal
724-
725-
You can customize the ordinal of the underlying ordered list by using the https://docs.hibernate.org/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ListIndexBase.html[`@ListIndexBase`] annotation.
726-
727-
[[collections-customizing-ordered-list-ordinal-mapping-example]]
728-
.`@ListIndexBase` mapping example
729-
====
730-
[source,java]
731-
----
732-
include::{example-dir-collection}/OrderColumnListIndexBaseTest.java[tags=collections-customizing-ordered-list-ordinal-mapping-example,indent=0]
733-
----
734-
====
735-
736-
When inserting two `Phone` records, Hibernate is going to start the List index from 100 this time.
737-
738-
[[collections-customizing-ordered-list-ordinal-persist-example]]
739-
.`@ListIndexBase` persist example
740-
====
741-
[source,java]
742-
----
743-
include::{example-dir-collection}/OrderColumnListIndexBaseTest.java[tags=collections-customizing-ordered-list-ordinal-persist-example,indent=0]
744-
----
745-
746-
[source,sql]
747-
----
748-
include::{extrasdir}/collections-customizing-ordered-list-ordinal-persist-example.sql[]
749-
----
750-
====
751-
752706
[[collections-customizing-ordered-by-sql-clause]]
753707
===== Customizing ORDER BY SQL clause
754708

hibernate-core/src/main/java/org/hibernate/annotations/NamedNativeQuery.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@
132132
*
133133
* @see org.hibernate.query.Query#setCacheMode(CacheMode)
134134
* @see org.hibernate.jpa.SpecHints#HINT_SPEC_CACHE_STORE_MODE
135+
*
136+
* @since 6.2
135137
*/
136138
CacheStoreMode cacheStoreMode() default CacheStoreMode.USE;
137139

@@ -140,6 +142,8 @@
140142
*
141143
* @see org.hibernate.query.Query#setCacheMode(CacheMode)
142144
* @see org.hibernate.jpa.SpecHints#HINT_SPEC_CACHE_RETRIEVE_MODE
145+
*
146+
* @since 6.2
143147
*/
144148
CacheRetrieveMode cacheRetrieveMode() default CacheRetrieveMode.USE;
145149

hibernate-core/src/main/java/org/hibernate/annotations/NamedQuery.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
* {@link EntityManager#createNamedQuery(String, Class)}.
5555
*
5656
* @see jakarta.persistence.NamedQuery#resultClass()
57+
*
58+
* @since 7.0
5759
*/
5860
Class<?> resultClass() default void.class;
5961

@@ -127,6 +129,8 @@
127129
*
128130
* @see org.hibernate.query.Query#setCacheStoreMode(CacheStoreMode)
129131
* @see org.hibernate.jpa.SpecHints#HINT_SPEC_CACHE_STORE_MODE
132+
*
133+
* @since 6.2
130134
*/
131135
CacheStoreMode cacheStoreMode() default CacheStoreMode.USE;
132136

@@ -135,6 +139,8 @@
135139
*
136140
* @see org.hibernate.query.Query#setCacheRetrieveMode(CacheRetrieveMode)
137141
* @see org.hibernate.jpa.SpecHints#HINT_SPEC_CACHE_RETRIEVE_MODE
142+
*
143+
* @since 6.2
138144
*/
139145
CacheRetrieveMode cacheRetrieveMode() default CacheRetrieveMode.USE;
140146

hibernate-core/src/main/java/org/hibernate/boot/BootLogging.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public interface BootLogging extends BasicLogger {
4545
@Message(id = 160101, value = "Duplicate generator name: '%s'")
4646
void duplicateGeneratorName(String name);
4747

48-
4948
@LogMessage(level = DEBUG)
5049
@Message(id = 160111, value = "Package not found or no package-info.java: %s")
5150
void packageNotFound(String packageName);
@@ -84,7 +83,6 @@ public interface BootLogging extends BasicLogger {
8483
@ManyToOne and @OneToOne associations mapped with @NotFound are forced to EAGER fetching""")
8584
void ignoreNotFoundWithFetchTypeLazy(String entity, String association);
8685

87-
// --- New typed TRACE/DEBUG messages for boot internals ---
8886
@LogMessage(level = TRACE)
8987
@Message(id = 160140, value = "Binding formula: %s")
9088
void bindingFormula(String formula);
@@ -449,4 +447,12 @@ public interface BootLogging extends BasicLogger {
449447
@LogMessage(level = DEBUG)
450448
@Message(id = 160244, value = "Skipping HBM processing of entity hierarchy [%s], as at least one entity [%s] has been processed")
451449
void skippingHbmProcessingOfEntityHierarchy(String rootEntityName, String processedEntity);
450+
451+
@LogMessage(level = WARN)
452+
@Message(id = 160245, value = "Association '%s' is 'mappedBy' another entity and should not specify a '@MapKeyColumn' (use '@MapKey' instead)")
453+
void mappedByShouldNotSpecifyMapKeyColumn(String associationPath);
454+
455+
@LogMessage(level = WARN)
456+
@Message(id = 160246, value = "Association '%s' is 'mappedBy' another entity and should not specify an '@OrderColumn' (use '@OrderBy' instead)")
457+
void mappedByShouldNotSpecifyOrderColumn(String associationPath);
452458
}

hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ public MetadataSources(ServiceRegistry serviceRegistry, XmlMappingBinderAccess x
105105
// service registry really should be either BootstrapServiceRegistry or StandardServiceRegistry type...
106106
if ( !isExpectedServiceRegistryType( serviceRegistry ) ) {
107107
if ( BOOT_LOGGER.isDebugEnabled() ) {
108-
BOOT_LOGGER.unexpectedServiceRegistryType(
109-
serviceRegistry.getClass().getName()
110-
);
108+
BOOT_LOGGER.unexpectedServiceRegistryType( serviceRegistry.getClass().getName() );
111109
}
112110
}
113111
this.serviceRegistry = serviceRegistry;

hibernate-core/src/main/java/org/hibernate/boot/archive/internal/ArchiveHelper.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import org.hibernate.boot.archive.spi.ArchiveException;
1616

17+
import static org.hibernate.boot.BootLogging.BOOT_LOGGER;
18+
1719

1820
/**
1921
* Helper for dealing with archives
@@ -89,7 +91,7 @@ else if ( "zip".equals( protocol )
8991
"Unable to determine JAR Url from " + url + ". Cause: " + e.getMessage()
9092
);
9193
}
92-
org.hibernate.boot.BootLogging.BOOT_LOGGER.jarUrlFromUrlEntry( String.valueOf(url), String.valueOf(jarUrl) );
94+
BOOT_LOGGER.jarUrlFromUrlEntry( String.valueOf(url), String.valueOf(jarUrl) );
9395
return jarUrl;
9496
}
9597

hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
1414
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1515
import org.hibernate.engine.spi.SessionFactoryImplementor;
16+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1617
import org.hibernate.event.spi.PreCollectionUpdateEvent;
1718
import org.hibernate.event.spi.PreCollectionUpdateEventListener;
1819
import org.hibernate.event.spi.PreDeleteEvent;
@@ -51,21 +52,26 @@ public class BeanValidationEventListener
5152
private final Validator validator;
5253
private final GroupsPerOperation groupsPerOperation;
5354

55+
private SessionFactoryImplementor sessionFactory;
56+
5457
public BeanValidationEventListener(
5558
ValidatorFactory factory, Map<String, Object> settings, ClassLoaderService classLoaderService) {
5659
traversableResolver = new HibernateTraversableResolver();
5760
validator =
5861
factory.usingContext()
5962
.traversableResolver( traversableResolver )
6063
.getValidator();
61-
groupsPerOperation = GroupsPerOperation.from( settings, new ClassLoaderAccessImpl( classLoaderService ) );
64+
groupsPerOperation =
65+
GroupsPerOperation.from( settings,
66+
new ClassLoaderAccessImpl( classLoaderService ) );
6267
}
6368

6469
@Override
6570
public void sessionFactoryCreated(SessionFactory factory) {
66-
var implementor = factory.unwrap( SessionFactoryImplementor.class );
67-
implementor.getMappingMetamodel()
68-
.forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, implementor ) );
71+
sessionFactory = factory.unwrap( SessionFactoryImplementor.class );
72+
sessionFactory.getMappingMetamodel()
73+
.forEachEntityDescriptor( entityPersister ->
74+
traversableResolver.addPersister( entityPersister, sessionFactory ) );
6975
}
7076

7177
public boolean onPreInsert(PreInsertEvent event) {
@@ -110,53 +116,69 @@ public void onPreUpdateCollection(PreCollectionUpdateEvent event) {
110116
final Object entity = castNonNull( event.getCollection().getOwner() );
111117
validate(
112118
entity,
113-
event.getSession().getEntityPersister( event.getAffectedOwnerEntityName(), entity ),
119+
getEntityPersister( event.getSession(), event.getAffectedOwnerEntityName(), entity ),
114120
GroupsPerOperation.Operation.UPDATE
115121
);
116122
}
117123

118-
private <T> void validate(
119-
T object,
120-
EntityPersister persister,
121-
GroupsPerOperation.Operation operation) {
124+
private EntityPersister getEntityPersister(
125+
SharedSessionContractImplementor session, String entityName, Object entity) {
126+
if ( session != null ) {
127+
return session.getEntityPersister( entityName, entity );
128+
}
129+
else {
130+
final var metamodel = sessionFactory.getMappingMetamodel();
131+
return entityName == null
132+
? metamodel.getEntityDescriptor( entity.getClass().getName() )
133+
: metamodel.getEntityDescriptor( entityName )
134+
.getSubclassEntityPersister( entity, sessionFactory );
135+
}
136+
}
137+
138+
private <T> void validate(T object, EntityPersister persister, GroupsPerOperation.Operation operation) {
122139
if ( object != null
123140
&& persister.getRepresentationStrategy().getMode() == RepresentationMode.POJO ) {
124-
final Class<?>[] groups = groupsPerOperation.get( operation );
141+
final var groups = groupsPerOperation.get( operation );
125142
if ( groups.length > 0 ) {
126143
final var constraintViolations = validator.validate( object, groups );
127144
if ( !constraintViolations.isEmpty() ) {
128-
final Set<ConstraintViolation<?>> propagatedViolations = setOfSize( constraintViolations.size() );
145+
final Set<ConstraintViolation<?>> propagatedViolations =
146+
setOfSize( constraintViolations.size() );
129147
final Set<String> classNames = new HashSet<>();
130148
for ( var violation : constraintViolations ) {
131149
BEAN_VALIDATION_LOGGER.trace( violation );
132150
propagatedViolations.add( violation );
133151
classNames.add( violation.getLeafBean().getClass().getName() );
134152
}
135-
final var builder =
136-
new StringBuilder()
137-
.append( "Validation failed for classes " )
138-
.append( classNames )
139-
.append( " during " )
140-
.append( operation.getName() )
141-
.append( " time for groups " )
142-
.append( toString( groups ) )
143-
.append( "\nList of constraint violations:[\n" );
144-
for ( var violation : constraintViolations ) {
145-
builder.append( "\t" ).append( violation.toString() ).append( "\n" );
146-
}
147-
builder.append( "]" );
148-
throw new ConstraintViolationException( builder.toString(), propagatedViolations );
153+
throw new ConstraintViolationException(
154+
message( operation, classNames, groups, constraintViolations ),
155+
propagatedViolations );
149156
}
150157
}
151158
}
152159
}
153160

154-
private String toString(Class<?>[] groups) {
155-
final var string = new StringBuilder( "[" );
161+
private <T> String message(
162+
GroupsPerOperation.Operation operation,
163+
Set<String> classNames,
164+
Class<?>[] groups,
165+
Set<ConstraintViolation<T>> constraintViolations) {
166+
final var builder = new StringBuilder();
167+
builder.append( "Validation failed for classes " )
168+
.append( classNames )
169+
.append( " during " )
170+
.append( operation.getName() )
171+
.append( " time for groups [" );
156172
for ( var group : groups ) {
157-
string.append( group.getName() ).append( ", " );
173+
builder.append( group.getName() ).append( ", " );
174+
}
175+
builder.append( "]\nList of constraint violations:[\n" );
176+
for ( var violation : constraintViolations ) {
177+
builder.append( "\t" )
178+
.append( violation.toString() )
179+
.append( "\n" );
158180
}
159-
string.append( "]" );
160-
return string.toString();
181+
builder.append( "]" );
182+
return builder.toString();
161183
}
162184
}

0 commit comments

Comments
 (0)