Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public final Optional<ConnectionStatus<C>> findConnectionStatus() {
public final <R> R execute(@NonNull ConnectionDefinition definition, @NonNull Function<ConnectionStatus<C>, R> callback) {
DefaultConnectionStatus<C> connection = getConnection(definition);
try {
if (logger.isDebugEnabled()) {
logger.debug("Executing with a connection: [{}]", connection);
}
setupConnection(connection);
return connection.propagate(() -> callback.apply(connection));
} finally {
Expand All @@ -114,6 +117,9 @@ public final <R> R execute(@NonNull ConnectionDefinition definition, @NonNull Fu
@NonNull
@Override
public DefaultConnectionStatus<C> getConnection(@NonNull ConnectionDefinition definition) {
if (logger.isDebugEnabled()) {
logger.debug("Getting a connection for a definition: [{}]", definition);
}
ConnectionStatus<C> existingConnection = findConnectionStatus().orElse(null);
return switch (definition.getPropagationBehavior()) {
case REQUIRED -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.micronaut.data.jdbc.h2.assignedid

import io.micronaut.context.ApplicationContext
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.data.annotation.Relation
import io.micronaut.data.annotation.Id
import io.micronaut.data.jdbc.annotation.JdbcRepository
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.repository.CrudRepository
import io.micronaut.data.jdbc.h2.H2TestPropertyProvider
import jakarta.annotation.Nullable
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import jakarta.validation.constraints.NotBlank

class AssignedUuidCascadePersistSpec extends Specification implements H2TestPropertyProvider {

@Shared @AutoCleanup ApplicationContext ctx = ApplicationContext.run(getProperties())

@Shared TenantRepository tenantRepository = ctx.getBean(TenantRepository)
@Shared RoleRepository roleRepository = ctx.getBean(RoleRepository)

def "should persist children with assigned UUIDs via cascade persist"() {
given:
def tenantId = UUID.randomUUID()
def t = new Tenant(id: tenantId, name: 'Acme', host: true, serviceProvider: true)
3.times { i ->
def r = new Role(id: UUID.randomUUID(), name: "Role${i}", description: 'test role')
t.addRole(r)
}

when:
tenantRepository.save(t)

then:
tenantRepository.findById(tenantId).present
roleRepository.countByTenantId(tenantId) == 3
}

}

@MappedEntity("tenant")
class Tenant {
@Id
UUID id
@NotBlank
String name
boolean host
boolean serviceProvider
@Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "tenant", cascade = Relation.Cascade.ALL)
List<Role> roles = []
void addRole(Role r) {
if (r != null) {
r.tenant = this
roles.add(r)
}
}
}

@MappedEntity("role")
class Role {
@Id
UUID id
@NotBlank
String name
@Nullable
String description
@Relation(Relation.Kind.MANY_TO_ONE)
Tenant tenant
}

@JdbcRepository(dialect = Dialect.H2)
interface TenantRepository extends CrudRepository<Tenant, UUID> {}

@JdbcRepository(dialect = Dialect.H2)
interface RoleRepository extends CrudRepository<Role, UUID> {
long countByTenantId(UUID tenantId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package io.micronaut.data.jdbc.h2.assignedid

import io.micronaut.context.ApplicationContext
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.Join
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.data.annotation.Relation
import io.micronaut.data.jdbc.annotation.JdbcRepository
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.repository.CrudRepository
import io.micronaut.data.jdbc.h2.H2TestPropertyProvider
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import jakarta.validation.constraints.NotBlank

class AssignedUuidManyToManyPersistSpec extends Specification implements H2TestPropertyProvider {

@Override
List<String> packages() {
return Arrays.asList(getClass().package.name)
}

@Shared @AutoCleanup ApplicationContext ctx = ApplicationContext.run(getProperties())

@Shared JdbcStudentRepository studentRepository = ctx.getBean(JdbcStudentRepository)
@Shared JdbcCourseRepository courseRepository = ctx.getBean(JdbcCourseRepository)

def "should persist join rows with assigned UUIDs via cascade persist and support update"() {
given:
def s = new Student(id: UUID.randomUUID(), name: 'Denis')
def c1 = new Course(id: UUID.randomUUID(), name: 'Math')
def c2 = new Course(id: UUID.randomUUID(), name: 'Physics')
// Ensure child entities exist once; join rows should be created via cascade
courseRepository.save(c1)
courseRepository.save(c2)
s.addCourse(c1)
s.addCourse(c2)

when:
studentRepository.save(s)
def s2 = studentRepository.findById(s.id).orElse(null)

then:
s2 != null
s2.courses*.id as Set == [c1.id, c2.id] as Set

when: "associate existing course with new student"
def s3 = new Student(id: UUID.randomUUID(), name: 'John')
s3.addCourse(c1)
studentRepository.save(s3)
def found = studentRepository.findById(s3.id).orElse(null)

then:
found != null
found.courses*.id as Set == [c1.id] as Set

when: "update a course and update student"
c1.name = 'Mathematics'
s3.courses = [c1]
studentRepository.update(s3)
def found2 = studentRepository.findById(s3.id).orElse(null)

then:
found2 != null
found2.courses*.name as Set == ['Mathematics'] as Set
}
}

@MappedEntity("student_assigned")
class Student {
@Id
UUID id
@NotBlank
String name
@Relation(value = Relation.Kind.MANY_TO_MANY, cascade = [Relation.Cascade.PERSIST, Relation.Cascade.UPDATE])
List<Course> courses = []
void addCourse(Course c) {
if (c != null) {
courses.add(c)
}
}
}

@MappedEntity("course_assigned")
class Course {
@Id
UUID id
@NotBlank
String name
@Relation(value = Relation.Kind.MANY_TO_MANY, mappedBy = "courses")
List<Student> students = []
}

@JdbcRepository(dialect = Dialect.H2)
interface JdbcStudentRepository extends CrudRepository<Student, UUID> {
@Join("courses")
Optional<Student> findById(UUID id)
}

@JdbcRepository(dialect = Dialect.H2)
interface JdbcCourseRepository extends CrudRepository<Course, UUID> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.micronaut.data.r2dbc.h2

import io.micronaut.core.annotation.Nullable
import io.micronaut.data.annotation.*
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.r2dbc.annotation.R2dbcRepository
import io.micronaut.data.repository.reactive.ReactorCrudRepository
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import reactor.core.publisher.Mono
import spock.lang.Specification

@MicronautTest(transactional = false)
class H2AssignedUuidCascadePersistSpec extends Specification implements H2TestPropertyProvider {

@Inject R2dbcTenantRepository tenantRepository
@Inject R2dbcRoleRepository roleRepository

void "should persist children with assigned UUIDs via cascade persist (R2DBC)"() {
given:
def tenantId = UUID.randomUUID()
def t = new Tenant(id: tenantId, name: 'Acme', host: true, serviceProvider: true)
(0..<3).each { i ->
def r = new Role(id: UUID.randomUUID(), name: "Role${i}", description: 'test role')
t.addRole(r)
}

when:
tenantRepository.save(t).block()

then:
tenantRepository.findById(tenantId).block() != null
roleRepository.countByTenantId(tenantId).block() == 3
}
}

@MappedEntity("r2_tenant")
class Tenant {
@Id
UUID id
String name
boolean host
boolean serviceProvider
@Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "tenant", cascade = Relation.Cascade.ALL)
List<Role> roles = []
void addRole(Role r) {
if (r != null) {
r.tenant = this
roles.add(r)
}
}
}

@MappedEntity("r2_role")
class Role {
@Id
UUID id
String name
@Nullable
String description
@Relation(Relation.Kind.MANY_TO_ONE)
Tenant tenant
}

@R2dbcRepository(dialect = Dialect.H2)
interface R2dbcTenantRepository extends ReactorCrudRepository<Tenant, UUID> {
}

@R2dbcRepository(dialect = Dialect.H2)
interface R2dbcRoleRepository extends ReactorCrudRepository<Role, UUID> {
Mono<Long> countByTenantId(UUID tenantId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.micronaut.data.r2dbc.h2

import io.micronaut.data.annotation.*
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.r2dbc.annotation.R2dbcRepository
import io.micronaut.data.repository.reactive.ReactorCrudRepository
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import reactor.core.publisher.Mono
import spock.lang.Specification

@MicronautTest(transactional = false)
class H2ManyToManyAssignedIdSpec extends Specification implements H2TestPropertyProvider {

@Inject
R2dbcStudentRepository studentRepository
@Inject
R2dbcCourseRepository courseRepository

void "persist and update many-to-many with assigned UUIDs (reactive)"() {
given:
def s = new Student(id: UUID.randomUUID(), name: 'Denis')
def c1 = new Course(id: UUID.randomUUID(), name: 'Math')
def c2 = new Course(id: UUID.randomUUID(), name: 'Physics')
// Pre-persist children with assigned IDs; cascade should only write join rows
courseRepository.save(c1).block()
courseRepository.save(c2).block()
s.addCourse(c1)
s.addCourse(c2)

when:
studentRepository.save(s).block()
def s2 = studentRepository.findById(s.id).block()

then:
s2 != null
s2.courses*.id as Set == [c1.id, c2.id] as Set

when:
def s3 = new Student(id: UUID.randomUUID(), name: 'John')
s3.addCourse(c1)
studentRepository.save(s3).block()
def found = studentRepository.findById(s3.id).block()

then:
found != null
found.courses*.id as Set == [c1.id] as Set

when:
c1.name = 'Mathematics'
s3.courses = [c1]
studentRepository.update(s3).block()
def found2 = studentRepository.findById(s3.id).block()

then:
found2 != null
found2.courses*.name as Set == ['Mathematics'] as Set
}
}

@MappedEntity("r2_student_assigned")
class Student {
@Id
UUID id
String name
@Relation(value = Relation.Kind.MANY_TO_MANY, cascade = [Relation.Cascade.PERSIST, Relation.Cascade.UPDATE])
List<Course> courses = []
void addCourse(Course c) { if (c != null) courses.add(c) }
}

@MappedEntity("r2_course_assigned")
class Course {
@Id
UUID id
String name
@Relation(value = Relation.Kind.MANY_TO_MANY, mappedBy = "courses")
List<Student> students = []
}

@R2dbcRepository(dialect = Dialect.H2)
interface R2dbcStudentRepository extends ReactorCrudRepository<Student, UUID> {
@Join("courses")
@Override
Mono<Student> findById(UUID id)
}

@R2dbcRepository(dialect = Dialect.H2)
interface R2dbcCourseRepository extends ReactorCrudRepository<Course, UUID> {
}
Loading
Loading