Skip to content

Commit 1d302bf

Browse files
committed
Introduce tests for gh-28228
1 parent 4b150fd commit 1d302bf

File tree

6 files changed

+437
-1
lines changed

6 files changed

+437
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.junit.jupiter.orm;
18+
19+
import java.util.List;
20+
21+
import javax.persistence.EntityManager;
22+
import javax.persistence.EntityManagerFactory;
23+
import javax.persistence.PersistenceContext;
24+
import javax.sql.DataSource;
25+
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
29+
import org.springframework.beans.factory.annotation.Autowired;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.jdbc.core.JdbcTemplate;
33+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
34+
import org.springframework.orm.jpa.JpaTransactionManager;
35+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
36+
import org.springframework.orm.jpa.vendor.Database;
37+
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
38+
import org.springframework.test.context.jdbc.Sql;
39+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
40+
import org.springframework.test.context.junit.jupiter.orm.domain.JpaPersonRepository;
41+
import org.springframework.test.context.junit.jupiter.orm.domain.Person;
42+
import org.springframework.test.context.junit.jupiter.orm.domain.PersonListener;
43+
import org.springframework.test.context.junit.jupiter.orm.domain.PersonRepository;
44+
import org.springframework.transaction.annotation.EnableTransactionManagement;
45+
import org.springframework.transaction.annotation.Transactional;
46+
47+
import static org.assertj.core.api.Assertions.assertThat;
48+
49+
/**
50+
* Transactional tests for JPA entity listener support (a.k.a. lifecycle callback
51+
* methods).
52+
*
53+
* @author Sam Brannen
54+
* @since 5.3.18
55+
* @see <a href="https://github.com/spring-projects/spring-framework/issues/28228">issue gh-28228</a>
56+
* @see org.springframework.test.context.junit4.orm.HibernateSessionFlushingTests
57+
*/
58+
@SpringJUnitConfig
59+
@Transactional
60+
@Sql(statements = "insert into person(id, name) values(0, 'Jane')")
61+
class JpaEntityListenerTests {
62+
63+
@PersistenceContext
64+
EntityManager entityManager;
65+
66+
@Autowired
67+
JdbcTemplate jdbcTemplate;
68+
69+
@Autowired
70+
PersonRepository repo;
71+
72+
73+
@BeforeEach
74+
void setUp() {
75+
assertPeople("Jane");
76+
PersonListener.methodsInvoked.clear();
77+
}
78+
79+
@Test
80+
void find() {
81+
Person jane = repo.findByName("Jane");
82+
assertCallbacks("@PostLoad: Jane");
83+
84+
// Does not cause an additional @PostLoad
85+
repo.findById(jane.getId());
86+
assertCallbacks("@PostLoad: Jane");
87+
88+
// Clear to cause a new @PostLoad
89+
entityManager.clear();
90+
repo.findById(jane.getId());
91+
assertCallbacks("@PostLoad: Jane", "@PostLoad: Jane");
92+
}
93+
94+
@Test
95+
void save() {
96+
Person john = repo.save(new Person("John"));
97+
assertCallbacks("@PrePersist: John");
98+
99+
// Flush to cause a @PostPersist
100+
entityManager.flush();
101+
assertPeople("Jane", "John");
102+
assertCallbacks("@PrePersist: John", "@PostPersist: John");
103+
104+
// Does not cause a @PostLoad
105+
repo.findById(john.getId());
106+
assertCallbacks("@PrePersist: John", "@PostPersist: John");
107+
108+
// Clear to cause a @PostLoad
109+
entityManager.clear();
110+
repo.findById(john.getId());
111+
assertCallbacks("@PrePersist: John", "@PostPersist: John", "@PostLoad: John");
112+
}
113+
114+
@Test
115+
void update() {
116+
Person jane = repo.findByName("Jane");
117+
assertCallbacks("@PostLoad: Jane");
118+
119+
jane.setName("Jane Doe");
120+
// Does not cause a @PreUpdate or @PostUpdate
121+
repo.save(jane);
122+
assertCallbacks("@PostLoad: Jane");
123+
124+
// Flush to cause a @PreUpdate and @PostUpdate
125+
entityManager.flush();
126+
assertPeople("Jane Doe");
127+
assertCallbacks("@PostLoad: Jane", "@PreUpdate: Jane Doe", "@PostUpdate: Jane Doe");
128+
}
129+
130+
@Test
131+
void remove() {
132+
Person jane = repo.findByName("Jane");
133+
assertCallbacks("@PostLoad: Jane");
134+
135+
// Does not cause a @PostRemove
136+
repo.remove(jane);
137+
assertCallbacks("@PostLoad: Jane", "@PreRemove: Jane");
138+
139+
// Flush to cause a @PostRemove
140+
entityManager.flush();
141+
assertPeople();
142+
assertCallbacks("@PostLoad: Jane", "@PreRemove: Jane", "@PostRemove: Jane");
143+
}
144+
145+
private void assertCallbacks(String... callbacks) {
146+
assertThat(PersonListener.methodsInvoked).containsExactly(callbacks);
147+
}
148+
149+
private void assertPeople(String... expectedNames) {
150+
List<String> names = this.jdbcTemplate.queryForList("select name from person", String.class);
151+
if (expectedNames.length == 0) {
152+
assertThat(names).isEmpty();
153+
}
154+
else {
155+
assertThat(names).containsExactlyInAnyOrder(expectedNames);
156+
}
157+
}
158+
159+
160+
@Configuration(proxyBeanMethods = false)
161+
@EnableTransactionManagement
162+
static class Config {
163+
164+
@Bean
165+
PersonRepository personRepository() {
166+
return new JpaPersonRepository();
167+
}
168+
169+
@Bean
170+
DataSource dataSource() {
171+
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
172+
}
173+
174+
@Bean
175+
JdbcTemplate jdbcTemplate(DataSource dataSource) {
176+
return new JdbcTemplate(dataSource);
177+
}
178+
179+
@Bean
180+
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
181+
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
182+
emfb.setDataSource(dataSource);
183+
emfb.setPackagesToScan(Person.class.getPackage().getName());
184+
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
185+
hibernateJpaVendorAdapter.setGenerateDdl(true);
186+
hibernateJpaVendorAdapter.setDatabase(Database.HSQL);
187+
emfb.setJpaVendorAdapter(hibernateJpaVendorAdapter);
188+
return emfb;
189+
}
190+
191+
@Bean
192+
JpaTransactionManager transactionManager(EntityManagerFactory emf) {
193+
return new JpaTransactionManager(emf);
194+
}
195+
196+
}
197+
198+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.junit.jupiter.orm.domain;
18+
19+
import javax.persistence.EntityManager;
20+
import javax.persistence.PersistenceContext;
21+
22+
import org.springframework.stereotype.Repository;
23+
import org.springframework.transaction.annotation.Transactional;
24+
25+
/**
26+
* JPA based implementation of the {@link PersonRepository} API.
27+
*
28+
* @author Sam Brannen
29+
* @since 5.3.18
30+
*/
31+
@Transactional
32+
@Repository
33+
public class JpaPersonRepository implements PersonRepository {
34+
35+
@PersistenceContext
36+
private EntityManager entityManager;
37+
38+
@Override
39+
public Person findById(Long id) {
40+
return this.entityManager.find(Person.class, id);
41+
}
42+
43+
@Override
44+
public Person findByName(String name) {
45+
return this.entityManager.createQuery("from Person where name = :name", Person.class)
46+
.setParameter("name", name)
47+
.getSingleResult();
48+
}
49+
50+
@Override
51+
public Person save(Person person) {
52+
this.entityManager.persist(person);
53+
return person;
54+
}
55+
56+
@Override
57+
public void remove(Person person) {
58+
this.entityManager.remove(person);
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.junit.jupiter.orm.domain;
18+
19+
import javax.persistence.Entity;
20+
import javax.persistence.EntityListeners;
21+
import javax.persistence.GeneratedValue;
22+
import javax.persistence.GenerationType;
23+
import javax.persistence.Id;
24+
25+
/**
26+
* Person entity.
27+
*
28+
* @author Sam Brannen
29+
* @since 5.3.18
30+
*/
31+
@Entity
32+
@EntityListeners(PersonListener.class)
33+
public class Person {
34+
35+
@Id
36+
@GeneratedValue(strategy = GenerationType.AUTO)
37+
private Long id;
38+
39+
private String name;
40+
41+
42+
public Person() {
43+
}
44+
45+
public Person(String name) {
46+
this.name = name;
47+
}
48+
49+
public Long getId() {
50+
return this.id;
51+
}
52+
53+
protected void setId(Long id) {
54+
this.id = id;
55+
}
56+
57+
public String getName() {
58+
return this.name;
59+
}
60+
61+
public void setName(String name) {
62+
this.name = name;
63+
}
64+
65+
}

0 commit comments

Comments
 (0)