-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HHH-16084 - MERGE (upsert) for optional table updates - H2
- Loading branch information
Showing
6 changed files
with
120 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 10 additions & 9 deletions
19
hibernate-core/src/test/java/org/hibernate/orm/test/secondarytable/Record.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,27 @@ | ||
package org.hibernate.orm.test.secondarytable; | ||
|
||
import org.hibernate.annotations.SecondaryRow; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.SecondaryTable; | ||
import jakarta.persistence.SequenceGenerator; | ||
import jakarta.persistence.Table; | ||
import org.hibernate.annotations.SecondaryRow; | ||
|
||
@Entity | ||
@Table(name = "Overview") | ||
@SecondaryTable(name = "Details") | ||
@SecondaryTable(name = "Extras") | ||
@SecondaryRow(table = "Details", optional = false) | ||
@SecondaryRow(table = "Extras", optional = true) | ||
@Table(name = "Details") | ||
@SecondaryTable(name = "NonOptional") | ||
@SecondaryTable(name = "Optional") | ||
@SecondaryRow(table = "NonOptional", optional = false) | ||
@SecondaryRow(table = "Optional", optional = true) | ||
@SequenceGenerator(name="RecordSeq", sequenceName = "RecordId", allocationSize = 1) | ||
public class Record { | ||
@Id @GeneratedValue(generator = "RecordSeq") long id; | ||
String name; | ||
@Column(table = "Details") String text; | ||
@Column(table = "Details") boolean enabled; | ||
@Column(table = "Extras", name="`comment`") String comment; | ||
@Column(table = "NonOptional") String text; | ||
@Column(table = "NonOptional") boolean enabled; | ||
@Column(table = "Optional", name="`comment`") String comment; | ||
} | ||
|
161 changes: 68 additions & 93 deletions
161
hibernate-core/src/test/java/org/hibernate/orm/test/secondarytable/SecondaryRowTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,88 @@ | ||
package org.hibernate.orm.test.secondarytable; | ||
|
||
import java.time.LocalDateTime; | ||
import java.time.Instant; | ||
|
||
import org.hibernate.dialect.H2Dialect; | ||
import org.hibernate.engine.spi.SessionImplementor; | ||
|
||
import org.hibernate.testing.orm.junit.DomainModel; | ||
import org.hibernate.testing.orm.junit.SessionFactory; | ||
import org.hibernate.testing.orm.junit.SessionFactoryScope; | ||
import org.hibernate.testing.orm.junit.SkipForDialect; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@DomainModel(annotatedClasses = {Record.class,SpecialRecord.class}) | ||
@SessionFactory | ||
public class SecondaryRowTest { | ||
@Test | ||
@SkipForDialect( | ||
dialectClass = H2Dialect.class, | ||
reason = "This test relies on SQL execution counts which is based on the legacy multi-statement solution. " + | ||
"HHH-16084 adds support for H2 MERGE which handles those cases in one statement, so the counts are off. " + | ||
"Need to change this test to physically check the number of rows. " + | ||
"See e.g. org.hibernate.orm.test.write.UpsertTests" | ||
) | ||
public void testSecondaryRow(SessionFactoryScope scope) { | ||
// we need to check the actual number of rows. | ||
// because we now support merge/upsert SQL statements, the | ||
// because HHH-16084 implements support for usage | ||
int seq = scope.getSessionFactory().getJdbcServices().getDialect().getSequenceSupport().supportsSequences() | ||
? 1 : 0; | ||
|
||
Record record = new Record(); | ||
record.enabled = true; | ||
record.text = "Hello World!"; | ||
scope.inTransaction(s -> s.persist(record)); | ||
scope.getCollectingStatementInspector().assertExecutedCount(2+seq); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
Record record2 = new Record(); | ||
record2.enabled = true; | ||
record2.text = "Hello World!"; | ||
record2.comment = "Goodbye"; | ||
scope.inTransaction(s -> s.persist(record2)); | ||
scope.getCollectingStatementInspector().assertExecutedCount(3+seq); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
SpecialRecord specialRecord = new SpecialRecord(); | ||
specialRecord.enabled = true; | ||
specialRecord.text = "Hello World!"; | ||
specialRecord.validated = LocalDateTime.now(); | ||
scope.inTransaction(s -> s.persist(specialRecord)); | ||
scope.getCollectingStatementInspector().assertExecutedCount(3+seq); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
scope.inTransaction(s -> assertNotNull(s.find(Record.class, record.id))); | ||
scope.getCollectingStatementInspector().assertExecutedCount(1); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
scope.inTransaction(s -> assertNotNull(s.find(Record.class, record2.id))); | ||
scope.getCollectingStatementInspector().assertExecutedCount(1); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
scope.inTransaction(s -> assertNotNull(s.find(Record.class, specialRecord.id))); | ||
scope.getCollectingStatementInspector().assertExecutedCount(1); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
scope.inTransaction(s -> assertEquals(3, s.createQuery("from Record").getResultList().size())); | ||
scope.getCollectingStatementInspector().assertExecutedCount(1); | ||
scope.getCollectingStatementInspector().clear(); | ||
|
||
scope.inTransaction(s -> assertEquals(1, s.createQuery("from SpecialRecord").getResultList().size())); | ||
scope.getCollectingStatementInspector().assertExecutedCount(1); | ||
scope.getCollectingStatementInspector().clear(); | ||
void testSecondaryTableOptionality(SessionFactoryScope scope) { | ||
scope.inSession( (session) -> { | ||
verifySecondaryRows( "Optional", 0, session ); | ||
verifySecondaryRows( "NonOptional", 0, session ); | ||
} ); | ||
|
||
final Record created = scope.fromTransaction( (session) -> { | ||
Record record = new Record(); | ||
record.enabled = true; | ||
record.text = "Hello World!"; | ||
|
||
session.persist( record ); | ||
return record; | ||
} ); | ||
scope.inSession( (session) -> { | ||
verifySecondaryRows( "Optional", 0, session ); | ||
verifySecondaryRows( "NonOptional", 1, session ); | ||
} ); | ||
|
||
created.comment = "I was here"; | ||
final Record merged = scope.fromTransaction( (session) -> session.merge( created ) ); | ||
scope.inSession( (session) -> { | ||
verifySecondaryRows( "Optional", 1, session ); | ||
verifySecondaryRows( "NonOptional", 1, session ); | ||
} ); | ||
|
||
merged.comment = null; | ||
scope.inTransaction( (session) -> session.merge( merged ) ); | ||
scope.inSession( (session) -> { | ||
verifySecondaryRows( "Optional", 0, session ); | ||
verifySecondaryRows( "NonOptional", 1, session ); | ||
} ); | ||
} | ||
|
||
scope.inTransaction(s -> { | ||
Record r = s.find(Record.class, record.id); | ||
r.text = "new text"; | ||
r.comment = "the comment"; | ||
}); | ||
scope.getCollectingStatementInspector().assertExecutedCount(3); | ||
assertTrue( scope.getCollectingStatementInspector().getSqlQueries().get(1).startsWith("update ") ); | ||
assertTrue( scope.getCollectingStatementInspector().getSqlQueries().get(2).startsWith("insert ") ); | ||
scope.getCollectingStatementInspector().clear(); | ||
@Test | ||
public void testOwnedSecondaryTable(SessionFactoryScope scope) { | ||
final String View_name = scope.getSessionFactory().getJdbcServices().getDialect().quote( "`View`" ); | ||
verifySecondaryRows( View_name, 0, scope ); | ||
|
||
final SpecialRecord created = scope.fromTransaction( (session) -> { | ||
final SpecialRecord record = new SpecialRecord(); | ||
record.enabled = true; | ||
record.text = "Hello World!"; | ||
session.persist( record ); | ||
return record; | ||
} ); | ||
verifySecondaryRows( View_name, 0, scope ); | ||
|
||
created.timestamp = Instant.now(); | ||
final SpecialRecord merged = scope.fromTransaction( (session) -> session.merge( created ) ); | ||
verifySecondaryRows( View_name, 0, scope ); | ||
} | ||
|
||
scope.inTransaction(s -> { | ||
Record r = s.find(Record.class, record.id); | ||
r.comment = "new comment"; | ||
}); | ||
scope.getCollectingStatementInspector().assertExecutedCount(2); | ||
assertTrue( scope.getCollectingStatementInspector().getSqlQueries().get(1).startsWith("update ") ); | ||
scope.getCollectingStatementInspector().clear(); | ||
@AfterEach | ||
void cleanUpTestData(SessionFactoryScope scope) { | ||
scope.inTransaction( (session) -> { | ||
session.createSelectionQuery( "from Record" ).stream().forEach( session::remove ); | ||
} ); | ||
} | ||
|
||
scope.inTransaction(s -> { | ||
Record r = s.find(Record.class, record2.id); | ||
r.comment = null; | ||
}); | ||
scope.getCollectingStatementInspector().assertExecutedCount(2); | ||
assertTrue( scope.getCollectingStatementInspector().getSqlQueries().get(1).startsWith("delete ") ); | ||
scope.getCollectingStatementInspector().clear(); | ||
private static void verifySecondaryRows(String table, int expectedCount, SessionFactoryScope sfScope) { | ||
sfScope.inTransaction( (session) -> verifySecondaryRows( table, expectedCount, session ) ); | ||
} | ||
|
||
scope.inTransaction(s -> { | ||
SpecialRecord r = s.find(SpecialRecord.class, specialRecord.id); | ||
r.validated = null; | ||
r.timestamp = System.currentTimeMillis(); | ||
}); | ||
scope.getCollectingStatementInspector().assertExecutedCount(2); | ||
assertTrue( scope.getCollectingStatementInspector().getSqlQueries().get(1).startsWith("delete ") ); | ||
scope.getCollectingStatementInspector().clear(); | ||
private static void verifySecondaryRows(String table, int expectedCount, SessionImplementor session) { | ||
final String sql = "select count(1) from " + table; | ||
final int count = session.createNativeQuery( sql, Integer.class ).getSingleResult(); | ||
assertThat( count ).isEqualTo( expectedCount ); | ||
} | ||
} |
12 changes: 5 additions & 7 deletions
12
hibernate-core/src/test/java/org/hibernate/orm/test/secondarytable/SpecialRecord.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,17 @@ | ||
package org.hibernate.orm.test.secondarytable; | ||
|
||
import java.time.Instant; | ||
|
||
import org.hibernate.annotations.SecondaryRow; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.SecondaryTable; | ||
import org.hibernate.annotations.SecondaryRow; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@Entity | ||
@SecondaryTable(name = "Options") | ||
@SecondaryTable(name = "`View`") | ||
@SecondaryRow(table = "`View`", owned = false) | ||
public class SpecialRecord extends Record { | ||
@Column(table = "Options") | ||
LocalDateTime validated; | ||
@Column(table = "`View`", name="`timestamp`") | ||
Long timestamp; | ||
Instant timestamp; | ||
} |
Oops, something went wrong.