Skip to content

Commit 667136c

Browse files
committed
HHH-11237 - Added test case.
1 parent 995e5d6 commit 667136c

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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.test.component.empty;
8+
9+
import java.io.Serializable;
10+
import java.util.concurrent.atomic.AtomicInteger;
11+
12+
import javax.persistence.Embeddable;
13+
import javax.persistence.Embedded;
14+
import javax.persistence.Entity;
15+
import javax.persistence.Id;
16+
import javax.persistence.Version;
17+
18+
import org.hibernate.EmptyInterceptor;
19+
import org.hibernate.annotations.SelectBeforeUpdate;
20+
import org.hibernate.cfg.AvailableSettings;
21+
import org.hibernate.cfg.Configuration;
22+
import org.hibernate.type.Type;
23+
import org.junit.Test;
24+
25+
import org.hibernate.testing.TestForIssue;
26+
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
27+
import org.hibernate.testing.transaction.TransactionUtil;
28+
29+
import static org.junit.Assert.assertEquals;
30+
31+
/**
32+
* @author Chris Cranford
33+
*/
34+
public class SelectBeforeUpdateEmbeddedTest extends BaseCoreFunctionalTestCase {
35+
private final OnFlushDirtyInterceptor i = new OnFlushDirtyInterceptor();
36+
37+
@Override
38+
protected Class<?>[] getAnnotatedClasses() {
39+
return new Class<?>[] { Person.class };
40+
}
41+
42+
@Override
43+
protected void configure(Configuration configuration) {
44+
super.configure( configuration );
45+
// applies the OnFlushDirtyInterceptor that is meant to track whether a Flush event fires
46+
// in situations where it ultimately shouldn't because null composites should equate an
47+
// instantiated component with all null properties.
48+
configuration.setInterceptor( i );
49+
}
50+
51+
@Test
52+
@TestForIssue(jiraKey = "HHH-11237")
53+
public void testSelectBeforeUpdateUsingEmptyComposites() {
54+
// Opt-in behavior 5.1+
55+
rebuildSessionFactory( c -> c.setProperty( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, "true" ) );
56+
testSelectBeforeUpdate();
57+
}
58+
59+
@Test
60+
@TestForIssue(jiraKey = "HHH-11237")
61+
public void testSelectBeforeUpdateUsingNullComposites() {
62+
// Legacy behavior test
63+
rebuildSessionFactory( c -> c.setProperty( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, "false" ) );
64+
testSelectBeforeUpdate();
65+
}
66+
67+
/**
68+
* Performs various tests both available attached and detached entities which use
69+
* the {@code @SelectBeforeUpdate} annotation with an {code @Embedded} component.
70+
*/
71+
private void testSelectBeforeUpdate() {
72+
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
73+
final Person john = new Person( 1, "John", new Address() );
74+
session.save( john );
75+
76+
final Person mary = new Person( 2, "Mary", null );
77+
session.save( mary );
78+
} );
79+
80+
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
81+
final Person john = session.find( Person.class, 1 );
82+
i.reset();
83+
john.setAddress( null );
84+
session.flush();
85+
assertEquals( 0, i.getCalls() );
86+
87+
i.reset();
88+
final Person mary = session.find( Person.class, 2 );
89+
mary.setAddress( new Address() );
90+
session.flush();
91+
assertEquals( 0, i.getCalls() );
92+
} );
93+
94+
final Person john = TransactionUtil.doInHibernate( this::sessionFactory, session -> {
95+
return session.get( Person.class, 1 );
96+
} );
97+
98+
final Person mary = TransactionUtil.doInHibernate( this::sessionFactory, session -> {
99+
return session.get( Person.class, 2 );
100+
} );
101+
102+
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
103+
i.reset();
104+
john.setAddress( null );
105+
session.update( john );
106+
session.flush();
107+
assertEquals( 0, i.getCalls() );
108+
109+
i.reset();
110+
mary.setAddress( new Address() );
111+
session.update( mary );
112+
session.flush();
113+
assertEquals( 0, i.getCalls() );
114+
115+
} );
116+
}
117+
118+
@Entity(name = "Person")
119+
@SelectBeforeUpdate
120+
public static class Person {
121+
@Id
122+
private Integer id;
123+
private String name;
124+
@Embedded
125+
private Address address;
126+
@Version
127+
private Integer version;
128+
129+
Person() {
130+
131+
}
132+
133+
Person(Integer id, String name, Address address) {
134+
this.id = id;
135+
this.name = name;
136+
this.address = address;
137+
}
138+
139+
public Integer getId() {
140+
return id;
141+
}
142+
143+
public void setId(Integer id) {
144+
this.id = id;
145+
}
146+
147+
public String getName() {
148+
return name;
149+
}
150+
151+
public void setName(String name) {
152+
this.name = name;
153+
}
154+
155+
public Address getAddress() {
156+
return address;
157+
}
158+
159+
public void setAddress(Address address) {
160+
this.address = address;
161+
}
162+
163+
public Integer getVersion() {
164+
return version;
165+
}
166+
167+
public void setVersion(Integer version) {
168+
this.version = version;
169+
}
170+
}
171+
172+
@Embeddable
173+
public static class Address implements Serializable {
174+
private String postalCode;
175+
private String state;
176+
private String address;
177+
178+
Address() {
179+
180+
}
181+
182+
Address(String postalCode, String state, String address) {
183+
this.postalCode = postalCode;
184+
this.state = state;
185+
this.address = address;
186+
}
187+
188+
public String getPostalCode() {
189+
return postalCode;
190+
}
191+
192+
public void setPostalCode(String postalCode) {
193+
this.postalCode = postalCode;
194+
}
195+
196+
public String getState() {
197+
return state;
198+
}
199+
200+
public void setState(String state) {
201+
this.state = state;
202+
}
203+
204+
public String getAddress() {
205+
return address;
206+
}
207+
208+
public void setAddress(String address) {
209+
this.address = address;
210+
}
211+
212+
@Override
213+
public boolean equals(Object o) {
214+
if ( this == o ) {
215+
return true;
216+
}
217+
if ( o == null || getClass() != o.getClass() ) {
218+
return false;
219+
}
220+
221+
Address address1 = (Address) o;
222+
223+
if ( getPostalCode() != null ? !getPostalCode().equals( address1.getPostalCode() ) : address1.getPostalCode() != null ) {
224+
return false;
225+
}
226+
if ( getState() != null ? !getState().equals( address1.getState() ) : address1.getState() != null ) {
227+
return false;
228+
}
229+
return getAddress() != null ? getAddress().equals( address1.getAddress() ) : address1.getAddress() == null;
230+
}
231+
232+
@Override
233+
public int hashCode() {
234+
int result = getPostalCode() != null ? getPostalCode().hashCode() : 0;
235+
result = 31 * result + ( getState() != null ? getState().hashCode() : 0 );
236+
result = 31 * result + ( getAddress() != null ? getAddress().hashCode() : 0 );
237+
return result;
238+
}
239+
}
240+
241+
public static class OnFlushDirtyInterceptor extends EmptyInterceptor {
242+
private AtomicInteger calls = new AtomicInteger();
243+
244+
@Override
245+
public boolean onFlushDirty(Object entity,
246+
Serializable id,
247+
Object[] currentState,
248+
Object[] previousState,
249+
String[] propertyNames,
250+
Type[] types) {
251+
calls.incrementAndGet();
252+
return false;
253+
}
254+
255+
public int getCalls() {
256+
return calls.get();
257+
}
258+
259+
public void reset() {
260+
calls.set( 0 );
261+
}
262+
}
263+
}

0 commit comments

Comments
 (0)