我在使用雙向關系(OneToMany - ManyToOne)時遇到了性能問題。
我有一個Interaction可以有很多的物體ClassifiedGroup(有關更多詳細資訊,請參見下面的類),但我有一個特定的用例,我需要洗掉一些 ClassifiedGroups 并添加一些其他的。
問題是當我使用它InteractionRepository.java本身(一個簡單的 JpaRepository)來保存相關ClassifiedGroup的所有修改時,代碼將不得不從與互動鏈接的資料庫中獲取所有組。它還將獲取Interaction也可能很昂貴的許多其他屬性。我不想操縱Interaction物體來洗掉/創建新的ClassifiedGroup,但我也想要級聯型別,ALL因為我在創建互動時創建了第一個組,如果我洗掉互動本身,則應該洗掉所有組。
我試圖JpaRepository為ClassifiedGroup物體創建一個并直接操作它,但我在嘗試保存它時遇到錯誤:
代碼:
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
我發現了一些其他問題,如果我CascadeType.PERSIST在父物體中有 ,我將無法直接操作子物體,因為父物體將是子物體的“所有者”。
但我不得不問:有什么辦法可以避免這種性能瓶頸?我的互動物體有一些其他OneToOne的ManyToMany,ManyToOne和OneToMany關系,每次我使用時,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 = "";
}
分類組存盤庫
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>{}
我目前正在使用 Hibernate 5.6.5.Final。
uj5u.com熱心網友回復:
您的映射不正確 - 您需要查看有關如何映射派生 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 提供程式在收到關系中的空值時會自動知道我想要做什么,因為可能是資料沒有序列化,或者可能是更改。因此,在考慮性能的地方,我撰寫了自己的合并方法來處理接收序列化資料(例如來自 REST 介面),并確定如果存在的話,它應該如何合并到托管實體中。
另請注意,CascadeType.ALL 可能不是您真正想要的。您所說的只是拾取對 Interaction.groups 串列的更改,并使用 Interaction 洗掉參考的 ClassifiedGroups,并且作為一種說服力,您可以在一次呼叫中保留一個 Interaction 及其組串列。那只是cascade={PERSIST, REMOVE}. ALL 包括合并、重繪 和分離,它們有性能開銷,如果你真的不需要它們,可能會讓你感到悲傷。尤其是合并 - 當您在其組串列中傳遞具有更改和 ClassifiedGroups 的互動時,這些 ClassifiedGroups 與資料庫中的任何差異都將被合并。這可能會導致像過時資料覆寫這樣的后果,并且當然會有很大的開銷,因為合并也會級聯 ClassifiedGroup 參考。不要輕易使用級聯選項。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/445642.html
