Skip to content

Commit 0cb5138

Browse files
committed
HHH-11801 Ensure ListProxy and SetProxy implement equals/hashCode
1 parent 207a363 commit 0cb5138

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

hibernate-core/src/main/java/org/hibernate/collection/spi/AbstractPersistentCollection.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import java.util.List;
1414
import java.util.ListIterator;
1515
import java.util.Map;
16+
import java.util.Objects;
17+
import java.util.Set;
1618
import java.util.UUID;
1719

1820
import org.hibernate.AssertionFailure;
@@ -1061,6 +1063,21 @@ public Object[] toArray() {
10611063
public <A> A[] toArray(A[] array) {
10621064
return set.toArray( array );
10631065
}
1066+
1067+
@Override
1068+
public final boolean equals(Object o) {
1069+
if ( o == this ) {
1070+
return true;
1071+
}
1072+
return o instanceof Set<?> s
1073+
&& s.size() == size()
1074+
&& containsAll( s );
1075+
}
1076+
1077+
@Override
1078+
public int hashCode() {
1079+
return Objects.hashCode( set );
1080+
}
10641081
}
10651082

10661083
protected final class ListProxy implements List<E> {
@@ -1195,6 +1212,16 @@ public <A> A[] toArray(A[] array) {
11951212
return list.toArray( array );
11961213
}
11971214

1215+
@Override
1216+
public final boolean equals(Object o) {
1217+
return o == this || list.equals( o );
1218+
}
1219+
1220+
@Override
1221+
public int hashCode() {
1222+
return Objects.hashCode( list );
1223+
}
1224+
11981225
}
11991226

12001227
/**
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.collection;
6+
7+
import jakarta.persistence.Embeddable;
8+
import jakarta.persistence.EmbeddedId;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.Id;
11+
import jakarta.persistence.JoinTable;
12+
import jakarta.persistence.ManyToOne;
13+
import jakarta.persistence.MapKey;
14+
import jakarta.persistence.OneToMany;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.Jira;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.junit.jupiter.api.BeforeEach;
20+
import org.junit.jupiter.api.Test;
21+
22+
import java.io.Serializable;
23+
import java.util.ArrayList;
24+
import java.util.HashMap;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Set;
28+
29+
import static org.hamcrest.CoreMatchers.equalTo;
30+
import static org.hamcrest.CoreMatchers.is;
31+
import static org.hamcrest.CoreMatchers.not;
32+
import static org.hamcrest.CoreMatchers.sameInstance;
33+
import static org.hamcrest.MatcherAssert.assertThat;
34+
35+
@DomainModel(
36+
annotatedClasses = {
37+
ListAndSetProxyTest.TheOne.class,
38+
ListAndSetProxyTest.TheMany.class
39+
}
40+
)
41+
@SessionFactory
42+
@Jira("https://hibernate.atlassian.net/browse/HHH-11801")
43+
public class ListAndSetProxyTest {
44+
45+
@BeforeEach
46+
public void setUp(SessionFactoryScope scope) {
47+
scope.inTransaction(
48+
session -> {
49+
TheOne one = new TheOne( "1" );
50+
session.persist( one );
51+
52+
TheMapKey theMapKey = new TheMapKey( one );
53+
TheMany theMany = new TheMany( theMapKey );
54+
session.persist( theMany );
55+
56+
Map<TheMapKey, TheMany> map = new HashMap<>();
57+
map.put( theMapKey, theMany );
58+
one.setTheManys( map );
59+
one.getTheManyList().add( theMany );
60+
}
61+
);
62+
}
63+
64+
@Test
65+
public void testIt(SessionFactoryScope scope) {
66+
scope.inSession(
67+
session -> {
68+
TheOne one = session.find( TheOne.class, "1" );
69+
70+
Set<TheMapKey> set1 = one.getTheManys().keySet();
71+
Set<TheMapKey> set2 = one.getTheManys().keySet();
72+
assertThat( set1, is( equalTo( set2 ) ) );
73+
assertThat( set1, is( not( sameInstance( set2 ) ) ) );
74+
75+
List<TheMany> list1 = one.getTheManyList().subList( 0, 1 );
76+
List<TheMany> list2 = one.getTheManyList().subList( 0, 1 );
77+
assertThat( list1, is( equalTo( list2 ) ) );
78+
assertThat( list1, is( not( sameInstance( list2 ) ) ) );
79+
}
80+
);
81+
}
82+
83+
@Entity(name = "TheOne")
84+
public static class TheOne {
85+
private String id;
86+
private String aString;
87+
private Map<TheMapKey, TheMany> theManys = new HashMap<>();
88+
private List<TheMany> theManyList = new ArrayList<>();
89+
90+
TheOne() {
91+
}
92+
93+
public TheOne(String id) {
94+
this.id = id;
95+
}
96+
97+
@Id
98+
public String getId() {
99+
return this.id;
100+
}
101+
102+
public void setId(String id) {
103+
this.id = id;
104+
}
105+
106+
@OneToMany(mappedBy = "theMapKey.theOne")
107+
@MapKey(name = "theMapKey")
108+
public Map<TheMapKey, TheMany> getTheManys() {
109+
return theManys;
110+
}
111+
112+
public void setTheManys(Map<TheMapKey, TheMany> theManys) {
113+
this.theManys = theManys;
114+
}
115+
116+
@OneToMany
117+
@JoinTable
118+
public List<TheMany> getTheManyList() {
119+
return theManyList;
120+
}
121+
122+
public void setTheManyList(List<TheMany> theManyList) {
123+
this.theManyList = theManyList;
124+
}
125+
126+
public String getaString() {
127+
return aString;
128+
}
129+
130+
public void setaString(String aString) {
131+
this.aString = aString;
132+
}
133+
}
134+
135+
@Embeddable
136+
public static class TheMapKey implements Serializable {
137+
private TheOne theOne;
138+
private int anInt;
139+
140+
TheMapKey() {
141+
}
142+
143+
public TheMapKey(TheOne theOne) {
144+
this.theOne = theOne;
145+
}
146+
147+
@ManyToOne
148+
public TheOne getTheOne() {
149+
return theOne;
150+
}
151+
152+
public void setTheOne(TheOne theOne) {
153+
this.theOne = theOne;
154+
}
155+
156+
public int getAnInt() {
157+
return anInt;
158+
}
159+
160+
public void setAnInt(int anInt) {
161+
this.anInt = anInt;
162+
}
163+
}
164+
165+
@Entity(name = "TheMany")
166+
public static class TheMany {
167+
private TheMapKey theMapKey;
168+
private String aString;
169+
170+
TheMany() {
171+
}
172+
173+
public TheMany(TheMapKey theMapKey) {
174+
this.theMapKey = theMapKey;
175+
}
176+
177+
@EmbeddedId
178+
public TheMapKey getTheMapKey() {
179+
return theMapKey;
180+
}
181+
182+
public void setTheMapKey(TheMapKey theMapKey) {
183+
this.theMapKey = theMapKey;
184+
}
185+
186+
public String getaString() {
187+
return aString;
188+
}
189+
190+
public void setaString(String aString) {
191+
this.aString = aString;
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)