Skip to content

Commit 3a3edc2

Browse files
committed
HHH-19840 Add test for issue
1 parent 66e6184 commit 3a3edc2

File tree

3 files changed

+264
-117
lines changed

3 files changed

+264
-117
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.annotations;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.Id;
10+
import org.hibernate.annotations.CreationTimestamp;
11+
import org.hibernate.annotations.SourceType;
12+
import org.hibernate.annotations.UpdateTimestamp;
13+
import org.hibernate.cfg.AvailableSettings;
14+
import org.hibernate.generator.internal.CurrentTimestampGeneration;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.Jira;
17+
import org.hibernate.testing.orm.junit.ServiceRegistry;
18+
import org.hibernate.testing.orm.junit.SessionFactory;
19+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
20+
import org.hibernate.testing.orm.junit.Setting;
21+
import org.hibernate.testing.orm.junit.SettingProvider;
22+
import org.junit.jupiter.api.Test;
23+
24+
import java.time.Instant;
25+
import java.util.stream.IntStream;
26+
27+
import static java.lang.Thread.sleep;
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertNotNull;
31+
import static org.junit.jupiter.api.Assertions.assertTrue;
32+
33+
@DomainModel(annotatedClasses = InMemoryTimestampGenerationBatchTest.Person.class)
34+
@SessionFactory(generateStatistics = true)
35+
@ServiceRegistry(settings = @Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "5"),
36+
settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME,
37+
provider = InMemoryTimestampGenerationBatchTest.MutableClockProvider.class))
38+
@Jira("https://hibernate.atlassian.net/browse/HHH-19840")
39+
public class InMemoryTimestampGenerationBatchTest {
40+
private static final MutableClock clock = new MutableClock();
41+
42+
private static final int PERSON_COUNT = 8;
43+
44+
@Test
45+
public void test(SessionFactoryScope scope) throws InterruptedException {
46+
final var statistics = scope.getSessionFactory().getStatistics();
47+
scope.inTransaction( session -> {
48+
Person person = null;
49+
for ( int i = 1; i <= PERSON_COUNT; i++ ) {
50+
person = new Person();
51+
person.setId( (long) i );
52+
person.setName( "person_" + i );
53+
session.persist( person );
54+
}
55+
56+
statistics.clear();
57+
session.flush();
58+
59+
assertEquals( 1, statistics.getPrepareStatementCount(), "Expected updates to execute in batches" );
60+
61+
assertNotNull( person.getCreatedOn() );
62+
assertNotNull( person.getUpdatedOn() );
63+
} );
64+
65+
66+
clock.tick();
67+
sleep( 1 );
68+
69+
scope.inTransaction( session -> {
70+
final var persons = session.findMultiple( Person.class,
71+
IntStream.rangeClosed( 1, PERSON_COUNT )
72+
.mapToObj( i -> (long) i )
73+
.toList() );
74+
75+
assertThat( persons ).hasSize( PERSON_COUNT );
76+
assertThat( persons ).doesNotContainNull();
77+
78+
Person person = null;
79+
for ( final Person p : persons ) {
80+
p.setName( p.getName() + "_updated" );
81+
person = p;
82+
}
83+
84+
final var createdOn = person.getCreatedOn();
85+
final var updatedOn = person.getUpdatedOn();
86+
87+
statistics.clear();
88+
session.flush();
89+
90+
assertEquals( 1, statistics.getPrepareStatementCount(), "Expected updates to execute in batches" );
91+
92+
assertEquals( person.getCreatedOn(), createdOn );
93+
assertTrue( person.getUpdatedOn().isAfter( updatedOn ) );
94+
} );
95+
}
96+
97+
public static class MutableClockProvider implements SettingProvider.Provider<Object> {
98+
@Override
99+
public Object getSetting() {
100+
return clock;
101+
}
102+
}
103+
104+
@Entity(name = "Person")
105+
public static class Person {
106+
@Id
107+
private Long id;
108+
109+
private String name;
110+
111+
@Column(nullable = false)
112+
@CreationTimestamp(source = SourceType.VM)
113+
private Instant createdOn;
114+
115+
@Column(nullable = false)
116+
@UpdateTimestamp(source = SourceType.VM)
117+
private Instant updatedOn;
118+
119+
public Long getId() {
120+
return id;
121+
}
122+
123+
public void setId(Long id) {
124+
this.id = id;
125+
}
126+
127+
public String getName() {
128+
return name;
129+
}
130+
131+
public void setName(String name) {
132+
this.name = name;
133+
}
134+
135+
public Instant getCreatedOn() {
136+
return createdOn;
137+
}
138+
139+
public Instant getUpdatedOn() {
140+
return updatedOn;
141+
}
142+
}
143+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.annotations;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.Id;
10+
import org.hibernate.annotations.CreationTimestamp;
11+
import org.hibernate.annotations.SourceType;
12+
import org.hibernate.annotations.UpdateTimestamp;
13+
import org.hibernate.generator.internal.CurrentTimestampGeneration;
14+
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
15+
import org.hibernate.testing.orm.junit.Jira;
16+
import org.hibernate.testing.orm.junit.Jpa;
17+
import org.hibernate.testing.orm.junit.SettingProvider;
18+
import org.junit.jupiter.api.Test;
19+
20+
import java.time.Instant;
21+
22+
import static java.lang.Thread.sleep;
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
import static org.junit.jupiter.api.Assertions.assertTrue;
26+
27+
@Jpa(annotatedClasses = InMemoryTimestampGenerationTest.Person.class,
28+
settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME,
29+
provider = InMemoryTimestampGenerationTest.MutableClockProvider.class))
30+
@Jira("https://hibernate.atlassian.net/browse/HHH-19840")
31+
public class InMemoryTimestampGenerationTest {
32+
private static final MutableClock clock = new MutableClock();
33+
34+
@Test
35+
public void test(EntityManagerFactoryScope scope) throws InterruptedException {
36+
final Instant createdOn = scope.fromTransaction( entityManager -> {
37+
Person person = new Person();
38+
person.setId( 1L );
39+
person.setFirstName( "Jon" );
40+
person.setLastName( "Doe" );
41+
entityManager.persist( person );
42+
43+
entityManager.flush();
44+
45+
assertNotNull( person.getCreatedOn() );
46+
assertNotNull( person.getUpdatedOn() );
47+
return person.getCreatedOn();
48+
} );
49+
50+
clock.tick();
51+
sleep( 1 );
52+
53+
scope.inTransaction( entityManager -> {
54+
final Person person = entityManager.find( Person.class, 1L );
55+
final Instant updatedOn = person.getUpdatedOn();
56+
person.setLastName( "Doe Jr." );
57+
58+
entityManager.flush();
59+
60+
assertEquals( person.getCreatedOn(), createdOn );
61+
assertTrue( person.getUpdatedOn().isAfter( updatedOn ) );
62+
} );
63+
}
64+
65+
static class MutableClockProvider implements SettingProvider.Provider<Object> {
66+
@Override
67+
public Object getSetting() {
68+
return clock;
69+
}
70+
}
71+
72+
@Entity(name = "Person")
73+
static class Person {
74+
@Id
75+
private Long id;
76+
77+
private String firstName;
78+
79+
private String lastName;
80+
81+
@Column(nullable = false)
82+
@CreationTimestamp(source= SourceType.VM)
83+
private Instant createdOn;
84+
85+
@Column(nullable = false)
86+
@UpdateTimestamp(source= SourceType.VM)
87+
private Instant updatedOn;
88+
89+
public Long getId() {
90+
return id;
91+
}
92+
93+
public void setId(Long id) {
94+
this.id = id;
95+
}
96+
97+
public String getFirstName() {
98+
return firstName;
99+
}
100+
101+
public void setFirstName(String firstName) {
102+
this.firstName = firstName;
103+
}
104+
105+
public String getLastName() {
106+
return lastName;
107+
}
108+
109+
public void setLastName(String lastName) {
110+
this.lastName = lastName;
111+
}
112+
113+
public Instant getCreatedOn() {
114+
return createdOn;
115+
}
116+
117+
public Instant getUpdatedOn() {
118+
return updatedOn;
119+
}
120+
}
121+
}

hibernate-core/src/test/java/org/hibernate/orm/test/annotations/InMemoryUpdateTimestampTest.java

Lines changed: 0 additions & 117 deletions
This file was deleted.

0 commit comments

Comments
 (0)