diff --git a/common/spring-data/build.gradle.kts b/common/spring-data/build.gradle.kts new file mode 100644 index 000000000..38a716275 --- /dev/null +++ b/common/spring-data/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("org.orkg.gradle.kotlin-library") +} + +dependencies { + api("org.springframework:spring-core") + api("org.springframework:spring-tx") +} diff --git a/common/spring-data/src/main/kotlin/org/orkg/spring/data/annotations/SpringDataAnnotations.kt b/common/spring-data/src/main/kotlin/org/orkg/spring/data/annotations/SpringDataAnnotations.kt new file mode 100644 index 000000000..0477eecb5 --- /dev/null +++ b/common/spring-data/src/main/kotlin/org/orkg/spring/data/annotations/SpringDataAnnotations.kt @@ -0,0 +1,24 @@ +package org.orkg.spring.data.annotations + +import org.springframework.core.annotation.AliasFor +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional + +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Transactional(transactionManager = "jpaTransactionManager") +annotation class TransactionalOnJPA( + @get:AliasFor(annotation = Transactional::class, attribute = "readOnly") val readOnly: Boolean = false, + @get:AliasFor(annotation = Transactional::class, attribute = "propagation") val propagation: Propagation = Propagation.REQUIRED, + @get:AliasFor(annotation = Transactional::class, attribute = "isolation") val isolation: Isolation = Isolation.DEFAULT, +) + +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Transactional(transactionManager = "neo4jTransactionManager") +annotation class TransactionalOnNeo4j( + @get:AliasFor(annotation = Transactional::class, attribute = "readOnly") val readOnly: Boolean = false, + @get:AliasFor(annotation = Transactional::class, attribute = "propagation") val propagation: Propagation = Propagation.REQUIRED, + @get:AliasFor(annotation = Transactional::class, attribute = "isolation") val isolation: Isolation = Isolation.DEFAULT, +) diff --git a/community/community-adapter-output-spring-data-jpa/build.gradle.kts b/community/community-adapter-output-spring-data-jpa/build.gradle.kts index e3a9d7fcc..5a9f82097 100644 --- a/community/community-adapter-output-spring-data-jpa/build.gradle.kts +++ b/community/community-adapter-output-spring-data-jpa/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { api("org.springframework.data:spring-data-commons") api("org.springframework.data:spring-data-jpa") api("org.springframework:spring-context") - api("org.springframework:spring-tx") + api(project(":common:spring-data")) implementation(project(":common:string-utils")) api(project(":common:identifiers")) api(project(":community:community-core-model")) @@ -35,6 +35,8 @@ testing { runtimeOnly(project(":migrations:liquibase")) runtimeOnly("org.liquibase:liquibase-core") runtimeOnly("org.postgresql:postgresql") + implementation("org.springframework.boot:spring-boot-test") + implementation("org.springframework:spring-orm") implementation("org.springframework.boot:spring-boot-test-autoconfigure") implementation("org.springframework:spring-test") implementation(testFixtures(project(":common:testing"))) diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/ContributorFromUserAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/ContributorFromUserAdapter.kt index baba9a72e..e6cd1c8df 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/ContributorFromUserAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/ContributorFromUserAdapter.kt @@ -14,12 +14,14 @@ import org.orkg.eventbus.EventBus import org.orkg.eventbus.Listener import org.orkg.eventbus.events.DisplayNameUpdated import org.orkg.eventbus.events.UserRegistered +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.beans.factory.InitializingBean import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Component @Component +@TransactionalOnJPA class ContributorFromUserAdapter( private val eventBus: EventBus, private val postgresContributorRepository: PostgresContributorRepository, diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresConferenceSeriesAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresConferenceSeriesAdapter.kt index 631375d28..8542a502a 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresConferenceSeriesAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresConferenceSeriesAdapter.kt @@ -8,11 +8,13 @@ import org.orkg.community.adapter.output.jpa.internal.PostgresOrganizationReposi import org.orkg.community.domain.ConferenceSeries import org.orkg.community.domain.ConferenceSeriesId import org.orkg.community.output.ConferenceSeriesRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Component @Component +@TransactionalOnJPA class SpringJpaPostgresConferenceSeriesAdapter( private val postgresConferenceSeriesRepository: PostgresConferenceSeriesRepository, private val postgresOrganizationRepository: PostgresOrganizationRepository, diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresKeycloakEventStateAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresKeycloakEventStateAdapter.kt index 94bbf6365..bc5d9e890 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresKeycloakEventStateAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresKeycloakEventStateAdapter.kt @@ -4,11 +4,11 @@ import org.orkg.community.adapter.output.jpa.internal.KeycloakEventStateEntity import org.orkg.community.adapter.output.jpa.internal.PostgresKeycloakEventStateRepository import org.orkg.community.domain.EventType import org.orkg.community.output.KeycloakEventStateRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional @Component -@Transactional +@TransactionalOnJPA class SpringJpaPostgresKeycloakEventStateAdapter( private val postgresRepository: PostgresKeycloakEventStateRepository, ) : KeycloakEventStateRepository { diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryAdapter.kt index 0874960ad..f4a763e4a 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryAdapter.kt @@ -14,11 +14,13 @@ import org.orkg.community.adapter.output.jpa.internal.toObservatory import org.orkg.community.domain.Contributor import org.orkg.community.domain.Observatory import org.orkg.community.output.ObservatoryRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Component @Component +@TransactionalOnJPA class SpringJpaPostgresObservatoryAdapter( private val postgresRepository: PostgresObservatoryRepository, private val postgresOrganizationRepository: PostgresOrganizationRepository, diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryFilterAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryFilterAdapter.kt index 1905feca6..46d10b640 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryFilterAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresObservatoryFilterAdapter.kt @@ -8,11 +8,13 @@ import org.orkg.community.output.ObservatoryFilterRepository import java.util.* import org.orkg.common.ObservatoryId import org.orkg.community.domain.ObservatoryFilterId +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Component @Component +@TransactionalOnJPA class SpringJpaPostgresObservatoryFilterAdapter( private val postgresRepository: PostgresObservatoryFilterRepository ) : ObservatoryFilterRepository { diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresOrganizationAdapter.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresOrganizationAdapter.kt index 7fbb46a5a..2ce5f0865 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresOrganizationAdapter.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/SpringJpaPostgresOrganizationAdapter.kt @@ -12,11 +12,13 @@ import org.orkg.community.domain.Contributor import org.orkg.community.domain.Organization import org.orkg.community.domain.OrganizationType import org.orkg.community.output.OrganizationRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Component @Component +@TransactionalOnJPA class SpringJpaPostgresOrganizationAdapter( private val postgresOrganizationRepository: PostgresOrganizationRepository, private val postgresObservatoryRepository: PostgresObservatoryRepository, diff --git a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaConfiguration.kt b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaConfiguration.kt index a231fdbf2..eff01944c 100644 --- a/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaConfiguration.kt +++ b/community/community-adapter-output-spring-data-jpa/src/main/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaConfiguration.kt @@ -1,12 +1,10 @@ package org.orkg.community.adapter.output.jpa.configuration import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.data.jpa.repository.config.EnableJpaRepositories @Configuration -@EnableJpaRepositories("org.orkg.community.adapter.output.jpa.internal") +@EnableJpaRepositories("org.orkg.community.adapter.output.jpa.internal", transactionManagerRef = "jpaTransactionManager") @EntityScan("org.orkg.community.adapter.output.jpa.internal") -@ComponentScan(basePackages = ["org.orkg.community.adapter.output.jpa"]) class CommunityJpaConfiguration diff --git a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/ContributorAdapterIdempotencyTest.kt b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/ContributorAdapterIdempotencyTest.kt index d69325e67..983e10917 100644 --- a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/ContributorAdapterIdempotencyTest.kt +++ b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/ContributorAdapterIdempotencyTest.kt @@ -5,6 +5,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.orkg.common.testing.fixtures.fixedClock import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaConfiguration +import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaTestConfiguration import org.orkg.eventbus.ReallySimpleEventBus import org.orkg.eventbus.events.UserRegistered import org.orkg.testing.MockUserId @@ -19,10 +20,11 @@ import org.springframework.test.context.TestConstructor @ContextConfiguration( classes = [ CommunityJpaConfiguration::class, + CommunityJpaTestConfiguration::class, ContributorFromUserAdapter::class, ReallySimpleEventBus::class, ], - initializers = [PostgresContainerInitializer::class] + initializers = [PostgresContainerInitializer::class], ) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) diff --git a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryContractTest.kt b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryContractTest.kt index 2669f1b7d..da575b542 100644 --- a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryContractTest.kt +++ b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryContractTest.kt @@ -1,6 +1,7 @@ package org.orkg.community.adapter.output.jpa import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaConfiguration +import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaTestConfiguration import org.orkg.community.output.ObservatoryRepository import org.orkg.community.output.OrganizationRepository import org.orkg.community.testing.fixtures.ObservatoryRepositoryContracts @@ -17,6 +18,7 @@ import org.springframework.test.context.TestConstructor classes = [ SpringJpaPostgresObservatoryAdapter::class, CommunityJpaConfiguration::class, + CommunityJpaTestConfiguration::class, ReallySimpleEventBus::class, ], initializers = [PostgresContainerInitializer::class] @@ -24,7 +26,6 @@ import org.springframework.test.context.TestConstructor @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) internal class PostgresObservatoryContractTest : ObservatoryRepositoryContracts { - @Autowired private lateinit var adapter: SpringJpaPostgresObservatoryAdapter diff --git a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryFilterContractTest.kt b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryFilterContractTest.kt index 83b004fc7..0e132b85e 100644 --- a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryFilterContractTest.kt +++ b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/PostgresObservatoryFilterContractTest.kt @@ -1,6 +1,7 @@ package org.orkg.community.adapter.output.jpa import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaConfiguration +import org.orkg.community.adapter.output.jpa.configuration.CommunityJpaTestConfiguration import org.orkg.community.output.ObservatoryFilterRepository import org.orkg.community.output.ObservatoryRepository import org.orkg.community.output.OrganizationRepository @@ -19,6 +20,7 @@ import org.springframework.test.context.TestConstructor classes = [ SpringJpaPostgresObservatoryFilterAdapter::class, CommunityJpaConfiguration::class, + CommunityJpaTestConfiguration::class, ReallySimpleEventBus::class, ], initializers = [PostgresContainerInitializer::class] @@ -26,7 +28,6 @@ import org.springframework.test.context.TestConstructor @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) internal class PostgresObservatoryFilterContractTest : ObservatoryFilterRepositoryContracts { - @Autowired private lateinit var adapter: SpringJpaPostgresObservatoryFilterAdapter diff --git a/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaTestConfiguration.kt b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaTestConfiguration.kt new file mode 100644 index 000000000..8f85b613e --- /dev/null +++ b/community/community-adapter-output-spring-data-jpa/src/test/kotlin/org/orkg/community/adapter/output/jpa/configuration/CommunityJpaTestConfiguration.kt @@ -0,0 +1,13 @@ +package org.orkg.community.adapter.output.jpa.configuration + +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.orm.jpa.JpaTransactionManager + +@TestConfiguration +@ComponentScan("org.orkg.community.adapter.output.jpa") +class CommunityJpaTestConfiguration { + @Bean + fun jpaTransactionManager(): JpaTransactionManager = JpaTransactionManager() +} diff --git a/community/community-core-services/build.gradle.kts b/community/community-core-services/build.gradle.kts index ec7566980..0ee87c911 100644 --- a/community/community-core-services/build.gradle.kts +++ b/community/community-core-services/build.gradle.kts @@ -8,7 +8,7 @@ plugins { dependencies { api("org.springframework.data:spring-data-commons") api("org.springframework:spring-context") - api("org.springframework:spring-tx") + api(project(":common:spring-data")) api(project(":common:identifiers")) api(project(":community:community-core-model")) api(project(":community:community-ports-input")) diff --git a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ConferenceSeriesService.kt b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ConferenceSeriesService.kt index e2aa3bd36..235f02622 100644 --- a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ConferenceSeriesService.kt +++ b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ConferenceSeriesService.kt @@ -5,13 +5,13 @@ import org.orkg.common.OrganizationId import org.orkg.community.input.ConferenceSeriesUseCases import org.orkg.community.output.ConferenceSeriesRepository import org.orkg.community.output.OrganizationRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnJPA class ConferenceSeriesService( private val postgresConferenceSeriesRepository: ConferenceSeriesRepository, private val postgresOrganizationRepository: OrganizationRepository diff --git a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ContributorService.kt b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ContributorService.kt index 45cd71f46..d2fc0a766 100644 --- a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ContributorService.kt +++ b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ContributorService.kt @@ -5,11 +5,11 @@ import org.orkg.common.ContributorId import org.orkg.community.input.ContributorUseCases import org.orkg.community.input.CreateContributorUseCase.CreateCommand import org.orkg.community.output.ContributorRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnJPA class ContributorService( private val repository: ContributorRepository, ) : ContributorUseCases { diff --git a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryFilterService.kt b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryFilterService.kt index 1c507624e..b54539c05 100644 --- a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryFilterService.kt +++ b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryFilterService.kt @@ -1,6 +1,5 @@ package org.orkg.community.domain -import org.orkg.community.output.ObservatoryFilterRepository import java.time.Clock import java.time.LocalDateTime import java.util.* @@ -8,18 +7,19 @@ import org.orkg.common.ObservatoryId import org.orkg.community.input.CreateObservatoryFilterUseCase import org.orkg.community.input.ObservatoryFilterUseCases import org.orkg.community.input.UpdateObservatoryFilterUseCase +import org.orkg.community.output.ObservatoryFilterRepository import org.orkg.community.output.ObservatoryRepository import org.orkg.graph.domain.ClassNotFound import org.orkg.graph.domain.PredicateNotFound import org.orkg.graph.output.ClassRepository import org.orkg.graph.output.PredicateRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnJPA class ObservatoryFilterService( private val repository: ObservatoryFilterRepository, private val observatoryRepository: ObservatoryRepository, diff --git a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryService.kt b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryService.kt index 788e91390..892f35196 100644 --- a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryService.kt +++ b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/ObservatoryService.kt @@ -14,13 +14,13 @@ import org.orkg.graph.domain.Classes import org.orkg.graph.domain.ResearchFieldNotFound import org.orkg.graph.domain.Resources import org.orkg.graph.output.ResourceRepository +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnJPA class ObservatoryService( private val postgresObservatoryRepository: ObservatoryRepository, private val postgresOrganizationRepository: OrganizationRepository, diff --git a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/OrganizationService.kt b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/OrganizationService.kt index 2f790a4f2..41110e980 100644 --- a/community/community-core-services/src/main/kotlin/org/orkg/community/domain/OrganizationService.kt +++ b/community/community-core-services/src/main/kotlin/org/orkg/community/domain/OrganizationService.kt @@ -10,11 +10,11 @@ import org.orkg.mediastorage.domain.Image import org.orkg.mediastorage.domain.ImageId import org.orkg.mediastorage.input.CreateImageUseCase import org.orkg.mediastorage.input.ImageUseCases +import org.orkg.spring.data.annotations.TransactionalOnJPA import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnJPA class OrganizationService( private val postgresOrganizationRepository: OrganizationRepository, private val imageService: ImageUseCases diff --git a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts index ce98afe20..c7a84c321 100644 --- a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts +++ b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts @@ -14,9 +14,7 @@ testing { runtimeOnly("org.springframework.boot:spring-boot-starter-test") runtimeOnly("org.springframework.boot:spring-boot-starter-data-neo4j") implementation("io.kotest:kotest-framework-api") - implementation("org.springframework.boot:spring-boot-test-autoconfigure") implementation("org.springframework:spring-beans") - implementation("org.springframework:spring-test") implementation("io.kotest:kotest-runner-junit5") runtimeOnly("eu.michael-simons.neo4j:neo4j-migrations-spring-boot-starter") implementation(project(":graph:graph-core-services")) diff --git a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/Extensions.kt b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/Extensions.kt index e9b809238..bd69d0ba3 100644 --- a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/Extensions.kt +++ b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/Extensions.kt @@ -21,7 +21,6 @@ import org.neo4j.cypherdsl.core.SymbolicName import org.neo4j.driver.Record import org.neo4j.driver.types.TypeSystem import org.orkg.common.ContributorId -import org.orkg.common.ThingId import org.orkg.contenttypes.domain.Certainty import org.orkg.contenttypes.domain.RosettaStoneStatement import org.orkg.contenttypes.domain.RosettaStoneStatementVersion diff --git a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/configuration/ContentTypesNeo4jConfiguration.kt b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/configuration/ContentTypesNeo4jConfiguration.kt index 1039991f2..2317517bd 100644 --- a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/configuration/ContentTypesNeo4jConfiguration.kt +++ b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/contenttypes/adapter/output/neo4j/configuration/ContentTypesNeo4jConfiguration.kt @@ -1,12 +1,10 @@ package org.orkg.contenttypes.adapter.output.neo4j.configuration import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories @Configuration -@EnableNeo4jRepositories("org.orkg.contenttypes.adapter.output.neo4j.internal") +@EnableNeo4jRepositories("org.orkg.contenttypes.adapter.output.neo4j.internal", transactionManagerRef = "neo4jTransactionManager") @EntityScan("org.orkg.contenttypes.adapter.output.neo4j.internal") -@ComponentScan(basePackages = ["org.orkg.contenttypes.adapter.output.neo4j.internal"]) class ContentTypesNeo4jConfiguration diff --git a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jContributionComparisonAdapterContractTest.kt b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jContributionComparisonAdapterContractTest.kt index 39adf84ca..8786b382d 100644 --- a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jContributionComparisonAdapterContractTest.kt +++ b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jContributionComparisonAdapterContractTest.kt @@ -15,17 +15,10 @@ import org.orkg.graph.output.LiteralRepository import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository -import org.orkg.testing.Neo4jContainerInitializer +import org.orkg.testing.annotations.Neo4jContainerUnitTest import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest -import org.springframework.cache.annotation.EnableCaching -import org.springframework.test.context.ContextConfiguration -@EnableAutoConfiguration -@DataNeo4jTest -@EnableCaching -@ContextConfiguration( +@Neo4jContainerUnitTest( classes = [ SpringDataNeo4jContributionComparisonAdapter::class, SpringDataNeo4jStatementAdapter::class, @@ -34,11 +27,8 @@ import org.springframework.test.context.ContextConfiguration SpringDataNeo4jResourceAdapter::class, SpringDataNeo4jPredicateAdapter::class, GraphNeo4jConfiguration::class, - ContentTypesNeo4jConfiguration::class + ContentTypesNeo4jConfiguration::class, ], - initializers = [ - Neo4jContainerInitializer::class - ] ) internal class SpringDataNeo4jContributionComparisonAdapterContractTest( @Autowired private val springDataNeo4jContributionComparisonAdapter: ContributionComparisonRepository, diff --git a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jRankingServiceAdapterContractTest.kt b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jRankingServiceAdapterContractTest.kt index c4ad0968c..ddfb81d48 100644 --- a/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jRankingServiceAdapterContractTest.kt +++ b/content-types/content-types-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/contenttypes/adapter/output/neo4j/SpringDataNeo4jRankingServiceAdapterContractTest.kt @@ -19,17 +19,10 @@ import org.orkg.graph.output.LiteralRepository import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository -import org.orkg.testing.Neo4jContainerInitializer +import org.orkg.testing.annotations.Neo4jContainerUnitTest import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest -import org.springframework.cache.annotation.EnableCaching -import org.springframework.test.context.ContextConfiguration -@EnableAutoConfiguration -@DataNeo4jTest -@EnableCaching -@ContextConfiguration( +@Neo4jContainerUnitTest( classes = [ SpringDataNeo4jStatementAdapter::class, SpringDataNeo4jResourceAdapter::class, @@ -41,11 +34,8 @@ import org.springframework.test.context.ContextConfiguration SpringDataNeo4jRosettaStoneStatementAdapter::class, SpringDataNeo4jRankingServiceAdapter::class, GraphNeo4jConfiguration::class, - ContentTypesNeo4jConfiguration::class + ContentTypesNeo4jConfiguration::class, ], - initializers = [ - Neo4jContainerInitializer::class - ] ) internal class SpringDataNeo4jRankingServiceAdapterContractTest( @Autowired private val springDataNeo4jStatementAdapter: StatementRepository, diff --git a/content-types/content-types-core-services/build.gradle.kts b/content-types/content-types-core-services/build.gradle.kts index 7b43f77b4..1a1b441a8 100644 --- a/content-types/content-types-core-services/build.gradle.kts +++ b/content-types/content-types-core-services/build.gradle.kts @@ -10,7 +10,7 @@ dependencies { api("org.springframework.data:spring-data-commons") api("org.springframework:spring-beans") api("org.springframework:spring-context") - api("org.springframework:spring-tx") + api(project(":common:spring-data")) api("org.jbibtex:jbibtex") api(project(":common:functional")) implementation(project(":common:pagination")) diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/AuthorService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/AuthorService.kt index fc8fca057..49517574d 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/AuthorService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/AuthorService.kt @@ -3,13 +3,13 @@ package org.orkg.contenttypes.domain import org.orkg.common.ThingId import org.orkg.contenttypes.input.RetrieveAuthorUseCase import org.orkg.graph.output.AuthorRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j(readOnly = true) class AuthorService( private val repository: AuthorRepository, ) : RetrieveAuthorUseCase { diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ComparisonService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ComparisonService.kt index cbc56807c..74d97d80a 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ComparisonService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ComparisonService.kt @@ -91,15 +91,15 @@ import org.orkg.graph.input.UnsafeStatementUseCases import org.orkg.graph.output.ListRepository import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ComparisonService( private val repository: ContributionComparisonRepository, private val resourceRepository: ResourceRepository, diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldHierarchyService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldHierarchyService.kt index 787c7f2fb..9a43690a2 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldHierarchyService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldHierarchyService.kt @@ -6,13 +6,13 @@ import org.orkg.contenttypes.output.ResearchFieldHierarchyRepository import org.orkg.contenttypes.output.ResearchFieldRepository import org.orkg.graph.domain.ResearchFieldNotFound import org.orkg.graph.domain.Resource +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ResearchFieldHierarchyService( private val repository: ResearchFieldHierarchyRepository, private val researchFieldRepository: ResearchFieldRepository diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldService.kt index c995894f0..76884d06e 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchFieldService.kt @@ -14,14 +14,14 @@ import org.orkg.graph.domain.Resource import org.orkg.graph.domain.Visibility import org.orkg.graph.domain.VisibilityFilter import org.orkg.graph.input.ResourceUseCases +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ResearchFieldService( private val researchFieldRepository: ResearchFieldRepository, private val researchFieldsQuery: FindResearchFieldsQuery, diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchProblemService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchProblemService.kt index a402b7f72..546a92a64 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchProblemService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/ResearchProblemService.kt @@ -15,14 +15,14 @@ import org.orkg.graph.domain.Resource import org.orkg.graph.domain.Visibility import org.orkg.graph.domain.VisibilityFilter import org.orkg.graph.input.ResourceUseCases +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ResearchProblemService( private val researchProblemRepository: ResearchProblemRepository, private val resourceService: ResourceUseCases, diff --git a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/VisualizationService.kt b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/VisualizationService.kt index 37d4d658f..e32b622b5 100644 --- a/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/VisualizationService.kt +++ b/content-types/content-types-core-services/src/main/kotlin/org/orkg/contenttypes/domain/VisualizationService.kt @@ -31,14 +31,14 @@ import org.orkg.graph.input.UnsafeResourceUseCases import org.orkg.graph.input.UnsafeStatementUseCases import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class VisualizationService( private val resourceRepository: ResourceRepository, private val statementRepository: StatementRepository, diff --git a/curation/curation-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts b/curation/curation-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts index 9d620217c..8eebfd584 100644 --- a/curation/curation-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts +++ b/curation/curation-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts @@ -29,6 +29,7 @@ testing { } dependencies { + api("org.springframework.boot:spring-boot-autoconfigure") api("org.springframework.data:spring-data-commons") api("org.springframework.data:spring-data-neo4j") api("org.springframework:spring-context") diff --git a/curation/curation-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/curation/adapter/output/neo4j/configuration/CurationNeo4jConfiguration.kt b/curation/curation-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/curation/adapter/output/neo4j/configuration/CurationNeo4jConfiguration.kt index 6f4b7da71..eb1fa96d0 100644 --- a/curation/curation-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/curation/adapter/output/neo4j/configuration/CurationNeo4jConfiguration.kt +++ b/curation/curation-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/curation/adapter/output/neo4j/configuration/CurationNeo4jConfiguration.kt @@ -1,8 +1,10 @@ package org.orkg.curation.adapter.output.neo4j.configuration +import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.context.annotation.Configuration import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories @Configuration -@EnableNeo4jRepositories("org.orkg.curation.adapter.output.neo4j.internal") +@EnableNeo4jRepositories("org.orkg.curation.adapter.output.neo4j.internal", transactionManagerRef = "neo4jTransactionManager") +@EntityScan(basePackages = ["org.orkg.curation.adapter.output.neo4j.internal"]) class CurationNeo4jConfiguration diff --git a/data-export/data-export-core/src/test/kotlin/org/orkg/export/domain/ExportPredicateIdToLabelServiceIntegrationTest.kt b/data-export/data-export-core/src/test/kotlin/org/orkg/export/domain/ExportPredicateIdToLabelServiceIntegrationTest.kt index 92116f16d..7268a0dd7 100644 --- a/data-export/data-export-core/src/test/kotlin/org/orkg/export/domain/ExportPredicateIdToLabelServiceIntegrationTest.kt +++ b/data-export/data-export-core/src/test/kotlin/org/orkg/export/domain/ExportPredicateIdToLabelServiceIntegrationTest.kt @@ -8,7 +8,6 @@ import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.orkg.common.ThingId import org.orkg.common.testing.fixtures.MockkDescribeSpec import org.orkg.export.testing.fixtures.verifyThatDirectoryExistsAndIsEmpty import org.orkg.graph.domain.Predicates diff --git a/documentation/antora/modules/ROOT/pages/how-to-guides.adoc b/documentation/antora/modules/ROOT/pages/how-to-guides.adoc index 8694f5d41..889101f20 100644 --- a/documentation/antora/modules/ROOT/pages/how-to-guides.adoc +++ b/documentation/antora/modules/ROOT/pages/how-to-guides.adoc @@ -9,3 +9,4 @@ * xref:how-to-guides/development/populate-database.adoc[] * xref:how-to-guides/development/test-fixtures.adoc[] * xref:how-to-guides/development/debug-docker-networks.adoc[] +* xref:how-to-guides/development/debug-transactions.adoc[] diff --git a/documentation/antora/modules/ROOT/pages/how-to-guides/development/debug-transactions.adoc b/documentation/antora/modules/ROOT/pages/how-to-guides/development/debug-transactions.adoc new file mode 100644 index 000000000..712903306 --- /dev/null +++ b/documentation/antora/modules/ROOT/pages/how-to-guides/development/debug-transactions.adoc @@ -0,0 +1,100 @@ += How to debug transaction issues + +Transaction issues can be tricky. +We rely on correct nesting and TX reuse. +To make debugging easier, there exists a script (`scripts/analyseTransactionLogfile.main.kts`) that visualizes transactions from test output. + +== Capturing the test output + +Creating the test output is straight-forward, but not obvious: + +. Select an integration test to visualize. +It needs to be a test that uses the relevant infrastructure (to create transactions), and should ideally be accessed via a controller (over HTTP). + +. Annotate the text class with `@ExtendWith(OutputCaptureExtension::class)`. + +. Increase logger output for all transaction related loggers to at least the `DEBUG` level (`TRACE` is recommended), e.g., by setting the properties on the test class: ++ +[source,kotlin] +---- +@TestPropertySource( + properties = [ + "logging.level.org.springframework.transaction=TRACE", + "logging.level.org.springframework.orm.jpa=TRACE", + "logging.level.org.springframework.data.neo4j.core.transaction=TRACE", + ] +) +---- ++ +Alternatively, these can be set via `application.yaml` or some other mean. ++ +NOTE: The settings above are set minimal, recommended set which the script expects. + +. Inject a `CapturedOutput` instance into the test, and save the output to a file after the test is finished, e.g. ++ +[source,kotlin] +---- +@Test +fun txTest(output: CapturedOutput) { + // Test code here ... + + // You can save the "all" (recommended), "out", or "err" captures. + File("captured.log").writeText(output.all) +} +---- + +The final setup should look something like this: + +[source,kotlin] +---- +@ExtendWith(OutputCaptureExtension::class) +@TestPropertySource( + properties = [ + "logging.level.org.springframework.transaction=TRACE", + "logging.level.org.springframework.orm.jpa=TRACE", + "logging.level.org.springframework.data.neo4j.core.transaction=TRACE", + ], +) +// Other annotations here ... +internal class MyIntegrationTest { + @Test + fun txTest(output: CapturedOutput) { + // Test code here ... + File("captured.log").writeText(output.all) + } + + // Possibly more test methods here ... +} + +---- + +After the test was started and ran to completion, the output file can be analyzed. +If you do not provide an explicit path, it is usually relative to the Gradle module that the test belongs to. + +== Analysis + +=== Generating the diagram + +Now that you captured the output, you can run `analyseTransactionLogfile.main.kts` to create a PlantUML sequence diagram. +The script returns the output on standard output, so redirecting the output to a file is required, like this: + +[source,shell] +---- +./analyseTransactionLogfile.main.kts captured.log > captured.log.puml +---- + +NOTE: If you open the file in IntelliJ IDEA, it can take up to a minute to process it, because of its size. + +You can further process the file, e.g., converting it to SVG; either directly from IntelliJ IDEA or using other means, like the PlantUML command line tool or Kroki. + +=== Notes on the diagram + +The transaction managers are color-coded to make it easier to spot. +Transactions are displayed as boxes, marking their beginning and end, as well as all calls made to other services. +Calls creating a new transactions are prefixed with a plus sign ("+") in front of their name. +Square brackets indicate the log line number that relates to the call in the diagram, so you can find them easily. + +Ideally, there should only be one straight box on the left from top to bottom, meaning that all later transactions are reusing the outermost one. +Note that due to direct service calls in a setup method, each such call can (and will) open its own transaction. +This is fine in setup and teardown methods, but not in the test method itself. +If you see the same behavior, once the call to the controller starts, the transaction is not reused, which can indicate a performance problem. diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts b/graph/graph-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts index 785a01c0b..0bf58d9f0 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/build.gradle.kts @@ -9,6 +9,7 @@ plugins { dependencies { api(project(":common:identifiers")) + api(project(":common:spring-data")) api(project(":graph:graph-core-model")) api(project(":graph:graph-ports-output")) implementation(project(":common:datatypes")) @@ -22,7 +23,6 @@ dependencies { api("org.springframework.data:spring-data-neo4j") api("org.springframework:spring-context") api("org.springframework:spring-core") - api("org.springframework:spring-tx") implementation("org.eclipse.rdf4j:rdf4j-common-io") implementation(project(":common:neo4j-dsl")) @@ -51,7 +51,6 @@ testing { implementation(testFixtures(project(":graph:graph-ports-output"))) implementation(testFixtures(project(":testing:spring"))) implementation("io.kotest:kotest-assertions-shared") - implementation("org.springframework.boot:spring-boot-test-autoconfigure") implementation("org.springframework:spring-beans") implementation("org.springframework:spring-test") implementation("io.kotest:kotest-assertions-core") diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4JLegacyStatisticsAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4JLegacyStatisticsAdapter.kt index 8c1cdc72c..b0dc60252 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4JLegacyStatisticsAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4JLegacyStatisticsAdapter.kt @@ -13,12 +13,14 @@ import org.orkg.graph.domain.ResearchFieldStats import org.orkg.graph.domain.Resource import org.orkg.graph.domain.TrendingResearchProblems import org.orkg.graph.output.LegacyStatisticsRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.neo4j.core.Neo4jClient import org.springframework.stereotype.Component @Component +@TransactionalOnNeo4j class SpringDataNeo4JLegacyStatisticsAdapter( private val neo4jRepository: Neo4jLegacyStatisticsRepository, private val neo4jClient: Neo4jClient, diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassAdapter.kt index f4225532e..7c0a311dc 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassAdapter.kt @@ -23,6 +23,7 @@ import org.orkg.graph.domain.ExactSearchString import org.orkg.graph.domain.FuzzySearchString import org.orkg.graph.domain.SearchString import org.orkg.graph.output.ClassRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.annotation.CacheConfig import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable @@ -39,11 +40,12 @@ const val CLASS_ID_TO_CLASS_EXISTS_CACHE = "class-id-to-class-exists" private const val FULLTEXT_INDEX_FOR_LABEL = "fulltext_idx_for_class_on_label" @Component +@TransactionalOnNeo4j @CacheConfig(cacheNames = [CLASS_ID_TO_CLASS_CACHE, CLASS_ID_TO_CLASS_EXISTS_CACHE]) class SpringDataNeo4jClassAdapter( private val neo4jRepository: Neo4jClassRepository, private val neo4jClassIdGenerator: Neo4jClassIdGenerator, - private val neo4jClient: Neo4jClient + private val neo4jClient: Neo4jClient, ) : ClassRepository { @Caching( evict = [ diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassHierarchyAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassHierarchyAdapter.kt index 5f9711d6c..3d94387fa 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassHierarchyAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jClassHierarchyAdapter.kt @@ -26,6 +26,7 @@ import org.orkg.graph.domain.ClassHierarchyEntry import org.orkg.graph.domain.ClassSubclassRelation import org.orkg.graph.output.ClassHierarchyRepository import org.orkg.graph.output.ClassRelationRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.neo4j.core.Neo4jClient @@ -35,8 +36,9 @@ private const val SUBCLASS_OF = "SUBCLASS_OF" private const val INSTANCE_OF = "INSTANCE_OF" @Component +@TransactionalOnNeo4j class SpringDataNeo4jClassHierarchyAdapter( - private val neo4jClient: Neo4jClient + private val neo4jClient: Neo4jClient, ) : ClassHierarchyRepository, ClassRelationRepository { override fun save(classRelation: ClassSubclassRelation) { diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jLiteralAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jLiteralAdapter.kt index 6acd3b919..dfd3fff19 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jLiteralAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jLiteralAdapter.kt @@ -23,6 +23,7 @@ import org.orkg.graph.domain.FuzzySearchString import org.orkg.graph.domain.Literal import org.orkg.graph.domain.SearchString import org.orkg.graph.output.LiteralRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.annotation.CacheConfig import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable @@ -39,11 +40,12 @@ const val LITERAL_ID_TO_LITERAL_EXISTS_CACHE = "literal-id-to-literal-exists" private const val FULLTEXT_INDEX_FOR_LABEL = "fulltext_idx_for_literal_on_label" @Component +@TransactionalOnNeo4j @CacheConfig(cacheNames = [LITERAL_ID_TO_LITERAL_CACHE, LITERAL_ID_TO_LITERAL_EXISTS_CACHE]) class SpringDataNeo4jLiteralAdapter( private val neo4jRepository: Neo4jLiteralRepository, private val neo4jLiteralIdGenerator: Neo4jLiteralIdGenerator, - private val neo4jClient: Neo4jClient + private val neo4jClient: Neo4jClient, ) : LiteralRepository { override fun nextIdentity(): ThingId { // IDs could exist already by manual creation. We need to find the next available one. diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jPredicateAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jPredicateAdapter.kt index 0667ed839..48aa63b89 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jPredicateAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jPredicateAdapter.kt @@ -23,6 +23,7 @@ import org.orkg.graph.domain.FuzzySearchString import org.orkg.graph.domain.Predicate import org.orkg.graph.domain.SearchString import org.orkg.graph.output.PredicateRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.annotation.CacheConfig import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable @@ -38,11 +39,12 @@ const val PREDICATE_ID_TO_PREDICATE_CACHE = "predicate-id-to-predicate" private const val FULLTEXT_INDEX_FOR_LABEL = "fulltext_idx_for_predicate_on_label" @Component +@TransactionalOnNeo4j @CacheConfig(cacheNames = [PREDICATE_ID_TO_PREDICATE_CACHE]) class SpringDataNeo4jPredicateAdapter( private val neo4jRepository: Neo4jPredicateRepository, private val idGenerator: Neo4jPredicateIdGenerator, - private val neo4jClient: Neo4jClient + private val neo4jClient: Neo4jClient, ) : PredicateRepository { override fun exists(id: ThingId): Boolean = neo4jRepository.existsById(id) diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jResourceAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jResourceAdapter.kt index a58bffbfa..a10d1ad0b 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jResourceAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jResourceAdapter.kt @@ -38,6 +38,7 @@ import org.orkg.graph.domain.SearchString import org.orkg.graph.domain.Visibility import org.orkg.graph.domain.VisibilityFilter import org.orkg.graph.output.ResourceRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.annotation.CacheConfig import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable @@ -55,11 +56,12 @@ private const val INSTANCE_OF = "INSTANCE_OF" private const val SUBCLASS_OF = "SUBCLASS_OF" @Component +@TransactionalOnNeo4j @CacheConfig(cacheNames = [RESOURCE_ID_TO_RESOURCE_CACHE, RESOURCE_ID_TO_RESOURCE_EXISTS_CACHE]) class SpringDataNeo4jResourceAdapter( private val neo4jRepository: Neo4jResourceRepository, private val neo4jResourceIdGenerator: Neo4jResourceIdGenerator, - private val neo4jClient: Neo4jClient + private val neo4jClient: Neo4jClient, ) : ResourceRepository { override fun nextIdentity(): ThingId { // IDs could exist already by manual creation. We need to find the next available one. diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jStatementAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jStatementAdapter.kt index 245e20efd..453ea15fd 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jStatementAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jStatementAdapter.kt @@ -58,6 +58,7 @@ import org.orkg.graph.domain.VisibilityFilter import org.orkg.graph.output.OwnershipInfo import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.StatementRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.CacheManager import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl @@ -69,6 +70,7 @@ import org.springframework.stereotype.Component private const val RELATED = "RELATED" @Component +@TransactionalOnNeo4j class SpringDataNeo4jStatementAdapter( private val neo4jStatementIdGenerator: Neo4jStatementIdGenerator, private val predicateRepository: PredicateRepository, diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jThingAdapter.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jThingAdapter.kt index e7851222f..9407c27a5 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jThingAdapter.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jThingAdapter.kt @@ -6,6 +6,7 @@ import org.orkg.graph.adapter.output.neo4j.internal.Neo4jThing import org.orkg.graph.adapter.output.neo4j.internal.Neo4jThingRepository import org.orkg.graph.domain.Thing import org.orkg.graph.output.ThingRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.cache.annotation.CacheConfig import org.springframework.cache.annotation.Cacheable import org.springframework.data.domain.Page @@ -15,9 +16,10 @@ import org.springframework.stereotype.Component const val THING_ID_TO_THING_CACHE = "thing-id-to-thing" @Component +@TransactionalOnNeo4j @CacheConfig(cacheNames = [THING_ID_TO_THING_CACHE]) class SpringDataNeo4jThingAdapter( - private val neo4jRepository: Neo4jThingRepository + private val neo4jRepository: Neo4jThingRepository, ) : ThingRepository { @Cacheable(key = "#id", cacheNames = [THING_ID_TO_THING_CACHE]) override fun findByThingId(id: ThingId): Optional = neo4jRepository.findById(id).map(Neo4jThing::toThing) diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/configuration/GraphNeo4jConfiguration.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/configuration/GraphNeo4jConfiguration.kt index 8af4626f6..34faa9e78 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/configuration/GraphNeo4jConfiguration.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/configuration/GraphNeo4jConfiguration.kt @@ -19,7 +19,7 @@ import org.springframework.data.neo4j.core.convert.Neo4jConversions import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories @Configuration -@EnableNeo4jRepositories("org.orkg.graph.adapter.output.neo4j.internal") +@EnableNeo4jRepositories("org.orkg.graph.adapter.output.neo4j.internal", transactionManagerRef = "neo4jTransactionManager") @EntityScan("org.orkg.graph.adapter.output.neo4j.internal") @ComponentScan(basePackages = ["org.orkg.graph.adapter.output.neo4j.internal"]) class GraphNeo4jConfiguration { @@ -43,9 +43,10 @@ class GraphNeo4jConfiguration { fun neo4jClient( driver: Driver, databaseNameProvider: DatabaseSelectionProvider, - neo4jConversions: Neo4jConversions + neo4jConversions: Neo4jConversions, ): Neo4jClient = - Neo4jClient.with(driver) + Neo4jClient + .with(driver) .withDatabaseSelectionProvider(databaseNameProvider) .withNeo4jConversions(neo4jConversions) .build() diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jPredicateRepository.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jPredicateRepository.kt index c5a8faca6..d37a128cf 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jPredicateRepository.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jPredicateRepository.kt @@ -3,12 +3,10 @@ package org.orkg.graph.adapter.output.neo4j.internal import org.orkg.common.ThingId import org.springframework.data.neo4j.repository.Neo4jRepository import org.springframework.data.neo4j.repository.query.Query -import org.springframework.transaction.annotation.Transactional private const val id = "${'$'}id" interface Neo4jPredicateRepository : Neo4jRepository { - @Transactional override fun deleteById(id: ThingId) @Query(""" diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jResourceRepository.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jResourceRepository.kt index 850544889..2306edd01 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jResourceRepository.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/graph/adapter/output/neo4j/internal/Neo4jResourceRepository.kt @@ -9,7 +9,6 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.neo4j.repository.Neo4jRepository import org.springframework.data.neo4j.repository.query.Query -import org.springframework.transaction.annotation.Transactional private const val classes = "${'$'}classes" private const val label = "${'$'}label" @@ -66,7 +65,6 @@ interface Neo4jResourceRepository : Neo4jRepository { countQuery = """MATCH (n:`Resource`) WHERE n.created_by <> "00000000-0000-0000-0000-000000000000" RETURN COUNT(DISTINCT n.created_by) as cnt""") fun findAllContributorIds(pageable: Pageable): Page - @Transactional override fun deleteById(id: ThingId) @Query("""MATCH (node:Resource) $WHERE_VISIBILITY AND ANY(c in $classes WHERE c IN labels(node)) $WITH_NODE_PROPERTIES $ORDER_BY_CREATED_AT $RETURN_NODE $ORDER_BY_PAGE_PARAMS""", diff --git a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jListAdapterContractTest.kt b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jListAdapterContractTest.kt index 8efcba92e..258f5ec8c 100644 --- a/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jListAdapterContractTest.kt +++ b/graph/graph-adapter-output-spring-data-neo4j-sdn6/src/test/kotlin/org/orkg/graph/adapter/output/neo4j/SpringDataNeo4jListAdapterContractTest.kt @@ -8,17 +8,10 @@ import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository import org.orkg.graph.testing.fixtures.listRepositoryContract -import org.orkg.testing.Neo4jContainerInitializer +import org.orkg.testing.annotations.Neo4jContainerUnitTest import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest -import org.springframework.cache.annotation.EnableCaching -import org.springframework.test.context.ContextConfiguration -@EnableAutoConfiguration -@DataNeo4jTest -@EnableCaching -@ContextConfiguration( +@Neo4jContainerUnitTest( classes = [ ListAdapter::class, SpringDataNeo4jStatementAdapter::class, @@ -27,17 +20,14 @@ import org.springframework.test.context.ContextConfiguration SpringDataNeo4jLiteralAdapter::class, SpringDataNeo4jClassAdapter::class, SpringDataNeo4jThingAdapter::class, - GraphNeo4jConfiguration::class + GraphNeo4jConfiguration::class, ], - initializers = [ - Neo4jContainerInitializer::class - ] ) internal class SpringDataNeo4jListAdapterContractTest( @Autowired private val listAdapter: ListRepository, @Autowired private val springDataNeo4jResourceAdapter: ResourceRepository, @Autowired private val springDataNeo4jPredicateAdapter: PredicateRepository, - @Autowired private val springDataNeo4jStatementAdapter: StatementRepository + @Autowired private val springDataNeo4jStatementAdapter: StatementRepository, ) : DescribeSpec({ include( listRepositoryContract( diff --git a/graph/graph-core-services/build.gradle.kts b/graph/graph-core-services/build.gradle.kts index 91e46bcdb..f89f04f73 100644 --- a/graph/graph-core-services/build.gradle.kts +++ b/graph/graph-core-services/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { api("org.eclipse.rdf4j:rdf4j-common-io") api("org.springframework.data:spring-data-commons") api("org.springframework:spring-context") - api("org.springframework:spring-tx") + api(project(":common:spring-data")) implementation("dev.forkhandles:values4k") implementation(project(":community:community-core-model")) } diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/BulkStatementService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/BulkStatementService.kt index 5572d19f0..bf2ecac3b 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/BulkStatementService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/BulkStatementService.kt @@ -4,16 +4,15 @@ import kotlin.collections.List import org.orkg.common.ThingId import org.orkg.graph.input.GetBulkStatementsQuery import org.orkg.graph.output.StatementRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class BulkStatementService( private val statementRepository: StatementRepository, ) : GetBulkStatementsQuery { - override fun getBulkStatementsBySubjects( subjects: List, pageable: Pageable diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassHierarchyService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassHierarchyService.kt index 90981a63b..30881db13 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassHierarchyService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassHierarchyService.kt @@ -9,13 +9,13 @@ import org.orkg.graph.input.ClassHierarchyUseCases import org.orkg.graph.output.ClassHierarchyRepository import org.orkg.graph.output.ClassRelationRepository import org.orkg.graph.output.ClassRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ClassHierarchyService( private val repository: ClassHierarchyRepository, private val relationRepository: ClassRelationRepository, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassService.kt index 960151099..a3ae099e8 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ClassService.kt @@ -11,13 +11,13 @@ import org.orkg.graph.input.ClassUseCases import org.orkg.graph.input.CreateClassUseCase import org.orkg.graph.input.UpdateClassUseCase import org.orkg.graph.output.ClassRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ClassService( private val repository: ClassRepository, private val clock: Clock, @@ -52,7 +52,7 @@ class ClassService( return newClass.id } - @Transactional(readOnly = true) + @TransactionalOnNeo4j(readOnly = true) override fun exists(id: ThingId): Boolean = repository.exists(id) override fun findAll( diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/FormattedLabelService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/FormattedLabelService.kt index d29816352..62fb357ea 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/FormattedLabelService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/FormattedLabelService.kt @@ -5,11 +5,11 @@ import kotlin.collections.List import org.orkg.common.ThingId import org.orkg.graph.input.FormattedLabelUseCases import org.orkg.graph.output.FormattedLabelRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class FormattedLabelService( private val repository: FormattedLabelRepository ) : FormattedLabelUseCases { diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ImportService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ImportService.kt index 24318bfa2..282e85747 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ImportService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ImportService.kt @@ -19,11 +19,11 @@ import org.orkg.graph.input.UnsafeStatementUseCases import org.orkg.graph.output.ExternalClassService import org.orkg.graph.output.ExternalPredicateService import org.orkg.graph.output.ExternalResourceService +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ImportService( private val externalClassRepositories: MutableList, private val externalResourceRepositories: MutableList, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LegacyStatisticsService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LegacyStatisticsService.kt index 0e7b82f80..12bcda95a 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LegacyStatisticsService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LegacyStatisticsService.kt @@ -14,14 +14,14 @@ import org.orkg.community.output.OrganizationRepository import org.orkg.graph.input.RetrieveLegacyStatisticsUseCase import org.orkg.graph.output.LegacyStatisticsRepository import org.orkg.graph.output.ResourceRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class LegacyStatisticsService( private val legacyStatisticsRepository: LegacyStatisticsRepository, private val contributorRepository: ContributorRepository, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ListService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ListService.kt index 145b9dc88..4a11d9bc5 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ListService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ListService.kt @@ -10,13 +10,13 @@ import org.orkg.graph.input.ListUseCases import org.orkg.graph.input.UpdateListUseCase import org.orkg.graph.output.ListRepository import org.orkg.graph.output.ThingRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ListService( private val repository: ListRepository, private val thingRepository: ThingRepository, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LiteralService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LiteralService.kt index 81b9a39a5..fe03ebeb8 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LiteralService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/LiteralService.kt @@ -10,13 +10,13 @@ import org.orkg.graph.input.CreateLiteralUseCase import org.orkg.graph.input.LiteralUseCases import org.orkg.graph.output.LiteralRepository import org.orkg.graph.output.StatementRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class LiteralService( private val repository: LiteralRepository, private val statementRepository: StatementRepository, @@ -49,7 +49,7 @@ class LiteralService( return id } - @Transactional(readOnly = true) + @TransactionalOnNeo4j(readOnly = true) override fun exists(id: ThingId): Boolean = repository.exists(id) override fun findAll( diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ObjectService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ObjectService.kt index 779b19c54..ce7cb94e3 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ObjectService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ObjectService.kt @@ -22,9 +22,11 @@ import org.orkg.graph.input.LiteralUseCases import org.orkg.graph.input.PredicateUseCases import org.orkg.graph.input.ResourceUseCases import org.orkg.graph.input.UnsafeStatementUseCases +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.stereotype.Service @Service +@TransactionalOnNeo4j class ObjectService( private val resourceService: ResourceUseCases, private val literalService: LiteralUseCases, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/PredicateService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/PredicateService.kt index cca2e4a97..06ff3006c 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/PredicateService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/PredicateService.kt @@ -12,19 +12,19 @@ import org.orkg.graph.input.CreatePredicateUseCase import org.orkg.graph.input.PredicateUseCases import org.orkg.graph.input.UpdatePredicateUseCase import org.orkg.graph.output.PredicateRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class PredicateService( private val repository: PredicateRepository, private val contributorRepository: ContributorRepository, private val clock: Clock, ) : PredicateUseCases { - @Transactional(readOnly = true) + @TransactionalOnNeo4j(readOnly = true) override fun exists(id: ThingId): Boolean = repository.exists(id) override fun create(command: CreatePredicateUseCase.CreateCommand): ThingId { diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ResourceService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ResourceService.kt index 7c38f2e67..12c876c00 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ResourceService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ResourceService.kt @@ -22,13 +22,13 @@ import org.orkg.graph.output.ClassRepository import org.orkg.graph.output.ResourceRepository import org.orkg.graph.output.StatementRepository import org.orkg.graph.output.ThingRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class ResourceService( private val repository: ResourceRepository, private val statementRepository: StatementRepository, @@ -39,7 +39,7 @@ class ResourceService( private val observatoryRepository: ObservatoryRepository, private val organizationRepository: OrganizationRepository ) : ResourceUseCases { - @Transactional(readOnly = true) + @TransactionalOnNeo4j(readOnly = true) override fun exists(id: ThingId): Boolean = repository.exists(id) override fun create(command: CreateResourceUseCase.CreateCommand): ThingId { diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/StatementService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/StatementService.kt index 3afc9230d..11ce8b018 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/StatementService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/StatementService.kt @@ -13,14 +13,14 @@ import org.orkg.graph.output.LiteralRepository import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.StatementRepository import org.orkg.graph.output.ThingRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j class StatementService( private val thingRepository: ThingRepository, private val predicateService: PredicateRepository, @@ -32,7 +32,7 @@ class StatementService( override fun findById(statementId: StatementId): Optional = statementRepository.findByStatementId(statementId) - @Transactional(readOnly = true) + @TransactionalOnNeo4j(readOnly = true) override fun exists(id: StatementId): Boolean = statementRepository.exists(id) override fun findAll( diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ThingService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ThingService.kt index bf810262f..a0ef6c9c3 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ThingService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/ThingService.kt @@ -4,11 +4,11 @@ import java.util.* import org.orkg.common.ThingId import org.orkg.graph.input.RetrieveThingUseCase import org.orkg.graph.output.ThingRepository +import org.orkg.spring.data.annotations.TransactionalOnNeo4j import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional +@TransactionalOnNeo4j(readOnly = true) class ThingService( private val repository: ThingRepository, ) : RetrieveThingUseCase { diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeResourceService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeResourceService.kt index dcef1be31..04297b665 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeResourceService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeResourceService.kt @@ -11,10 +11,8 @@ import org.orkg.graph.input.UnsafeResourceUseCases import org.orkg.graph.input.UpdateResourceUseCase.UpdateCommand import org.orkg.graph.output.ResourceRepository import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional class UnsafeResourceService( protected val repository: ResourceRepository, protected val clock: Clock, diff --git a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeStatementService.kt b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeStatementService.kt index d8339a8f9..06091aded 100644 --- a/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeStatementService.kt +++ b/graph/graph-core-services/src/main/kotlin/org/orkg/graph/domain/UnsafeStatementService.kt @@ -10,10 +10,8 @@ import org.orkg.graph.output.PredicateRepository import org.orkg.graph.output.StatementRepository import org.orkg.graph.output.ThingRepository import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional @Service -@Transactional class UnsafeStatementService( private val thingRepository: ThingRepository, private val predicateRepository: PredicateRepository, diff --git a/graph/graph-ports-output/build.gradle.kts b/graph/graph-ports-output/build.gradle.kts index 82678fa0f..8e38d3c65 100644 --- a/graph/graph-ports-output/build.gradle.kts +++ b/graph/graph-ports-output/build.gradle.kts @@ -9,7 +9,6 @@ dependencies { api("org.eclipse.rdf4j:rdf4j-common-io") api("org.springframework.data:spring-data-commons") - api("org.springframework:spring-tx") testFixturesApi("io.kotest:kotest-framework-api") testFixturesImplementation("io.kotest:kotest-assertions-shared") diff --git a/graph/graph-ports-output/src/main/kotlin/org/orkg/graph/output/StatementRepository.kt b/graph/graph-ports-output/src/main/kotlin/org/orkg/graph/output/StatementRepository.kt index 445f06d82..338152d50 100644 --- a/graph/graph-ports-output/src/main/kotlin/org/orkg/graph/output/StatementRepository.kt +++ b/graph/graph-ports-output/src/main/kotlin/org/orkg/graph/output/StatementRepository.kt @@ -18,25 +18,26 @@ import org.orkg.graph.domain.VisibilityFilter import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort -import org.springframework.transaction.annotation.Transactional -interface StatementRepository : EntityRepository { - fun countIncomingStatements(id: ThingId): Long +interface StatementReadRepository : EntityRepository { + fun count(): Long + + fun countByIdRecursive(id: ThingId): Long // Subject id + fun countIncomingStatements(ids: Set): Map - fun findAllDescriptions(ids: Set): Map + + fun countIncomingStatements(id: ThingId): Long + + fun countPredicateUsage(pageable: Pageable): Page + fun determineOwnership(statementIds: Set): Set - // legacy methods: - fun nextIdentity(): StatementId - @Transactional - fun save(statement: GeneralStatement) - @Transactional - fun saveAll(statements: Set) - fun count(): Long - fun delete(statement: GeneralStatement) - fun deleteByStatementId(id: StatementId) - fun deleteByStatementIds(ids: Set) - fun deleteAll() - fun findByStatementId(id: StatementId): Optional + + fun fetchAsBundle( + id: ThingId, + configuration: BundleConfiguration, + sort: Sort, + ): Iterable + fun findAll( pageable: Pageable, subjectClasses: Set = emptySet(), @@ -48,32 +49,89 @@ interface StatementRepository : EntityRepository createdAtEnd: OffsetDateTime? = null, objectClasses: Set = emptySet(), objectId: ThingId? = null, - objectLabel: String? = null + objectLabel: String? = null, ): Page - fun findAllByStatementIdIn(ids: Set, pageable: Pageable): Page - fun countByIdRecursive(id: ThingId): Long // Subject id - fun findAllBySubjects(subjectIds: List, pageable: Pageable): Page - fun findAllByObjects(objectIds: List, pageable: Pageable): Page - fun fetchAsBundle(id: ThingId, configuration: BundleConfiguration, sort: Sort): Iterable - fun countPredicateUsage(pageable: Pageable): Page - fun findDOIByContributionId(id: ThingId): Optional - fun findByDOI(doi: String, classes: Set): Optional - fun findAllBySubjectClassAndDOI(subjectClass: ThingId, doi: String, pageable: Pageable): Page + fun findAllByObjects( + objectIds: List, + pageable: Pageable, + ): Page - fun findAllProblemsByObservatoryId(id: ObservatoryId, pageable: Pageable): Page - fun findAllContributorsByResourceId(id: ThingId, pageable: Pageable): Page - fun findTimelineByResourceId(id: ThingId, pageable: Pageable): Page - fun findAllProblemsByOrganizationId(id: OrganizationId, pageable: Pageable): Page + fun findAllByStatementIdIn( + ids: Set, + pageable: Pageable, + ): Page + + fun findAllBySubjectClassAndDOI( + subjectClass: ThingId, + doi: String, + pageable: Pageable, + ): Page + + fun findAllBySubjects( + subjectIds: List, + pageable: Pageable, + ): Page + + fun findAllContributorsByResourceId( + id: ThingId, + pageable: Pageable, + ): Page + + fun findAllDescriptions(ids: Set): Map fun findAllPapersByObservatoryIdAndFilters( observatoryId: ObservatoryId?, filters: List, visibility: VisibilityFilter, - pageable: Pageable + pageable: Pageable, ): Page + + fun findAllProblemsByObservatoryId( + id: ObservatoryId, + pageable: Pageable, + ): Page + + fun findAllProblemsByOrganizationId( + id: OrganizationId, + pageable: Pageable, + ): Page + + fun findByDOI( + doi: String, + classes: Set, + ): Optional + + fun findByStatementId(id: StatementId): Optional + + fun findDOIByContributionId(id: ThingId): Optional + + fun findTimelineByResourceId( + id: ThingId, + pageable: Pageable, + ): Page } +interface StatementWriteRepository { + fun delete(statement: GeneralStatement) + + fun deleteAll() + + fun deleteByStatementId(id: StatementId) + + fun deleteByStatementIds(ids: Set) + + fun nextIdentity(): StatementId + + fun save(statement: GeneralStatement) + + fun saveAll(statements: Set) +} + +interface StatementRepository : + StatementReadRepository, + StatementWriteRepository + data class OwnershipInfo( val statementId: StatementId, val owner: ContributorId, diff --git a/rest-api-server/build.gradle.kts b/rest-api-server/build.gradle.kts index 08ef92f9a..151159cf5 100644 --- a/rest-api-server/build.gradle.kts +++ b/rest-api-server/build.gradle.kts @@ -187,6 +187,8 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind") implementation("org.springframework:spring-tx") implementation("org.springframework:spring-webmvc") + implementation("org.springframework:spring-orm") + implementation("jakarta.persistence:jakarta.persistence-api") kapt("org.springframework.boot:spring-boot-configuration-processor") @@ -244,7 +246,6 @@ dependencies { "integrationTestImplementation"("org.springframework.restdocs:spring-restdocs-core") "integrationTestImplementation"("org.springframework:spring-beans") "integrationTestImplementation"("org.springframework:spring-test") - "integrationTestImplementation"("org.springframework:spring-tx") "integrationTestImplementation"(project(":content-types:content-types-adapter-input-rest-spring-mvc")) "integrationTestImplementation"(project(":content-types:content-types-core-model")) "integrationTestImplementation"(project(":content-types:content-types-ports-input")) diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ContributorControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ContributorControllerIntegrationTest.kt index c908d5a96..08a8c6c8f 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ContributorControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ContributorControllerIntegrationTest.kt @@ -13,10 +13,8 @@ import org.springframework.restdocs.payload.PayloadDocumentation.responseFields import org.springframework.restdocs.request.RequestDocumentation.parameterWithName import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @SpringBootTest -@Transactional internal class ContributorControllerIntegrationTest : MockMvcBaseTest("contributors") { @Autowired diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ObservatoryControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ObservatoryControllerIntegrationTest.kt index 5e63482d6..7b629c273 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ObservatoryControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/ObservatoryControllerIntegrationTest.kt @@ -28,13 +28,10 @@ import org.springframework.restdocs.request.RequestDocumentation.parameterWithNa import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional import orkg.orkg.community.testing.fixtures.observatoryResponseFields @Neo4jContainerIntegrationTest -@Transactional internal class ObservatoryControllerIntegrationTest : MockMvcBaseTest("observatories") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/OrganizationControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/OrganizationControllerIntegrationTest.kt index 1f98a5e33..7dbbf8851 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/OrganizationControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/community/adapter/input/rest/OrganizationControllerIntegrationTest.kt @@ -25,12 +25,9 @@ import org.springframework.restdocs.payload.ResponseFieldsSnippet import org.springframework.restdocs.request.RequestDocumentation.parameterWithName import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class OrganizationControllerIntegrationTest : MockMvcBaseTest("organizations") { - @Autowired private lateinit var contributorService: ContributorUseCases @@ -58,11 +55,11 @@ internal class OrganizationControllerIntegrationTest : MockMvcBaseTest("organiza @AfterEach fun cleanup() { - service.removeAll() observatoryService.removeAll() + service.removeAll() + contributorService.deleteAll() resourceService.removeAll() classService.removeAll() - contributorService.deleteAll() } @Test diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerIntegrationTest.kt index 0e8bcc49a..9c7a0e200 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ComparisonControllerIntegrationTest.kt @@ -58,10 +58,8 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Import import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional @Import(InMemorySimCompThingRepositoryAdapter::class) internal class ComparisonControllerIntegrationTest : MockMvcBaseTest("comparisons") { diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ContributionComparisonControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ContributionComparisonControllerIntegrationTest.kt index 41d939467..bfb3ae3a5 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ContributionComparisonControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ContributionComparisonControllerIntegrationTest.kt @@ -25,10 +25,8 @@ import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath import org.springframework.restdocs.payload.PayloadDocumentation.responseFields import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class ContributionComparisonControllerIntegrationTest : MockMvcBaseTest("contribution-comparison") { @Autowired diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyComparisonControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyComparisonControllerIntegrationTest.kt index 92fa88952..cfdda9fff 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyComparisonControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyComparisonControllerIntegrationTest.kt @@ -33,7 +33,6 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @Neo4jContainerIntegrationTest internal class LegacyComparisonControllerIntegrationTest : MockMvcBaseTest("comparisons") { - @Autowired private lateinit var resourceService: ResourceUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyPaperControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyPaperControllerIntegrationTest.kt index 4c4cf32e8..1c48002f2 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyPaperControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LegacyPaperControllerIntegrationTest.kt @@ -31,12 +31,9 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class LegacyPaperControllerIntegrationTest : MockMvcBaseTest("papers") { - @Autowired private lateinit var predicateService: PredicateUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerIntegrationTest.kt index 016bb3b9e..256f22659 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/LiteratureListControllerIntegrationTest.kt @@ -50,12 +50,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class LiteratureListControllerIntegrationTest : MockMvcBaseTest("literature-lists") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/PaperControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/PaperControllerIntegrationTest.kt index c5e56fd50..13c046001 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/PaperControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/PaperControllerIntegrationTest.kt @@ -46,12 +46,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class PaperControllerIntegrationTest : MockMvcBaseTest("papers") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ProblemControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ProblemControllerIntegrationTest.kt index 501923857..5da2a96a4 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ProblemControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/ProblemControllerIntegrationTest.kt @@ -33,12 +33,9 @@ import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.restdocs.request.RequestDocumentation.queryParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class ProblemControllerIntegrationTest : MockMvcBaseTest("research-problems") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerIntegrationTest.kt index 94d8c98c6..3f5b33267 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneStatementControllerIntegrationTest.kt @@ -62,13 +62,10 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Import import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional @Import(ContentTypeJacksonModule::class) internal class RosettaStoneStatementControllerIntegrationTest : MockMvcBaseTest("rosetta-stone-statements") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerIntegrationTest.kt index b66497442..d3fe65ea0 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/RosettaStoneTemplateControllerIntegrationTest.kt @@ -50,12 +50,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class RosettaStoneTemplateControllerIntegrationTest : MockMvcBaseTest("rosetta-stone-templates") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerIntegrationTest.kt index 820bd2d04..bed88f750 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/SmartReviewControllerIntegrationTest.kt @@ -55,12 +55,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class SmartReviewControllerIntegrationTest : MockMvcBaseTest("smart-reviews") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateControllerIntegrationTest.kt index 87a6a849e..fc1bb601b 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateControllerIntegrationTest.kt @@ -52,12 +52,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class TemplateControllerIntegrationTest : MockMvcBaseTest("templates") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateInstanceControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateInstanceControllerIntegrationTest.kt index db568736a..93ef7d955 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateInstanceControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/TemplateInstanceControllerIntegrationTest.kt @@ -40,12 +40,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class TemplateInstanceControllerIntegrationTest : MockMvcBaseTest("template-instances") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/VisualizationControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/VisualizationControllerIntegrationTest.kt index 1770f9ff4..c7ea91254 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/VisualizationControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/contenttypes/adapter/input/rest/VisualizationControllerIntegrationTest.kt @@ -31,12 +31,9 @@ import org.orkg.testing.spring.MockMvcBaseTest import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.domain.PageRequest import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class VisualizationControllerIntegrationTest : MockMvcBaseTest("visualizations") { - @Autowired private lateinit var contributorService: ContributorUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ClassControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ClassControllerIntegrationTest.kt index c6c107f58..a845e663c 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ClassControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ClassControllerIntegrationTest.kt @@ -18,10 +18,8 @@ import org.springframework.restdocs.request.RequestDocumentation.parameterWithNa import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class ClassControllerIntegrationTest : MockMvcBaseTest("classes") { @Autowired diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/LiteralControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/LiteralControllerIntegrationTest.kt index bf42cbba7..01887f27b 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/LiteralControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/LiteralControllerIntegrationTest.kt @@ -22,12 +22,9 @@ import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class LiteralControllerIntegrationTest : MockMvcBaseTest("literals") { - @Autowired private lateinit var service: LiteralUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/PredicateControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/PredicateControllerIntegrationTest.kt index 2141f903e..4fdd80fc1 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/PredicateControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/PredicateControllerIntegrationTest.kt @@ -27,12 +27,9 @@ import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class PredicateControllerIntegrationTest : MockMvcBaseTest("predicates") { - @Autowired private lateinit var service: PredicateUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ResourceControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ResourceControllerIntegrationTest.kt index ac0b6c43d..926024892 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ResourceControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/ResourceControllerIntegrationTest.kt @@ -40,13 +40,10 @@ import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.context.TestPropertySource import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional @TestPropertySource(properties = ["orkg.features.formatted_labels=false"]) internal class ResourceControllerIntegrationTest : MockMvcBaseTest("resources") { - @Autowired private lateinit var service: ResourceUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatementControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatementControllerIntegrationTest.kt index 12ff9a5c8..13872454d 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatementControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatementControllerIntegrationTest.kt @@ -25,12 +25,9 @@ import org.springframework.restdocs.request.RequestDocumentation.parameterWithNa import org.springframework.restdocs.request.RequestDocumentation.pathParameters import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class StatementControllerIntegrationTest : MockMvcBaseTest("statements") { - @Autowired private lateinit var statementService: StatementUseCases diff --git a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatisticsControllerIntegrationTest.kt b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatisticsControllerIntegrationTest.kt index 4065a00b2..855584d05 100644 --- a/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatisticsControllerIntegrationTest.kt +++ b/rest-api-server/src/integrationTest/kotlin/org/orkg/graph/adapter/input/rest/StatisticsControllerIntegrationTest.kt @@ -20,12 +20,9 @@ import org.springframework.data.domain.PageRequest import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath import org.springframework.restdocs.payload.PayloadDocumentation.responseFields import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional @Neo4jContainerIntegrationTest -@Transactional internal class StatisticsControllerIntegrationTest : MockMvcBaseTest("statistics") { - @Autowired private lateinit var resourceService: ResourceUseCases diff --git a/rest-api-server/src/main/kotlin/org/orkg/configuration/JPAConfiguration.kt b/rest-api-server/src/main/kotlin/org/orkg/configuration/JPAConfiguration.kt new file mode 100644 index 000000000..89d16fb4b --- /dev/null +++ b/rest-api-server/src/main/kotlin/org/orkg/configuration/JPAConfiguration.kt @@ -0,0 +1,12 @@ +package org.orkg.configuration + +import jakarta.persistence.EntityManagerFactory +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.orm.jpa.JpaTransactionManager + +@Configuration +class JPAConfiguration { + @Bean + fun jpaTransactionManager(emf: EntityManagerFactory): JpaTransactionManager = JpaTransactionManager(emf) +} diff --git a/rest-api-server/src/main/kotlin/org/orkg/configuration/Neo4jConfiguration.kt b/rest-api-server/src/main/kotlin/org/orkg/configuration/Neo4jConfiguration.kt index 6cee6a1de..192472960 100644 --- a/rest-api-server/src/main/kotlin/org/orkg/configuration/Neo4jConfiguration.kt +++ b/rest-api-server/src/main/kotlin/org/orkg/configuration/Neo4jConfiguration.kt @@ -3,6 +3,7 @@ package org.orkg.configuration import org.neo4j.driver.Driver import org.springframework.beans.factory.ObjectProvider import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -14,22 +15,34 @@ import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager // Configure custom transaction manager, because Spring Data Neo4j does not do that anymore if JPA is autoconfigured. -// See https://github.com/spring-projects/spring-data-neo4j/issues/2931. +// See https://github.com/spring-projects/spring-data-neo4j/issues/2931 for more information. +// It will, however, happyly reuse the transaction manager provided by JPA's autoconfiguration, and will not be able +// to reuse a running transaction initiated by JPA, leading to hundreds of transactions to Neo4j for longer requests. +// The proper solution is to split the transaction management, and explicitly define the manager to be used. @Configuration class Neo4jConfiguration { - @Bean("neo4jTemplate") + // Inject a Neo4j template, because this is not done automatically when JPA is configured as well. + @Bean @ConditionalOnMissingBean(Neo4jOperations::class) fun neo4jTemplate( neo4jClient: Neo4jClient, neo4jMappingContext: Neo4jMappingContext, + neo4jTransactionManager: Neo4jTransactionManager, + ): Neo4jTemplate = Neo4jTemplate(neo4jClient, neo4jMappingContext, neo4jTransactionManager) + + /** + * A new transaction manager for Neo4j. + * + * The source was copied from [Neo4jDataAutoConfiguration.transactionManager] and converted to Kotlin. + * The customizers are required, so custom converters, e.g., for IDs, can be hooked into it. + */ + @Bean + fun neo4jTransactionManager( driver: Driver, - databaseNameProvider: DatabaseSelectionProvider, - optionalCustomizers: ObjectProvider - ): Neo4jTemplate { - val transactionManager = Neo4jTransactionManager(driver, databaseNameProvider) - optionalCustomizers.ifAvailable { customizer: TransactionManagerCustomizers -> - customizer.customize(transactionManager) + databaseSelectionProvider: DatabaseSelectionProvider, + optionalCustomizers: ObjectProvider, + ): Neo4jTransactionManager = + Neo4jTransactionManager(driver, databaseSelectionProvider).also { transactionManager -> + optionalCustomizers.ifAvailable { customizer -> customizer.customize(transactionManager) } } - return Neo4jTemplate(neo4jClient, neo4jMappingContext, transactionManager) - } } diff --git a/rest-api-server/src/main/resources/config/application.yml b/rest-api-server/src/main/resources/config/application.yml index 2263cc92e..f90645788 100644 --- a/rest-api-server/src/main/resources/config/application.yml +++ b/rest-api-server/src/main/resources/config/application.yml @@ -130,3 +130,7 @@ spring: logging: level: org.springframework.data.neo4j: DEBUG + # Transaction logging + #org.springframework.transaction: TRACE + #org.springframework.orm.jpa: TRACE + #org.springframework.data.neo4j.core.transaction: TRACE diff --git a/scripts/analyseTransactionLogfile.main.kts b/scripts/analyseTransactionLogfile.main.kts new file mode 100755 index 000000000..682dec878 --- /dev/null +++ b/scripts/analyseTransactionLogfile.main.kts @@ -0,0 +1,137 @@ +#!/usr/bin/env kotlin + +import java.io.File +import java.util.Stack + +val capacity = 16 * 1024 // chars + +val filename: String = args.firstOrNull() ?: error("A log file needs to be passed as argument.") + +val file = File(filename) + +if (!file.exists()) error("Cannot find file \"$filename\".") + +val diagram = StringBuilder(capacity) + +File(filename).useLines { lines: Sequence -> + diagram.appendLine("@startuml") + // diagram.appendLine("!pragma teoz true") // Use new rendering engine + + diagram.appendLine("hide unlinked") + + diagram.appendLine("participant Test") + diagram.appendLine("participant neo4jTransactionManager") + diagram.appendLine("participant jpaTransactionManager") + + // State, used for calls + val callStack: Stack> = Stack() + + lines.forEachIndexed { idx, line -> + val lineNumber = idx.inc() + + // Jump over lines that do not have a timestamp + if (!line.matches(Regex("^[0-9].*"))) return@forEachIndexed + + // Split log lines into elements + val (metadata, message: String) = line.split(" - ") + val (time, _, _, level, logger) = metadata.split(" ") + + // TODO: Parse "No need to create transaction for" + when { + // Creating a transaction + message.startsWith("Creating new transaction with name") -> { + val regex = Regex("""Creating new transaction with name \[(?.+?)]: (?[^;]+)(; '(?.+?)')?""") + val groups = regex.find(message)!!.groups + val (clazz, method) = groups["name"]!!.value.split('.').takeLast(2) + val properties = groups["props"]!!.value.split(',') + var manager = groups["manager"]?.value ?: "" // from Simple*Repository + + if (manager.isEmpty()) { + manager = + when { + logger.endsWith("Neo4jTransactionManager") -> { + "neo4jTransactionManager" + } + + else -> error("Unable to determine transaction manager") + } + } + + val symbols = mutableListOf("plus") + if ("readOnly" in properties) symbols += "lock-locked" + + val color = when(manager) { + "neo4jTransactionManager" -> "orange" + "jpaTransactionManager" -> "blue" + else -> "" + } + + with(diagram) { + appendCall(lineNumber, "Test", manager, clazz, method, symbols) + append("activate $manager") + if (color.isNotEmpty()) { + append(" #$color") + } + appendLine() + } + + callStack.push("Test" to manager) + } + // Closing a transaction + message.startsWith("Initiating transaction commit") -> { + val currentCall = callStack.pop() + diagram.appendLine("deactivate ${currentCall.second}") + } + // Looking for an existing transaction + message.startsWith("Getting transaction for") && logger.endsWith("TransactionInterceptor") -> { + val regex = Regex("""Getting transaction for \[(?.+?)]""") + val groups = regex.find(message)!!.groups + var (clazz, method) = groups["name"]!!.value.split('.').takeLast(2) + + val currentComponent = callStack.peek().second + + if (clazz == "SimpleNeo4jRepository") { + clazz = currentComponent.removePrefix("SpringDataNeo4j").removeSuffix("Adapter") + "InternalRepository" + } + + diagram.appendCall(lineNumber, currentComponent, clazz, clazz, method) + callStack.push(currentComponent to clazz) + } + // Completing running transaction + message.startsWith("Completing transaction for") && logger.endsWith("TransactionInterceptor") -> { + val regex = Regex("""Completing transaction for \[(?.+?)]""") + val groups = regex.find(message)!!.groups + var (clazz, method) = groups["name"]!!.value.split('.').takeLast(2) + + val currentCall = callStack.pop() + + if (clazz == "SimpleNeo4jRepository") { + clazz = currentCall.second + } + + // plausibility check; should never trigger + require(currentCall.second == clazz) { "error with state: $callStack, $currentCall, $clazz" } + + diagram.appendLine("$clazz --> ${currentCall.first}") + diagram.appendLine("deactivate $clazz") + } + // TODO: parse: No need to create transaction for + } + } + require(callStack.isEmpty()) { "Not all calls were accounted for. There is a bug in the script." } + diagram.appendLine("@enduml") +} + +println(diagram.toString()) + +fun StringBuilder.appendCall( + lineNo: Int, + from: String, + to: String, + clazz: String, + method: String, + symbols: List = listOf(), +): StringBuilder { + val syms = if (symbols.isNotEmpty()) symbols.joinToString(separator = "", postfix = " ") { "<&$it>" } else "" + return this.appendLine("$from -> $to: [$lineNo]\\n$syms$clazz\\n.$method") +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5bc20ba60..eab9f375f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,7 @@ include( "common:testing", "common:serialization", "common:neo4j-dsl", + "common:spring-data", "common:spring-webmvc", "common:identifiers", "common:pagination", @@ -22,7 +23,7 @@ include( ) include( "migrations:liquibase", - "migrations:neo4j-migrations" + "migrations:neo4j-migrations", ) include("keycloak") include("testing:kotest") @@ -93,7 +94,7 @@ include( "profiling:profiling-core-services", "profiling:profiling-ports-output", "profiling:profiling-adapter-output", - "profiling:profiling-adapter-output-spring-data-neo4j-sdn6" + "profiling:profiling-adapter-output-spring-data-neo4j-sdn6", ) include( "statistics:statistics-core-model", diff --git a/statistics/statistics-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/statistics/adapter/output/neo4j/configuration/StatisticsNeo4jConfiguration.kt b/statistics/statistics-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/statistics/adapter/output/neo4j/configuration/StatisticsNeo4jConfiguration.kt index 502d12946..061648e8b 100644 --- a/statistics/statistics-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/statistics/adapter/output/neo4j/configuration/StatisticsNeo4jConfiguration.kt +++ b/statistics/statistics-adapter-output-spring-data-neo4j-sdn6/src/main/kotlin/org/orkg/statistics/adapter/output/neo4j/configuration/StatisticsNeo4jConfiguration.kt @@ -2,7 +2,9 @@ package org.orkg.statistics.adapter.output.neo4j.configuration import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.context.annotation.Configuration +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories @Configuration -@EntityScan("org.orkg.statistics.adapter.output.neo4j.internal") +@EnableNeo4jRepositories("org.orkg.statistics.adapter.output.neo4j", transactionManagerRef = "neo4jTransactionManager") +@EntityScan("org.orkg.statistics.adapter.output.neo4j") class StatisticsNeo4jConfiguration diff --git a/testing/spring/build.gradle.kts b/testing/spring/build.gradle.kts index 2f18f7ceb..beb78153f 100644 --- a/testing/spring/build.gradle.kts +++ b/testing/spring/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { testFixturesApi("org.springframework:spring-core") testFixturesApi("org.springframework:spring-test") testFixturesApi("org.springframework.data:spring-data-commons") + testFixturesApi("org.springframework.data:spring-data-neo4j") testFixturesApi("com.fasterxml.jackson.core:jackson-databind") testFixturesApi("org.springframework.restdocs:spring-restdocs-mockmvc") testFixturesApi("org.testcontainers:junit-jupiter") @@ -34,4 +35,6 @@ dependencies { testFixturesImplementation("org.springframework.security:spring-security-crypto") testFixturesImplementation("org.springframework:spring-web") testFixturesImplementation("org.testcontainers:testcontainers") + testFixturesApi("org.neo4j.driver:neo4j-java-driver") + testFixturesImplementation("org.springframework:spring-tx") } diff --git a/testing/spring/src/testFixtures/kotlin/org/orkg/testing/annotations/Annotations.kt b/testing/spring/src/testFixtures/kotlin/org/orkg/testing/annotations/Annotations.kt index 2c4109c95..75a0276a9 100644 --- a/testing/spring/src/testFixtures/kotlin/org/orkg/testing/annotations/Annotations.kt +++ b/testing/spring/src/testFixtures/kotlin/org/orkg/testing/annotations/Annotations.kt @@ -3,15 +3,23 @@ package org.orkg.testing.annotations import ac.simons.neo4j.migrations.springframework.boot.autoconfigure.MigrationsAutoConfiguration import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.reflect.KClass +import org.neo4j.driver.Driver import org.orkg.testing.MockUserId import org.orkg.testing.Neo4jContainerInitializer +import org.springframework.beans.factory.ObjectProvider import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.ImportAutoConfiguration +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Import import org.springframework.core.annotation.AliasFor +import org.springframework.data.neo4j.core.DatabaseSelectionProvider +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager import org.springframework.test.context.ContextConfiguration @SpringBootTest @@ -23,11 +31,25 @@ annotation class Neo4jContainerIntegrationTest @EnableAutoConfiguration @ContextConfiguration(initializers = [Neo4jContainerInitializer::class]) @ImportAutoConfiguration(MigrationsAutoConfiguration::class) +@Import(Neo4jTestConfiguration::class) annotation class Neo4jContainerUnitTest( @get:AliasFor(annotation = ContextConfiguration::class, attribute = "classes") - val classes: Array> = [] + val classes: Array> = [], ) +@TestConfiguration +class Neo4jTestConfiguration { + @Bean + fun neo4jTransactionManager( + driver: Driver, + databaseSelectionProvider: DatabaseSelectionProvider, + optionalCustomizers: ObjectProvider, + ): Neo4jTransactionManager = + Neo4jTransactionManager(driver, databaseSelectionProvider).also { transactionManager -> + optionalCustomizers.ifAvailable { customizer -> customizer.customize(transactionManager) } + } +} + /** * Annotation that helps to set up tests with PostgreSQL in TestContainers. *