使用Cascade ALL操作OneToMany关系的子实体时出现问题



我在使用双向关系(OneToMany-ManyToOne(时遇到性能问题。

我有一个实体Interaction,它可以有许多ClassifiedGroup(有关更多详细信息,请参阅下面的类(,但我有一种特定的用例,需要删除一些ClassifiedGroup,并添加一些其他的ClassifiedGroups。

问题是,当我使用InteractionRepository.java本身(一个简单的JpaRepository(来保存对相关ClassifiedGroup的所有修改时,代码将不得不从与交互链接的数据库中获取所有组。它还将获取Interaction的许多其他属性,这些属性也可能是昂贵的。我不想操作Interaction实体来删除/创建新的ClassifiedGroup,但我也想将级联类型设置为ALL,因为我在创建交互时创建第一个组,如果我删除交互本身,则所有组都应该删除。

我试图为ClassifiedGroup实体创建一个JpaRepository并直接操作它,但在保存它时遇到了错误:

代码:

List<ClassifiedGroup> toAdd = generate(); // generate list to add
toAdd.forEach(g -> g.setInteraction(interaction));
this.classifiedGroupRepository.saveAll(toAdd);

错误:

org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.util.UUID' to required type 'br.com.stilingue.smartcare.entities.Interaction' for property 'interaction'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.UUID' to required type 'br.com.stilingue.smartcare.entities.Interaction' for property 'interaction': no matching editors or conversion strategy found
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:595)
at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:609)
at org.springframework.beans.AbstractNestablePropertyAccessor.processLocalProperty(AbstractNestablePropertyAccessor.java:458)
at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:278)
at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:246)
at org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper.setPropertyValue(DirectFieldAccessFallbackBeanWrapper.java:75)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation$IdentifierDerivingDirectFieldAccessFallbackBeanWrapper.setPropertyValue(JpaMetamodelEntityInformation.java:367)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.getId(JpaMetamodelEntityInformation.java:175)
at org.springframework.data.repository.core.support.AbstractEntityInformation.isNew(AbstractEntityInformation.java:46)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.isNew(JpaMetamodelEntityInformation.java:246)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:596)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAll(SimpleJpaRepository.java:631)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:529)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:599)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at com.sun.proxy.$Proxy197.saveAll(Unknown Source)
at br.com.stilingue.smartcare.event.interaction.update.strategy.impl.InteractionStilingueArrayUpdateStrategy.updateInteraction(InteractionStilingueArrayUpdateStrategy.java:78)
at br.com.stilingue.smartcare.event.interaction.update.strategy.impl.InteractionStilingueArrayUpdateStrategy$$FastClassBySpringCGLIB$$b5a01170.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at br.com.stilingue.smartcare.event.interaction.update.strategy.impl.InteractionStilingueArrayUpdateStrategy$$EnhancerBySpringCGLIB$$6a4de66a.updateInteraction(<generated>)
at br.com.stilingue.smartcare.event.interaction.update.strategy.impl.InteractionStilingueArrayUpdateStrategyIntegrationTest.testShouldAddNewGroups(InteractionStilingueArrayUpdateStrategyIntegrationTest.java:114)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.util.UUID' to required type 'br.com.stilingue.smartcare.entities.Interaction' for property 'interaction': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590)
... 120 more

我发现了一些其他问题,即如果我在父实体中具有CCD_;所有者;子实体的。

但我不得不问:有什么方法可以避免这种性能瓶颈吗?我的交互实体还有一些其他的OneToOneManyToManyManyToOneOneToMany关系,每次我使用InteractionRepository#save时,所有关系都会从数据库中提取。我真的需要能够自己创建/删除ClassifiedGroup实体。

我的课程:

交互

package br.com.stilingue.smartcare.entities;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.cloud.spanner.hibernate.types.SpannerJsonType;
import java.sql.Timestamp;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
@Data
@Entity
@Accessors(chain = true)
@Table(
name = "interactions",
uniqueConstraints = {@UniqueConstraint(columnNames = {"pid", "channel", "universe_id"})})
public class Interaction {
@Id
@GeneratedValue
@Type(type = "uuid-char")
@Column(name = "interaction_id", nullable = false)
private UUID interactionId;
@Column(name = "pid", nullable = false)
private String pid;
@Enumerated(EnumType.STRING)
@Column(name = "channel", nullable = false)
private Channel channel;
@Column(name = "universe_id", nullable = false)
private Long universeId;
@Column(name = "post_date", nullable = false)
private Timestamp postDate;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(
mappedBy = "interaction",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<ClassifiedGroup> groups;
// many other relations/attributes not relevant for the issue, but they also are loaded when I use InteractionRepository#save method
// method when the Interaction is created the first time
public void setRelations() {
if (this.groups != null) {
this.groups.forEach(group -> group.setInteraction(this));
}
}
}

分类组

package br.com.stilingue.smartcare.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
@Data
@Entity
@NoArgsConstructor
@Accessors(chain = true)
@Table(name = "classified_groups")
@IdClass(ClassifiedGroup.PrimaryKeys.class)
public class ClassifiedGroup {
@Data
public static class PrimaryKeys implements Serializable {
private Interaction interaction;
private String descriptor;
private String value;
}
public ClassifiedGroup(String group, ClassificationType type, String descriptor) {
this.value = group;
this.type = type;
this.descriptor = descriptor;
}
public ClassifiedGroup(String group, ClassificationType type) {
this(group, type, "");
}
@Id
@ToString.Exclude
@EqualsAndHashCode.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "interaction_id",
referencedColumnName = "interaction_id",
insertable = false,
updatable = false)
private Interaction interaction;
@Enumerated(EnumType.STRING)
@Column(name = "type", nullable = false)
private ClassificationType type;
@Id
@Column(name = "value", nullable = false)
private String value;
@Id
@Column(name = "descriptor", nullable = false)
private String descriptor = "";
}

ClassifiedGroupRepository

package br.com.stilingue.smartcare.repositories;
import br.com.stilingue.smartcare.entities.ClassifiedGroup;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ClassifiedGroupRepository
extends JpaRepository<ClassifiedGroup, ClassifiedGroup.PrimaryKeys> {}

交互存储库

package br.com.stilingue.smartcare.repositories;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface InteractionRepository extends JpaRepository<Interaction, UUID>{}

我目前正在使用Hibernate5.6.5.Final

您的映射不正确-您需要查看关于如何映射派生id的JPA教程,但错误"告诉"您什么是错误的,并指向ClassifiedGroup.interaction映射。PK类必须使用交互的ID(UUID(,而不是交互实例本身。

public static class PrimaryKeys implements Serializable {
private UUID interaction;
private String descriptor;
private String value;
}

由于JPA需要将该值插入到定义的";interaction_id";fk,ClassifiedGroup中的映射应该是可写,类似于:

@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "interaction_id",
referencedColumnName = "interaction_id")
private Interaction interaction;

对于你的其他问题-是的,有很多方法可以处理你想要的东西,但这取决于你想投入多少工作。让关系变得懒惰可以避免它们被不必要地提取,但你必须考虑通过其他机制进行序列化,并对分离的实体进行更改。我不相信Spring/JPA提供者在接收到关系中的null值时会自动知道我想要做什么,因为可能是数据没有序列化,也可能是更改。因此,在性能问题上,我编写了自己的合并方法来处理接收序列化数据(比如REST接口(,并确定如果存在,应如何将其合并到托管实例中。

还要注意,CascadeType.ALL可能不是您真正想要的。您所说的只是对Interaction.groups列表的更改会被拾取,引用的ClassifiedGroups会随着Interaction一起删除,作为说服,这样您就可以在一次调用中保持Interaction及其组列表。这只是cascade={PERSIST, REMOVE}。ALL包括合并、刷新和分离,这些都会带来性能开销,如果你真的不需要它们,可能会让你感到悲伤。特别是合并-当您在其组列表中传递具有更改和ClassifiedGroups的交互时,这些ClassifiedGroup与数据库中的任何差异都将被合并。这可能会导致诸如过时数据覆盖之类的后果,当然还有很大的开销,因为合并也会级联到ClassifiedGroup引用上。不要轻易使用级联选项。

最新更新