我已经开发了一个测试项目来重现此问题。
这是一个项目结构:
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>value-updated-after-fail-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
Persone.java 档案:
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@RequiredArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
@Column(nullable = false)
@NonNull
String name;
}
PersonRepository.java 档案:
@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {}
PersonService.java 档案:
@Component
public class PersonService {
private final PersonRepository repository;
public PersonService(PersonRepository repository) {
this.repository = repository;
}
@Transactional
public Person create(String name) {
return repository.save(new Person(name));
}
@Transactional
public Person save(Person person) {
if(StringUtils.isBlank(person.getName())) {
throw new RuntimeException();
}
Person personFromDB = getById(person.getId());
personFromDB.setName(person.getName());
return repository.save(personFromDB);
}
@Transactional
public Person getById(Long id) {
return repository.findById(id)
.orElseThrow(NullPointerException::new);
}
@Transactional
public void deleteAll() {
repository.deleteAll();
}
}
application.properties 档案:
spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
TestApplicationConfiguration.java 档案
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableJpaRepositories
@EntityScan("net.example.model")
@ComponentScan(basePackages = "net.example")
public class TestApplicationConfiguration {}
PersonServiceTest.java 档案:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class PersonServiceTest {
@Autowired
private PersonService service;
@AfterEach
void tearDownEach() {
service.deleteAll();
}
@Test
void rename() {
String expected = "name";
Person person = service.create(expected);
Person personFromDB = service.getById(person.getId());
personFromDB.setName("");
assertThrows(RuntimeException.class, () -> service.save(personFromDB));
assertEquals(expected, service.getById(personFromDB.getId()).getName());
}
}
**问题:**最后一个声明失败
org.opentest4j.AssertionFailedError:
Expected :name
Actual :
"我已经试过补救了“
1.我尝试删除PersonService#getById方法的@Transactional
注解,以避免该高速缓存中获取实体。-这没有解决问题
1.我尝试将spring.cache.type=none
添加到application.properties
文件以禁用该高速缓存。-这没有解决问题
"为什么我觉得该高速缓存“
调试时,我发现PersonService#getById()
方法并不返回实际数据,但该方法返回了一个标题已更改的缓存对象。
也许我没有正确地开发测试,也许我应该改变保存更改数据的方法。
请分享最佳实践和文章,以便更好地了解如何更新数据以及如何正确配置和编写Sping Boot 应用程序的测试。
1条答案
按热度按时间2izufjch1#
非常感谢Andrey B. Panfilov的评论。
我研究了
@Transactional
和Hibernate的一级缓存。实际上,用@DataJpaTest
注解的类中的每个测试方法调用都会创建、运行和回滚一个事务。每个事务都会创建和关闭Hibernate会话。正如我们所知,一级缓存一直存在到会话关闭。这就是为什么它也被称为会话缓存的原因。你可以在下面的截图中看到证据:
在第一个屏幕截图中,您可以看到
SpringExtension
(在@DataJpaTest
注解中定义)在调用每个测试之前打开一个新会话。在第二个屏幕截图中,您可以看到
SpringExtension
在调用每个测试后关闭会话。我决定覆盖默认的事务传播:
@Transactional(propagation = Propagation.NEVER)
调用方法时不创建事务,如果在现有事务中调用方法,则抛出异常帮助我的链接:
1.数据存取