我正在创建一个测试并基本上在 @Transactional
方法中执行不同的事务。
我添加了一个 Project
,然后向其添加了一个 Task
,最后将从 DB 中再次获取该项目以测试它是否已保存任务。
请注意,我展示的案例是一个单元测试,但我感兴趣的是修复事务性方法,而不是测试本身,因为我过去在“生产代码”中已经有了这个。
模型类:
@Entity
@Table(name = "Task")
public class Task {
@Id
@SequenceGenerator(name = "TaskSeq", sequenceName = "TaskSeq", initialValue = 100)
@GeneratedValue(generator = "TaskSeq")
private Long id;
@Column(nullable = false)
private String name;
private String description;
private LocalDateTime inZ;
private LocalDateTime outZ;
private boolean completed;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name = "projectId")
private Project project;
}
@Entity
@Table(name = "Project")
public class Project {
@Id
@SequenceGenerator(name = "ProjectSeq", sequenceName = "ProjectSeq", initialValue = 100)
@GeneratedValue(generator = "ProjectSeq")
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "project", cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<Task> tasks;
}
服务类:
@Service
public class ProjectServiceImpl implements ProjectService {
private final ProjectRepository projectRepository;
@Autowired
public ProjectServiceImpl(ProjectRepository projectRepository) {
this.projectRepository = projectRepository;
}
@Override
public Project save(Project project) {
return projectRepository.save(project);
}
@Override
public List<Project> findAll() {
return projectRepository.findAll();
}
}
@Service
public class TaskServiceImpl implements TaskService {
private TaskRepository taskRepository;
private ProjectRepository projectRepository;
@Autowired
public TaskServiceImpl(TaskRepository taskRepository, ProjectRepository projectRepository) {
this.taskRepository = taskRepository;
this.projectRepository = projectRepository;
}
@Override
@Transactional
public Task addTaskToProject(Long id, Task task) {
Project project = projectRepository.findById(id).orElseThrow(() -> new RuntimeException());
task.setProject(project);
return taskRepository.save(task);
}
}
我正在尝试使用事务方法的类:
public class TaskServiceTest extends JavaExampleApplicationTests {
@Autowired
private ProjectService projectService;
@Autowired
private TaskService taskService;
@Test
// @Transactional
public void canInsertTaskToProject() {
Project project = new Project();
project.setName("create company");
project = projectService.save(project);
Task task = new Task();
task.setName("Check how many people we need to hire");
task = taskService.addTaskToProject(project.getId(), task);
assertTrue(task.getId() > 0);
List<Project> projects = projectService.findAll();
assertEquals(1, projects.size());
assertEquals(1, projects.get(0).getTasks().size());
assertEquals(task.getId(), projects.get(0).getTasks().get(0).getId());
}
}
如果我将 @Transactional(REQUIRES_NEW)
添加到服务中的方法,它将起作用,但我不希望它好像在真实事务中调用此方法我希望它相应地回滚。另外我想避免使用太多 REQUIRES_NEW 以避免将来出现问题
如果我从测试方法中删除 @Transactional
,当我在最后两行测试任务列表的大小时它不会工作,因为它们是惰性的。
如果我在测试方法上添加 @Transactional
,它会在最后两行抛出 NullPointerException
,就像我执行 projectService.findAll( )
任务还没有提交
让它发挥作用的最佳方法是什么?我认为在 @Transactional
中,当我使用来自 db 的另一个命令时,它会获得尚未提交的最新更新..
提前致谢!
更新:添加了我从测试中删除 @Transactional
的原因
最佳答案
从根本上说,这是一个设计问题。在测试代码中,您正在进行更改,然后验证是否进行了这些更改。这给我们带来了 @Transactional
与否的问题。
使用@Transactional
,您最终会遇到这样一种情况,即发生了更改,但它们尚未刷新或提交,因此您无法在同一事务中看到更改。在这种情况下,您需要刷新结果,和/或重新考虑您的事务边界。
如果没有 @Transactional
,单个事务可以正常工作,但由于您不在事务中,因此无法初始化延迟加载的实体。为此,您的选择是以急切初始化这些值的方式加载值,或者以不需要初始化的方式加载值。这两者都可能涉及您存储库中的自定义查询。
如果没有看到实际的代码库,就不可能说出最佳方法。在这种情况下,使用 saveAndFlush()
可能会解决问题,但这并不是一个通用的解决方案。
关于Java Spring Hibernate——不同事务之间的@Transactional,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57869971/