Skip to content

Commit 25d8fb7

Browse files
simple DML translation
1 parent 21096b1 commit 25d8fb7

16 files changed

+815
-428
lines changed
Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.mongodb.hibernate.query.select;
17+
package com.mongodb.hibernate.query;
1818

1919
import static com.mongodb.hibernate.MongoTestAssertions.assertIterableEq;
2020
import static org.assertj.core.api.Assertions.assertThat;
2121
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2222

23+
import com.mongodb.client.MongoCollection;
2324
import com.mongodb.hibernate.TestCommandListener;
2425
import com.mongodb.hibernate.junit.MongoExtension;
2526
import java.util.List;
2627
import java.util.function.Consumer;
2728
import org.assertj.core.api.InstanceOfAssertFactories;
2829
import org.bson.BsonDocument;
30+
import org.hibernate.query.MutationQuery;
2931
import org.hibernate.query.SelectionQuery;
3032
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
3133
import org.hibernate.testing.orm.junit.ServiceRegistryScopeAware;
@@ -36,17 +38,17 @@
3638

3739
@SessionFactory(exportSchema = false)
3840
@ExtendWith(MongoExtension.class)
39-
abstract class AbstractSelectionQueryIntegrationTests implements SessionFactoryScopeAware, ServiceRegistryScopeAware {
41+
public abstract class AbstractQueryIntegrationTests implements SessionFactoryScopeAware, ServiceRegistryScopeAware {
4042

4143
private SessionFactoryScope sessionFactoryScope;
4244

4345
private TestCommandListener testCommandListener;
4446

45-
SessionFactoryScope getSessionFactoryScope() {
47+
protected SessionFactoryScope getSessionFactoryScope() {
4648
return sessionFactoryScope;
4749
}
4850

49-
TestCommandListener getTestCommandListener() {
51+
protected TestCommandListener getTestCommandListener() {
5052
return testCommandListener;
5153
}
5254

@@ -60,7 +62,7 @@ public void injectServiceRegistryScope(ServiceRegistryScope serviceRegistryScope
6062
this.testCommandListener = serviceRegistryScope.getRegistry().requireService(TestCommandListener.class);
6163
}
6264

63-
<T> void assertSelectionQuery(
65+
protected <T> void assertSelectionQuery(
6466
String hql,
6567
Class<T> resultType,
6668
Consumer<SelectionQuery<T>> queryPostProcessor,
@@ -74,11 +76,12 @@ <T> void assertSelectionQuery(
7476
resultList -> assertIterableEq(expectedResultList, resultList));
7577
}
7678

77-
<T> void assertSelectionQuery(String hql, Class<T> resultType, String expectedMql, List<T> expectedResultList) {
79+
protected <T> void assertSelectionQuery(
80+
String hql, Class<T> resultType, String expectedMql, List<T> expectedResultList) {
7881
assertSelectionQuery(hql, resultType, null, expectedMql, expectedResultList);
7982
}
8083

81-
<T> void assertSelectionQuery(
84+
protected <T> void assertSelectionQuery(
8285
String hql,
8386
Class<T> resultType,
8487
Consumer<SelectionQuery<T>> queryPostProcessor,
@@ -97,12 +100,12 @@ <T> void assertSelectionQuery(
97100
});
98101
}
99102

100-
<T> void assertSelectionQuery(
103+
protected <T> void assertSelectionQuery(
101104
String hql, Class<T> resultType, String expectedMql, Consumer<List<T>> resultListVerifier) {
102105
assertSelectionQuery(hql, resultType, null, expectedMql, resultListVerifier);
103106
}
104107

105-
<T> void assertSelectQueryFailure(
108+
protected <T> void assertSelectQueryFailure(
106109
String hql,
107110
Class<T> resultType,
108111
Consumer<SelectionQuery<T>> queryPostProcessor,
@@ -120,7 +123,7 @@ <T> void assertSelectQueryFailure(
120123
.hasMessage(expectedExceptionMessage, expectedExceptionMessageParameters));
121124
}
122125

123-
<T> void assertSelectQueryFailure(
126+
protected <T> void assertSelectQueryFailure(
124127
String hql,
125128
Class<T> resultType,
126129
Class<? extends Exception> expectedExceptionType,
@@ -135,12 +138,39 @@ <T> void assertSelectQueryFailure(
135138
expectedExceptionMessageParameters);
136139
}
137140

138-
void assertActualCommand(BsonDocument expectedCommand) {
141+
protected void assertActualCommand(BsonDocument expectedCommand) {
139142
var capturedCommands = testCommandListener.getStartedCommands();
140143

141144
assertThat(capturedCommands)
142145
.singleElement()
143146
.asInstanceOf(InstanceOfAssertFactories.MAP)
144147
.containsAllEntriesOf(expectedCommand);
145148
}
149+
150+
protected void assertMutateQuery(
151+
String hql,
152+
Consumer<MutationQuery> queryPostProcessor,
153+
int expectedMutatedCount,
154+
String expectedMql,
155+
MongoCollection<BsonDocument> collection,
156+
Iterable<BsonDocument> expectedDocuments) {
157+
sessionFactoryScope.inTransaction(session -> {
158+
var mutationQuery = session.createMutationQuery(hql);
159+
if (queryPostProcessor != null) {
160+
queryPostProcessor.accept(mutationQuery);
161+
}
162+
assertThat(mutationQuery.executeUpdate()).isEqualTo(expectedMutatedCount);
163+
assertActualCommand(BsonDocument.parse(expectedMql));
164+
});
165+
assertThat(collection.find()).containsExactlyElementsOf(expectedDocuments);
166+
}
167+
168+
protected void assertMutateQuery(
169+
String hql,
170+
int expectedMutatedCount,
171+
String expectedMql,
172+
MongoCollection<BsonDocument> collection,
173+
Iterable<BsonDocument> expectedDocuments) {
174+
assertMutateQuery(hql, null, expectedMutatedCount, expectedMql, collection, expectedDocuments);
175+
}
146176
}

src/integrationTest/java/com/mongodb/hibernate/query/select/Book.java renamed to src/integrationTest/java/com/mongodb/hibernate/query/Book.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,36 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.mongodb.hibernate.query.select;
17+
package com.mongodb.hibernate.query;
1818

1919
import jakarta.persistence.Entity;
2020
import jakarta.persistence.Id;
2121
import jakarta.persistence.Table;
2222
import java.math.BigDecimal;
2323

2424
@Entity(name = "Book")
25-
@Table(name = "books")
26-
class Book {
25+
@Table(name = Book.COLLECTION_NAME)
26+
public class Book {
27+
public static final String COLLECTION_NAME = "books";
28+
2729
@Id
28-
int id;
30+
public int id;
2931

3032
// TODO-HIBERNATE-48 dummy values are set for currently null value is not supported
31-
String title = "";
32-
Boolean outOfStock = false;
33-
Integer publishYear = 0;
34-
Long isbn13 = 0L;
35-
Double discount = 0.0;
36-
BigDecimal price = new BigDecimal("0.0");
33+
public String title = "";
34+
public Boolean outOfStock = false;
35+
public Integer publishYear = 0;
36+
public Long isbn13 = 0L;
37+
public Double discount = 0.0;
38+
public BigDecimal price = new BigDecimal("0.0");
39+
40+
public Book() {}
3741

38-
Book() {}
42+
public Book(int id, String title, Integer publishYear) {
43+
this(id, title, publishYear, false);
44+
}
3945

40-
Book(int id, String title, Integer publishYear, Boolean outOfStock) {
46+
public Book(int id, String title, Integer publishYear, Boolean outOfStock) {
4147
this.id = id;
4248
this.title = title;
4349
this.publishYear = publishYear;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2025-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.hibernate.query.mutate;
18+
19+
import static java.util.Collections.singleton;
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.hibernate.cfg.JdbcSettings.DIALECT;
22+
23+
import com.mongodb.hibernate.dialect.MongoDialect;
24+
import com.mongodb.hibernate.query.AbstractQueryIntegrationTests;
25+
import java.util.Set;
26+
import java.util.function.Consumer;
27+
import org.hibernate.dialect.Dialect;
28+
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
29+
import org.hibernate.engine.spi.SessionFactoryImplementor;
30+
import org.hibernate.query.MutationQuery;
31+
import org.hibernate.sql.ast.SqlAstTranslator;
32+
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
33+
import org.hibernate.sql.ast.tree.MutationStatement;
34+
import org.hibernate.sql.ast.tree.select.SelectStatement;
35+
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
36+
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
37+
import org.hibernate.sql.model.ast.TableMutation;
38+
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
39+
import org.hibernate.testing.orm.junit.ServiceRegistry;
40+
import org.hibernate.testing.orm.junit.Setting;
41+
42+
@ServiceRegistry(
43+
settings = {
44+
@Setting(
45+
name = DIALECT,
46+
value =
47+
"com.mongodb.hibernate.query.mutate.AbstractMutateQueryIntegrationTests$MutateTranslatorAwareDialect"),
48+
})
49+
class AbstractMutateQueryIntegrationTests extends AbstractQueryIntegrationTests {
50+
51+
void assertAffectedTableNames(String mutateHql, String expectedAffectedTableName) {
52+
assertAffectedTableNames(mutateHql, null, singleton(expectedAffectedTableName));
53+
}
54+
55+
void assertAffectedTableNames(
56+
String mutateHql, Consumer<MutationQuery> queryPostProcessor, String expectedAffectedTableName) {
57+
assertAffectedTableNames(mutateHql, queryPostProcessor, singleton(expectedAffectedTableName));
58+
}
59+
60+
void assertAffectedTableNames(
61+
String mutateHql, Consumer<MutationQuery> queryPostProcessor, Set<String> expectedAffectedTableNames) {
62+
getSessionFactoryScope().inTransaction(session -> {
63+
var query = session.createMutationQuery(mutateHql);
64+
if (queryPostProcessor != null) {
65+
queryPostProcessor.accept(query);
66+
}
67+
query.executeUpdate();
68+
var mutateTranslator =
69+
((MutateTranslatorAwareDialect) session.getJdbcServices().getDialect()).getMutateSqlAstTranslator();
70+
assertThat(mutateTranslator.getAffectedTableNames()).isEqualTo(expectedAffectedTableNames);
71+
});
72+
}
73+
74+
public static final class MutateTranslatorAwareDialect extends Dialect {
75+
private final Dialect delegate;
76+
private SqlAstTranslator<? extends JdbcOperationQueryMutation> mutateSqlAstTranslator;
77+
78+
public MutateTranslatorAwareDialect(DialectResolutionInfo info) {
79+
super(info);
80+
delegate = new MongoDialect(info);
81+
}
82+
83+
@Override
84+
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
85+
return new SqlAstTranslatorFactory() {
86+
@Override
87+
public SqlAstTranslator<JdbcOperationQuerySelect> buildSelectTranslator(
88+
SessionFactoryImplementor sessionFactory, SelectStatement statement) {
89+
return delegate.getSqlAstTranslatorFactory().buildSelectTranslator(sessionFactory, statement);
90+
}
91+
92+
@Override
93+
public SqlAstTranslator<? extends JdbcOperationQueryMutation> buildMutationTranslator(
94+
SessionFactoryImplementor sessionFactory, MutationStatement statement) {
95+
mutateSqlAstTranslator =
96+
delegate.getSqlAstTranslatorFactory().buildMutationTranslator(sessionFactory, statement);
97+
return mutateSqlAstTranslator;
98+
}
99+
100+
@Override
101+
public <O extends JdbcMutationOperation> SqlAstTranslator<O> buildModelMutationTranslator(
102+
TableMutation<O> mutation, SessionFactoryImplementor sessionFactory) {
103+
return delegate.getSqlAstTranslatorFactory().buildModelMutationTranslator(mutation, sessionFactory);
104+
}
105+
};
106+
}
107+
108+
SqlAstTranslator<? extends JdbcOperationQueryMutation> getMutateSqlAstTranslator() {
109+
return mutateSqlAstTranslator;
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)