Skip to content

Commit 1d89b2b

Browse files
committed
[hibernate#2006] Add test for UnexpectedAccessToTheDatabase error when merging a detached entity with a ToMany association
1 parent 19d5393 commit 1d89b2b

File tree

3 files changed

+579
-0
lines changed

3 files changed

+579
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive;
7+
8+
import java.util.Collection;
9+
import java.util.List;
10+
import java.util.Objects;
11+
12+
import org.junit.jupiter.api.BeforeEach;
13+
import org.junit.jupiter.api.Test;
14+
15+
import io.vertx.junit5.Timeout;
16+
import io.vertx.junit5.VertxTestContext;
17+
import jakarta.persistence.Entity;
18+
import jakarta.persistence.FetchType;
19+
import jakarta.persistence.Id;
20+
import jakarta.persistence.OneToMany;
21+
import jakarta.persistence.Table;
22+
23+
import static java.util.concurrent.TimeUnit.MINUTES;
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.fail;
26+
27+
@Timeout(value = 2, timeUnit = MINUTES)
28+
public class OneToManyArrayMergeTest extends BaseReactiveTest {
29+
30+
private final static Long USER_ID = 1L;
31+
private final static Long ADMIN_ROLE_ID = 2L;
32+
private final static Long USER_ROLE_ID = 3L;
33+
private final static String UPDATED_FIRSTNAME = "UPDATED FIRSTNAME";
34+
private final static String UPDATED_LASTNAME = "UPDATED LASTNAME";
35+
36+
@Override
37+
protected Collection<Class<?>> annotatedEntities() {
38+
return List.of( User.class, Role.class );
39+
}
40+
41+
@BeforeEach
42+
public void populateDb(VertxTestContext context) {
43+
Role adminRole = new Role( ADMIN_ROLE_ID, "admin" );
44+
Role userRole = new Role( USER_ROLE_ID, "user" );
45+
User user = new User( USER_ID, "first", "last", adminRole );
46+
test(
47+
context, getMutinySessionFactory()
48+
.withTransaction( s -> s.persistAll( user, adminRole, userRole ) )
49+
);
50+
}
51+
52+
@Test
53+
public void testMerge(VertxTestContext context) {
54+
test(
55+
context, getMutinySessionFactory()
56+
.withTransaction( s -> s.find( User.class, USER_ID ) )
57+
.chain( user -> getMutinySessionFactory()
58+
.withTransaction( s -> s
59+
.createQuery( "FROM Role", Role.class )
60+
.getResultList() )
61+
.map( roles -> {
62+
user.addAll( roles );
63+
user.setFirstname( UPDATED_FIRSTNAME );
64+
user.setLastname( UPDATED_LASTNAME );
65+
return user;
66+
} )
67+
)
68+
.chain( user -> {
69+
assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME );
70+
assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME );
71+
assertThat( user.getRoles() ).hasSize( 2 );
72+
return getMutinySessionFactory()
73+
.withTransaction( s -> s.merge( user ) );
74+
}
75+
)
76+
.onFailure().invoke( throwable -> fail( throwable ) )
77+
.chain( v -> getMutinySessionFactory()
78+
.withTransaction( s -> s.find( User.class, USER_ID ) )
79+
)
80+
.invoke( user -> {
81+
Role adminRole = new Role( ADMIN_ROLE_ID, "admin" );
82+
Role userRole = new Role( USER_ROLE_ID, "user" );
83+
assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME );
84+
assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME );
85+
assertThat( user.getRoles() ).containsExactlyInAnyOrder(
86+
adminRole,
87+
userRole
88+
);
89+
}
90+
)
91+
);
92+
}
93+
94+
@Entity(name = "User")
95+
@Table(name = "USER_TABLE")
96+
public static class User {
97+
98+
@Id
99+
private Long id;
100+
101+
private String firstname;
102+
103+
private String lastname;
104+
105+
@OneToMany(fetch = FetchType.EAGER)
106+
private Role[] roles;
107+
108+
public User() {
109+
}
110+
111+
public User(Long id, String firstname, String lastname, Role... roles) {
112+
this.id = id;
113+
this.firstname = firstname;
114+
this.lastname = lastname;
115+
this.roles = new Role[roles.length];
116+
for ( int i = 0; i < roles.length; i++ ) {
117+
this.roles[i] = roles[i];
118+
}
119+
}
120+
121+
public Long getId() {
122+
return id;
123+
}
124+
125+
public String getFirstname() {
126+
return firstname;
127+
}
128+
129+
public void setFirstname(String firstname) {
130+
this.firstname = firstname;
131+
}
132+
133+
public String getLastname() {
134+
return lastname;
135+
}
136+
137+
public void setLastname(String lastname) {
138+
this.lastname = lastname;
139+
}
140+
141+
public Role[] getRoles() {
142+
return roles;
143+
}
144+
145+
public void addAll(List<Role> roles) {
146+
this.roles = new Role[roles.size()];
147+
for ( int i = 0; i < roles.size(); i++ ) {
148+
this.roles[i] = roles.get( i );
149+
}
150+
}
151+
}
152+
153+
@Entity(name = "Role")
154+
@Table(name = "ROLE_TABLE")
155+
public static class Role {
156+
157+
@Id
158+
private Long id;
159+
private String code;
160+
161+
public Role() {
162+
}
163+
164+
public Role(Long id, String code) {
165+
this.id = id;
166+
this.code = code;
167+
}
168+
169+
public Object getId() {
170+
return id;
171+
}
172+
173+
@Override
174+
public boolean equals(Object o) {
175+
if ( o == null || getClass() != o.getClass() ) {
176+
return false;
177+
}
178+
Role role = (Role) o;
179+
return Objects.equals( id, role.id ) && Objects.equals( code, role.code );
180+
}
181+
182+
@Override
183+
public int hashCode() {
184+
return Objects.hash( id, code );
185+
}
186+
187+
@Override
188+
public String toString() {
189+
return "Role{" + code + '}';
190+
}
191+
}
192+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive;
7+
8+
import java.util.Collection;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.Objects;
13+
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.junit.jupiter.api.Test;
16+
17+
import io.vertx.junit5.Timeout;
18+
import io.vertx.junit5.VertxTestContext;
19+
import jakarta.persistence.Entity;
20+
import jakarta.persistence.FetchType;
21+
import jakarta.persistence.Id;
22+
import jakarta.persistence.OneToMany;
23+
import jakarta.persistence.Table;
24+
25+
import static java.util.concurrent.TimeUnit.MINUTES;
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.assertj.core.api.Assertions.fail;
28+
29+
@Timeout(value = 2, timeUnit = MINUTES)
30+
public class OneToManyMapMergeTest extends BaseReactiveTest {
31+
32+
private final static Long USER_ID = 1L;
33+
private final static Long ADMIN_ROLE_ID = 2L;
34+
private final static Long USER_ROLE_ID = 3L;
35+
private final static String UPDATED_FIRSTNAME = "UPDATED FIRSTNAME";
36+
private final static String UPDATED_LASTNAME = "UPDATED LASTNAME";
37+
38+
@Override
39+
protected Collection<Class<?>> annotatedEntities() {
40+
return List.of( User.class, Role.class );
41+
}
42+
43+
@BeforeEach
44+
public void populateDb(VertxTestContext context) {
45+
Role adminRole = new Role( ADMIN_ROLE_ID, "admin" );
46+
Role userRole = new Role( USER_ROLE_ID, "user" );
47+
User user = new User( USER_ID, "first", "last", adminRole );
48+
test(
49+
context, getMutinySessionFactory()
50+
.withTransaction( s -> s.persistAll( user, adminRole, userRole ) )
51+
);
52+
}
53+
54+
@Test
55+
public void testMerge(VertxTestContext context) {
56+
test(
57+
context, getMutinySessionFactory()
58+
.withTransaction( s -> s.find( User.class, USER_ID ) )
59+
.chain( user -> getMutinySessionFactory()
60+
.withTransaction( s -> s
61+
.createQuery( "FROM Role", Role.class )
62+
.getResultList() )
63+
.map( roles -> {
64+
user.addAll( roles );
65+
user.setFirstname( UPDATED_FIRSTNAME );
66+
user.setLastname( UPDATED_LASTNAME );
67+
return user;
68+
} )
69+
)
70+
.chain( user -> {
71+
assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME );
72+
assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME );
73+
assertThat( user.getRoles() ).hasSize( 2 );
74+
return getMutinySessionFactory()
75+
.withTransaction( s -> s.merge( user ) );
76+
}
77+
)
78+
.onFailure().invoke( throwable -> fail( throwable ) )
79+
.chain( v -> getMutinySessionFactory()
80+
.withTransaction( s -> s.find( User.class, USER_ID ) )
81+
)
82+
.invoke( user -> {
83+
Role adminRole = new Role( ADMIN_ROLE_ID, "admin" );
84+
Role userRole = new Role( USER_ROLE_ID, "user" );
85+
assertThat( user.getFirstname() ).isEqualTo( UPDATED_FIRSTNAME );
86+
assertThat( user.getLastname() ).isEqualTo( UPDATED_LASTNAME );
87+
assertThat( user.getRoles() ).containsEntry(
88+
adminRole.getCode(),
89+
adminRole
90+
);
91+
assertThat( user.getRoles() ).containsEntry(
92+
userRole.getCode(),
93+
userRole
94+
);
95+
}
96+
)
97+
);
98+
}
99+
100+
@Entity(name = "User")
101+
@Table(name = "USER_TABLE")
102+
public static class User {
103+
104+
@Id
105+
private Long id;
106+
107+
private String firstname;
108+
109+
private String lastname;
110+
111+
@OneToMany(fetch = FetchType.EAGER)
112+
private Map<String, Role> roles = new HashMap();
113+
114+
public User() {
115+
}
116+
117+
public User(Long id, String firstname, String lastname, Role... roles) {
118+
this.id = id;
119+
this.firstname = firstname;
120+
this.lastname = lastname;
121+
for ( Role role : roles ) {
122+
this.roles.put( role.getCode(), role );
123+
}
124+
}
125+
126+
public Long getId() {
127+
return id;
128+
}
129+
130+
public String getFirstname() {
131+
return firstname;
132+
}
133+
134+
public void setFirstname(String firstname) {
135+
this.firstname = firstname;
136+
}
137+
138+
public String getLastname() {
139+
return lastname;
140+
}
141+
142+
public void setLastname(String lastname) {
143+
this.lastname = lastname;
144+
}
145+
146+
public Map<String, Role> getRoles() {
147+
return roles;
148+
}
149+
150+
public void addAll(List<Role> roles) {
151+
this.roles.clear();
152+
for ( Role role : roles ) {
153+
this.roles.put( role.getCode(), role );
154+
}
155+
}
156+
}
157+
158+
@Entity(name = "Role")
159+
@Table(name = "ROLE_TABLE")
160+
public static class Role {
161+
162+
@Id
163+
private Long id;
164+
private String code;
165+
166+
public Role() {
167+
}
168+
169+
public Role(Long id, String code) {
170+
this.id = id;
171+
this.code = code;
172+
}
173+
174+
public Object getId() {
175+
return id;
176+
}
177+
178+
public String getCode() {
179+
return code;
180+
}
181+
182+
@Override
183+
public boolean equals(Object o) {
184+
if ( o == null || getClass() != o.getClass() ) {
185+
return false;
186+
}
187+
Role role = (Role) o;
188+
return Objects.equals( id, role.id ) && Objects.equals( code, role.code );
189+
}
190+
191+
@Override
192+
public int hashCode() {
193+
return Objects.hash( id, code );
194+
}
195+
196+
@Override
197+
public String toString() {
198+
return "Role{" + code + '}';
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)