Skip to content

Commit 928c786

Browse files
committed
HHH-18484 Add test for issue
1 parent 417eb89 commit 928c786

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
6+
*/
7+
package org.hibernate.orm.test.mapping.generated;
8+
9+
import java.time.ZonedDateTime;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.Locale;
13+
14+
import org.hibernate.annotations.CurrentTimestamp;
15+
16+
import org.hibernate.testing.jdbc.SQLStatementInspector;
17+
import org.hibernate.testing.orm.junit.DomainModel;
18+
import org.hibernate.testing.orm.junit.Jira;
19+
import org.hibernate.testing.orm.junit.SessionFactory;
20+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
21+
import org.junit.jupiter.api.AfterAll;
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
25+
import jakarta.persistence.CascadeType;
26+
import jakarta.persistence.Entity;
27+
import jakarta.persistence.GeneratedValue;
28+
import jakarta.persistence.Id;
29+
import jakarta.persistence.ManyToOne;
30+
import jakarta.persistence.OneToMany;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
* @author Marco Belladelli
36+
*/
37+
@DomainModel( annotatedClasses = {
38+
GeneratedNoOpUpdateTest.Pizza.class,
39+
GeneratedNoOpUpdateTest.Topping.class,
40+
} )
41+
@SessionFactory(useCollectingStatementInspector = true)
42+
@Jira( "https://hibernate.atlassian.net/browse/HHH-18484" )
43+
public class GeneratedNoOpUpdateTest {
44+
@Test
45+
public void testUpdate(SessionFactoryScope scope) {
46+
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
47+
inspector.clear();
48+
49+
final ZonedDateTime updatedTime = scope.fromTransaction( session -> {
50+
final Pizza pizza = session.find( Pizza.class, 1L );
51+
final ZonedDateTime initialTime = pizza.getLastUpdated();
52+
// Create a new topping
53+
final Topping newTopping1 = new Topping();
54+
newTopping1.setName( "Cheese" );
55+
newTopping1.setPizza( pizza );
56+
// Let's mutate the existing list
57+
pizza.getToppings().add( newTopping1 );
58+
session.flush();
59+
// pizza was not dirty so no update is executed
60+
inspector.assertNoUpdate();
61+
assertThat( pizza.getLastUpdated() ).isEqualTo( initialTime );
62+
return pizza.getLastUpdated();
63+
} );
64+
65+
inspector.clear();
66+
scope.inTransaction( session -> {
67+
// Now let's try adding a new topping via a new list
68+
final Pizza pizza = session.find( Pizza.class, 1L );
69+
// Create a new topping
70+
final Topping newTopping2 = new Topping();
71+
newTopping2.setName( "Mushroom" );
72+
newTopping2.setPizza( pizza );
73+
// This time, instead of mutating the existing list, we're creating a new list
74+
pizza.setToppings( List.of( pizza.getToppings().get( 0 ), newTopping2 ) );
75+
session.flush();
76+
// pizza this time was dirty, but still no update is executed because
77+
// only the unowned one-to-many association has changed
78+
inspector.assertNoUpdate();
79+
assertThat( pizza.getLastUpdated() ).isEqualTo( updatedTime );
80+
} );
81+
82+
scope.inTransaction( session -> {
83+
final Pizza pizza = session.find( Pizza.class, 1L );
84+
assertThat( pizza.getToppings() ).hasSize( 3 )
85+
.extracting( Topping::getName )
86+
.containsExactlyInAnyOrder( "Pepperoni", "Cheese", "Mushroom" );
87+
// This time we mutate the pizza to trigger a real update
88+
pizza.setName( "Salamino e funghi" );
89+
session.flush();
90+
assertThat( inspector.getSqlQueries() ).anyMatch( sql -> sql.toLowerCase( Locale.ROOT ).contains( "update" ) );
91+
assertThat( pizza.getLastUpdated() ).isAfter( updatedTime );
92+
} );
93+
}
94+
95+
@BeforeAll
96+
public void setUp(SessionFactoryScope scope) {
97+
scope.inTransaction( session -> {
98+
final Pizza pizza = new Pizza( 1L, "Salamino" );
99+
session.persist( pizza );
100+
final Topping topping = new Topping();
101+
topping.setName( "Pepperoni" );
102+
topping.setPizza( pizza );
103+
pizza.getToppings().add( topping );
104+
} );
105+
}
106+
107+
@AfterAll
108+
public void tearDown(SessionFactoryScope scope) {
109+
scope.getSessionFactory().getSchemaManager().truncateMappedObjects();
110+
}
111+
112+
@Entity( name = "Pizza" )
113+
static class Pizza {
114+
@Id
115+
private Long id;
116+
117+
@OneToMany( mappedBy = "pizza", cascade = CascadeType.ALL )
118+
private List<Topping> toppings = new ArrayList<>();
119+
120+
@CurrentTimestamp
121+
private ZonedDateTime lastUpdated;
122+
123+
private String name;
124+
125+
public Pizza() {
126+
}
127+
128+
public Pizza(Long id, String name) {
129+
this.id = id;
130+
this.name = name;
131+
}
132+
133+
public Long getId() {
134+
return id;
135+
}
136+
137+
public List<Topping> getToppings() {
138+
return toppings;
139+
}
140+
141+
public void setToppings(final List<Topping> toppings) {
142+
this.toppings = toppings;
143+
}
144+
145+
public ZonedDateTime getLastUpdated() {
146+
return lastUpdated;
147+
}
148+
149+
public String getName() {
150+
return name;
151+
}
152+
153+
public void setName(String name) {
154+
this.name = name;
155+
}
156+
}
157+
158+
@Entity( name = "Topping" )
159+
static class Topping {
160+
@Id
161+
@GeneratedValue
162+
private Long id;
163+
164+
@ManyToOne
165+
private Pizza pizza;
166+
167+
private String name;
168+
169+
public void setName(final String name) {
170+
this.name = name;
171+
}
172+
173+
public String getName() {
174+
return name;
175+
}
176+
177+
public void setPizza(final Pizza pizza) {
178+
this.pizza = pizza;
179+
}
180+
181+
}
182+
}

0 commit comments

Comments
 (0)