Felipe S.
Felipe S.

Reputation: 1723

Spring Hibernate - @Transactional between different transactions

I'm creating a test and basically doing different transactions inside a @Transactional method.

I add a Project, then add a Task to it, and last will fetch the project again from DB to test it has the task saved.

Please note the case I'm showing is a unit test but I'm more interesting in fixing the transactional methods and not the test itself as I already had this in the past in "production code".

Model Classes:

@Entity
@Table(name = "Task")
data class Task(
        @Id
        @SequenceGenerator(name = "TaskSeq", sequenceName = "TaskSeq", initialValue = 100)
        @GeneratedValue(generator = "TaskSeq")
        val id: Long = 0,

        @Column(nullable = false)
        val name: String,

        val description: String,

        val inZ: LocalDateTime = LocalDateTime.now(),
        var outZ: LocalDateTime = JpaConstants.MAX_DATETIME,
        var completed: Boolean = false,

        @ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH])
        @JoinColumn(name = "projectId")
        var project: Project? = null
) {
}

@Entity
@Table(name = "Project")
data class Project(
        @Id
        @SequenceGenerator(name = "ProjectSeq", sequenceName = "ProjectSeq", initialValue = 100)
        @GeneratedValue(generator = "ProjectSeq")
        val id: Long = 0,

        @Column(nullable = false)
        var name: String,

        @OneToMany(mappedBy = "project", cascade = [CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH])
        val tasks: MutableList<Task> = Lists.mutable.empty()
) {

}

Service Classes:

@Service
class ProjectServiceImpl(private val projectRepository: ProjectRepository) : ProjectService {

    override fun save(project: Project): Project {
        return projectRepository.save(project)
    }

}


@Service
class TaskServiceImpl(private val taskRepository: TaskRepository, private val projectRepository: ProjectRepository) : TaskService {


    override fun save(task: Task): Task {
        return taskRepository.save(task)
    }

    override fun addTaskToProject(projectId: Long, task: Task): Task {
        val project = projectRepository.findById(projectId).orElseThrow { RecordNotFoundException("Couldn't find project with id {$projectId}") }
        task.project = project

        return save(task)
    }
}

The class I'm trying to use the transactional method:

class TaskServiceImplTest : TaskApplicationTests() {

@Autowired
private lateinit var taskService: TaskService

@Autowired
private lateinit var taskRepository: TaskRepository

@Autowired
private lateinit var projectService: ProjectService


@Test
@Transactional
fun canInsertTaskToProject() {
    val project = projectService.save(Project(name = "Conquer Paris"))
    var task = Task(name = "Check how many people we need to hire", description = "")
    task = taskService.addTaskToProject(project.id, task)

    assertTrue(task.id > 0)
    val projects = projectService.findAll()
    assertEquals(1, projects.size())
    assertEquals(1, projects[0].tasks.size)
    assertEquals(task.id, projects[0].tasks[0].id)
}

If I add a @Transactional(REQUIRES_NEW) to the methods in the service it will work, but I don't want it as if this method is called inside a real transaction I want it to be rolled back accordingly. Also I'd like to avoid using too many REQUIRES_NEW to avoid future problems

If I remove the @Transactional from the test method, it won't work when I test the size of the task list on last two lines as they are lazy.

What is the best way to make it work ? I thought that inside a @Transactional when I used another command from db it would get the latest updates that were not committed yet..

If needed, code in Java is fine too :)

Thanks in advance!

Upvotes: 0

Views: 53

Answers (1)

Jonathan JOhx
Jonathan JOhx

Reputation: 5968

Based on your scenarios, you can use @TestEntityManagerso that each test can be managed in transaction context. This example can help you,

https://grokonez.com/testing/datajpatest-with-spring-boot

Upvotes: 1

Related Questions