Skip to content

Commit 8c97acd

Browse files
committed
HHH-19925 - Locking root(s) should be based on select-clause, not from-clause
1 parent c07eec0 commit 8c97acd

File tree

2 files changed

+97
-8
lines changed

2 files changed

+97
-8
lines changed

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,10 +2270,46 @@ private void collectRootPathsForLocking(SqmSelection<?> sqmSelection) {
22702270
if ( rootPathsForLockingCollector == null ) {
22712271
return;
22722272
}
2273-
if ( sqmSelection.getExpressible() instanceof EntityTypeImpl
2274-
&& sqmSelection.getSelectableNode() instanceof SqmPath<?> entityValuedPath ) {
2275-
rootPathsForLockingCollector.accept( entityValuedPath.getNavigablePath() );
2273+
2274+
collectRootPathsForLocking( sqmSelection.getSelectableNode() );
2275+
}
2276+
2277+
private void collectRootPathsForLocking(SqmSelectableNode<?> selectableNode) {
2278+
// roughly speaking we only care about 2 cases here:
2279+
// 1) entity path - the entity will be locked
2280+
// 2) scalar path - the entity from which the path originates will be locked
2281+
//
2282+
// note, however, that we need to account for both cases as the argument to a dynamic instantiation
2283+
2284+
if ( selectableNode instanceof SqmPath<?> selectedPath ) {
2285+
collectRootPathsForLocking( selectedPath );
2286+
}
2287+
else if ( selectableNode instanceof SqmDynamicInstantiation<?> dynamicInstantiation ) {
2288+
collectRootPathsForLocking( dynamicInstantiation );
2289+
}
2290+
}
2291+
2292+
private void collectRootPathsForLocking(SqmPath<?> selectedPath) {
2293+
assert rootPathsForLockingCollector != null;
2294+
2295+
if ( selectedPath == null ) {
2296+
// typically this comes from paths rooted in a CTE.
2297+
// regardless, without a path we cannot evaluate so just return.
2298+
return;
22762299
}
2300+
2301+
if ( selectedPath.getNodeType() instanceof EntityTypeImpl ) {
2302+
rootPathsForLockingCollector.accept( selectedPath.getNavigablePath() );
2303+
}
2304+
else {
2305+
collectRootPathsForLocking( selectedPath.getLhs() );
2306+
}
2307+
}
2308+
2309+
private void collectRootPathsForLocking(SqmDynamicInstantiation<?> dynamicInstantiation) {
2310+
dynamicInstantiation.getArguments().forEach( ( argument ) -> {
2311+
collectRootPathsForLocking( argument.getSelectableNode() );
2312+
} );
22772313
}
22782314

22792315
private void inferTargetPath(int index) {

hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockingBasedOnSelectClauseTests.java

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ void testSubQueryHqlTranslation(SessionFactoryScope factoryScope) {
8484
} );
8585
}
8686

87+
private static final String BOOK_PATH = "org.hibernate.orm.test.locking.LockingBasedOnSelectClauseTests$Book";
88+
private static final String BOOK_PATH_HQL = BOOK_PATH+ "(b)";
89+
private static final String BOOK_AUTHOR_PATH_HQL = BOOK_PATH_HQL + ".author";
90+
8791
@Test
8892
void testBasicHqlTranslation(SessionFactoryScope factoryScope) {
8993
final HqlHelper.HqlTranslation hqlTranslation = HqlHelper.translateHql(
@@ -96,7 +100,56 @@ void testBasicHqlTranslation(SessionFactoryScope factoryScope) {
96100
final SelectStatement selectAst = ( SelectStatement ) sqlAst;
97101
assertThat( selectAst.getRootPathsForLocking() ).hasSize( 1 );
98102
assertThat( selectAst.getRootPathsForLocking().get( 0 ).getFullPath() )
99-
.isEqualTo( "org.hibernate.orm.test.locking.LockingBasedOnSelectClauseTests$Book(b).author");
103+
.isEqualTo( BOOK_AUTHOR_PATH_HQL );
104+
}
105+
106+
@Test
107+
void testScalarHqlTranslation(SessionFactoryScope factoryScope) {
108+
final HqlHelper.HqlTranslation hqlTranslation = HqlHelper.translateHql(
109+
"select b.title from Book b",
110+
factoryScope.getSessionFactory()
111+
);
112+
113+
final Statement sqlAst = hqlTranslation.sqlAst();
114+
assertThat( sqlAst ).isInstanceOf( SelectStatement.class );
115+
final SelectStatement selectAst = ( SelectStatement ) sqlAst;
116+
assertThat( selectAst.getRootPathsForLocking() ).hasSize( 1 );
117+
assertThat( selectAst.getRootPathsForLocking().get( 0 ).getFullPath() )
118+
.isEqualTo( BOOK_PATH_HQL );
119+
}
120+
121+
@Test
122+
void testScalarHqlTranslation2(SessionFactoryScope factoryScope) {
123+
final HqlHelper.HqlTranslation hqlTranslation = HqlHelper.translateHql(
124+
"select b.title, b.author from Book b",
125+
factoryScope.getSessionFactory()
126+
);
127+
128+
final Statement sqlAst = hqlTranslation.sqlAst();
129+
assertThat( sqlAst ).isInstanceOf( SelectStatement.class );
130+
final SelectStatement selectAst = ( SelectStatement ) sqlAst;
131+
assertThat( selectAst.getRootPathsForLocking() ).hasSize( 2 );
132+
assertThat( selectAst.getRootPathsForLocking().get( 0 ).getFullPath() )
133+
.isEqualTo( BOOK_PATH_HQL );
134+
assertThat( selectAst.getRootPathsForLocking().get( 1 ).getFullPath() )
135+
.isEqualTo( BOOK_AUTHOR_PATH_HQL );
136+
}
137+
138+
@Test
139+
void testDynamicInstantiationHqlTranslation(SessionFactoryScope factoryScope) {
140+
final HqlHelper.HqlTranslation hqlTranslation = HqlHelper.translateHql(
141+
"select new list(b.title, b.author) from Book b",
142+
factoryScope.getSessionFactory()
143+
);
144+
145+
final Statement sqlAst = hqlTranslation.sqlAst();
146+
assertThat( sqlAst ).isInstanceOf( SelectStatement.class );
147+
final SelectStatement selectAst = ( SelectStatement ) sqlAst;
148+
assertThat( selectAst.getRootPathsForLocking() ).hasSize( 2 );
149+
assertThat( selectAst.getRootPathsForLocking().get( 0 ).getFullPath() )
150+
.isEqualTo( BOOK_PATH_HQL );
151+
assertThat( selectAst.getRootPathsForLocking().get( 1 ).getFullPath() )
152+
.isEqualTo( BOOK_AUTHOR_PATH_HQL );
100153
}
101154

102155
@Test
@@ -109,25 +162,25 @@ void testLoadingTranslation(SessionFactoryScope factoryScope) {
109162
);
110163
assertThat( translation.sqlAst().getRootPathsForLocking() ).hasSize( 1 );
111164
assertThat( translation.sqlAst().getRootPathsForLocking().get( 0 ).getFullPath() )
112-
.isEqualTo( "org.hibernate.orm.test.locking.LockingBasedOnSelectClauseTests$Book");
165+
.isEqualTo( BOOK_PATH );
113166
}
114167

115168
@Entity(name="Book")
116169
@Table(name="books")
117170
public static class Book {
118171
@Id
119172
private Integer id;
120-
private String name;
173+
private String title;
121174
@ManyToOne(fetch = FetchType.LAZY)
122175
@JoinColumn(name = "author_fk")
123176
private Author author;
124177

125178
public Book() {
126179
}
127180

128-
public Book(Integer id, String name, Author author) {
181+
public Book(Integer id, String title, Author author) {
129182
this.id = id;
130-
this.name = name;
183+
this.title = title;
131184
this.author = author;
132185
}
133186
}

0 commit comments

Comments
 (0)