Skip to content

Commit

Permalink
Allow to persist cascade for non-null embedded id (#1242)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed Dec 13, 2021
1 parent 6034511 commit fb4182c
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,9 @@ private <T> T cascadeEntity(T entity, RuntimePersistentEntity<T> persistentEntit
if (persisted.contains(child)) {
continue;
}
boolean hasId = childPersistentEntity.getIdentity().getProperty().get(child) != null;
if (!hasId && (cascadeType == Relation.Cascade.PERSIST)) {
RuntimePersistentProperty<Object> identity = childPersistentEntity.getIdentity();
boolean hasId = identity.getProperty().get(child) != null;
if ((!hasId || identity instanceof Association) && (cascadeType == Relation.Cascade.PERSIST)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cascading PERSIST for '{}' association: '{}'", persistentEntity.getName(), cascadeOp.ctx.associations);
}
Expand Down Expand Up @@ -283,7 +284,7 @@ private <T> T cascadeEntity(T entity, RuntimePersistentEntity<T> persistentEntit
JdbcEntitiesOperations<Object> op = new JdbcEntitiesOperations<>(childPersistentEntity, cascadeManyOp.children);
op.veto(persisted::contains);
RuntimePersistentProperty<Object> identity = childPersistentEntity.getIdentity();
op.veto(e -> identity.getProperty().get(e) != null);
op.veto(e -> identity.getProperty().get(e) != null && !(identity instanceof Association));

persistInBatch(connection,
cascadeManyOp.annotationMetadata,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package io.micronaut.data.jdbc.h2

import io.micronaut.context.ApplicationContext
import io.micronaut.core.annotation.NonNull
import io.micronaut.data.annotation.Embeddable
import io.micronaut.data.annotation.Join
import io.micronaut.data.annotation.MappedEntity
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.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.EmbeddedId
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
import javax.persistence.OneToMany

@MicronautTest
@H2DBProperties
class H2EmbeddedCascadeSpec extends Specification implements H2TestPropertyProvider {
@AutoCleanup
@Shared
ApplicationContext applicationContext = ApplicationContext.run(getProperties())

@Shared
@Inject
TemplateRepository templateRepository = applicationContext.getBean(TemplateRepository)

void "test embedded cascade"() {
when:
Template template = new Template()
template.name = "Template test"

Tag tag = new Tag()
TagPK tagPK = new TagPK()
tagPK.tag = "New tag"
tagPK.template = template
tag.id = tagPK

template.tags << tag

templateRepository.save(template)
template = templateRepository.findById(template.id).get()
then:
template
template.tags.size() == 1
}

}

@JdbcRepository(dialect = Dialect.H2)
interface TemplateRepository extends CrudRepository<Template, Long> {

@Join("tags")
@Override
Optional<Template> findById(Long aLong);
}

@MappedEntity
class Template {

@Id
@GeneratedValue
Long id

@NonNull
String name

@OneToMany(mappedBy = "id.template", cascade = CascadeType.ALL)
Set<Tag> tags = [] as Set

}

@MappedEntity
class Tag {

@EmbeddedId
TagPK id

}

@Embeddable
class TagPK implements Serializable {

@NonNull
@Column(name = "tag")
String tag

@ManyToOne
@JoinColumn(name = "template_id")
@Column(name = "template_id")
Template template

@Override
boolean equals(o) {
if (this.is(o)) {
return true
}
if (getClass() != o.class) {
return false
}

TagPK that = (TagPK) o

if (tag != that.tag) {
return false
}
return template == that.template
}

@Override
int hashCode() {
int result
result = tag.hashCode()
result = 31 * result + (template != null ? template.hashCode() : 0)
return result
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,11 @@ private <T> Mono<T> cascadeEntity(T en, RuntimePersistentEntity<T> persistentEnt
if (persisted.contains(child)) {
continue;
}
boolean hasId = childPersistentEntity.getIdentity().getProperty().get(child) != null;
RuntimePersistentProperty<Object> identity = childPersistentEntity.getIdentity();
boolean hasId = identity.getProperty().get(child) != null;

Mono<Object> childMono;
if (!hasId && (cascadeType == Relation.Cascade.PERSIST)) {
if ((!hasId || identity instanceof Association) && (cascadeType == Relation.Cascade.PERSIST)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cascading PERSIST for '{}' association: '{}'", persistentEntity.getName(), cascadeOp.ctx.associations);
}
Expand Down Expand Up @@ -291,7 +292,7 @@ private <T> Mono<T> cascadeEntity(T en, RuntimePersistentEntity<T> persistentEnt
R2dbcEntitiesOperations<Object> op = new R2dbcEntitiesOperations<>(childPersistentEntity, cascadeManyOp.children);
op.veto(persisted::contains);
RuntimePersistentProperty<Object> identity = childPersistentEntity.getIdentity();
op.veto(e -> identity.getProperty().get(e) != null);
op.veto(e -> identity.getProperty().get(e) != null && !(identity instanceof Association));

if (LOG.isDebugEnabled()) {
LOG.debug("Cascading PERSIST for '{}' association: '{}'", persistentEntity.getName(), cascadeOp.ctx.associations);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package io.micronaut.data.r2dbc.h2

import io.micronaut.context.ApplicationContext
import io.micronaut.core.annotation.NonNull
import io.micronaut.data.annotation.Embeddable
import io.micronaut.data.annotation.Join
import io.micronaut.data.annotation.MappedEntity
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.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.EmbeddedId
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
import javax.persistence.OneToMany

@MicronautTest
class H2EmbeddedCascadeSpec extends Specification implements H2TestPropertyProvider {
@AutoCleanup
@Shared
ApplicationContext applicationContext = ApplicationContext.run(getProperties())

@Shared
@Inject
TemplateRepository templateRepository = applicationContext.getBean(TemplateRepository)

void "test embedded cascade"() {
when:
Template template = new Template()
template.name = "Template test"

Tag tag = new Tag()
TagPK tagPK = new TagPK()
tagPK.tag = "New tag"
tagPK.template = template
tag.id = tagPK

template.tags << tag

templateRepository.save(template).block()
template = templateRepository.findById(template.id).blockOptional().get()
then:
template
template.tags.size() == 1
}

}

@R2dbcRepository(dialect = Dialect.H2)
interface TemplateRepository extends ReactorCrudRepository<Template, Long> {

@Join("tags")
@Override
Mono<Template> findById(Long aLong);
}

@MappedEntity
class Template {

@Id
@GeneratedValue
Long id

@NonNull
String name

@OneToMany(mappedBy = "id.template", cascade = CascadeType.ALL)
Set<Tag> tags = [] as Set

}

@MappedEntity
class Tag {

@EmbeddedId
TagPK id

}

@Embeddable
class TagPK implements Serializable {

@NonNull
@Column(name = "tag")
String tag

@ManyToOne
@JoinColumn(name = "template_id")
@Column(name = "template_id")
Template template

@Override
boolean equals(o) {
if (this.is(o)) {
return true
}
if (getClass() != o.class) {
return false
}

TagPK that = (TagPK) o

if (tag != that.tag) {
return false
}
return template == that.template
}

@Override
int hashCode() {
int result
result = tag.hashCode()
result = 31 * result + (template != null ? template.hashCode() : 0)
return result
}
}

0 comments on commit fb4182c

Please sign in to comment.