In the code below, the createPerson function, which isn't transactional, makes a call to createPersonTransactional, which is transactional.
IntelliJ highlights the call to createPersonTransactional
,saying that it's a transactional self invocation, and will not lead to a transaction at runtime.
Both functions are in a service that are called from a controller.
When the controller createPerson
function is called, the logs show a transaction being created anyway.
I always thought that function to function calls like this would hit the actual function, not the proxied function that spring creates which wraps the function with transactional code.
So is IntelliJ wrong in its warning ? I'm sure this used to be the case, but it doesn't appear to be anymore.
Spring Boot 3.2.2 BTW
@Service
class PersonService(val personRepository: PersonRepository) {
fun createPerson(personRequest: PersonRequest): Person {
return createPersonTransactional(personRequest)
}
@Transactional
fun createPersonTransactional(personRequest: PersonRequest): Person {
val person = Person(name = personRequest.name)
personRepository.save(person)
return person
}
}
interface PersonRepository : JpaRepository<Person, String>
@RestController
class PersonController(
private val personService: PersonService
) {
@PostMapping("/person", produces = [MediaType.APPLICATION_XML_VALUE])
fun createPerson(@RequestBody personRequest: PersonRequest): Person {
return personService.createPerson(personRequest)
}
}
Here are the logs showing the transaction taking place:
2024-02-09T09:38:32.084Z DEBUG 5102 --- [nio-8081-exec-3] o.s.web.servlet.DispatcherServlet : POST "/person", parameters={}
2024-02-09T09:38:32.084Z DEBUG 5102 --- [nio-8081-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.rosbug.PersonController#createPerson(PersonRequest)
2024-02-09T09:38:32.084Z DEBUG 5102 --- [nio-8081-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2024-02-09T09:38:32.152Z DEBUG 5102 --- [nio-8081-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [PersonRequest(name=bert)]
2024-02-09T09:38:32.178Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(1455667122<open>)] for JPA transaction
2024-02-09T09:38:32.178Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2024-02-09T09:38:32.182Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@65c57510]
2024-02-09T09:38:32.283Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
2024-02-09T09:38:32.283Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(1455667122<open>)]
2024-02-09T09:38:32.305Z DEBUG 5102 --- [nio-8081-exec-3] o.s.orm.jpa.JpaTransactionManager : Not closing pre-bound JPA EntityManager after transaction
2024-02-09T09:38:32.308Z DEBUG 5102 --- [nio-8081-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/xml', given [*/*] and supported [application/xml]
2024-02-09T09:38:32.320Z DEBUG 5102 --- [nio-8081-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Writing [com.example.rosbug.Person@3b4eb178]
2024-02-09T09:38:32.340Z DEBUG 5102 --- [nio-8081-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
2024-02-09T09:38:32.341Z DEBUG 5102 --- [nio-8081-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
I always thought that function to function calls like this would hit the actual function, not the proxied function that spring creates which wraps the function with transactional code.
I'd say that assumption is still correct. The @Transactional
on the createPersonTransactional
is still "ignored" when called from withing the same class - the transaction you see comes from the repository.
Please, provide more examples with logging the transaction details to confirm (and e.g. print some logs before and after calling each method). I think this will reveal that the method itself is not wrapped in transaction, but that the transaction you're seeing comes from PersonRepository.save()
. There are not details about PersonRepository, so I'm just guessing here - but I guess it extends SimpleJpaRepository
(from org.springframework.data.jpa.repository.support
) which has save
method annotated with @Transactional
.