Java Spring Hibernate——不同事务之间的@Transactional

标签 java spring hibernate

我正在创建一个测试并基本上在 @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/

相关文章:

java - 将所有最后的元素拆分并存储到一个数组中

java - 如何合并两个具有相同值的对象?

swift - 向 Spring 服务器发出 post 请求时出现 400 错误

java - JPA 实现——哪一个最好用?

hibernate - Spring MVC 表单 :select Tag, 多选未正确绑定(bind)?

java - 在EntityManager中执行JoinTransaction出现异常

java - 如何在 Intellij IDEA 中使用 Junit 删除自动代码完成

java - Maven:在项目之间共享源

java - 通过守护进程作为独立运行 spring

java - 线程将随着时间的推移而更新,以尝试避免可能的内存泄漏