具有嵌套复合主键实体的Spring Data JPA Persisting Entity本身是一个在持续时间分离的嵌套实体

时间:2015-03-01 01:57:49

标签: java spring jpa spring-data spring-data-jpa

我有三个jpa实体,我正在努力合作。 BoxProfile,BoxProfileItemAssignment和BoxItem所有代码如下所列。 BoxProfileItemAssignment有一个@EmbeddedId,使用@MapId来映射复合键。

BoxProfile有一组BoxProfileItemAssignments,分配是BoxItem和数量值。我希望能够在持久化新的BoxProfile的同时保留BoxProfileItemAssignments。 BoxProfileItemAssignments中的每个BoxItem在创建BoxProfile时已经被保留。

我使用spring数据JpaRepository接口来保存我的BoxProfile实体并通过服务层BoxProfileService访问repo。

当我尝试持久化一个新的BoxProfile实体时,由于一个分离的实体,我得到一个PersistenceException。我理解嵌套在我传入的BoxProfileItemAssignment实体中的BoxItem是分离的,但我不想对所述实体进行更改或更新我只是想使用它创建BoxProfileItemAssignment条目。

经过大量研究后,我似乎无法找到一个嵌套复合键实体持久化的示例,该实体本身具有嵌套实体。

如果有人能告诉我正确的注释组合是什么来实现我的目标,我将不胜感激。

  

javax.persistence.PersistenceException:org.hibernate.PersistentObjectException:传递给persist的分离实体:com.quadrimular.fyfe.fulfillment.domain.BoxItem           在org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)           在org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)           在org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)           在org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           在org.springframework.orm.jpa.SharedEntityManagerCreator $ SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:291)           在com.sun.proxy。$ Proxy53.persist(未知来源)           在org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:394)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           at org.springframework.data.repository.core.support.RepositoryFactorySupport $ QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:442)           at org.springframework.data.repository.core.support.RepositoryFactorySupport $ QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:427)           at org.springframework.data.repository.core.support.RepositoryFactorySupport $ QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           at org.springframework.transaction.interceptor.TransactionInterceptor $ 1.proceedWithInvocation(TransactionInterceptor.java:99)           at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)           at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor $ CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           在org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           在org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)           在com.sun.proxy。$ Proxy65.save(未知来源)           在com.quadrimular.fyfe.fulfillment.service.BoxProfileServiceImpl.addBoxProfile(BoxProfileServiceImpl.java:48)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           在org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)           在org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)           at org.springframework.transaction.interceptor.TransactionInterceptor $ 1.proceedWithInvocation(TransactionInterceptor.java:99)           at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:267)           at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)           在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)           在org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)           在com.sun.proxy。$ Proxy66.addBoxProfile(未知来源)           at com.quadrimular.fyfe.fulfillment.integration.ITBoxProfile.addBoxProfileDatabase(ITBoxProfile.java:96)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           at java.lang.reflect.Method.invoke(Method.java:606)           在org.junit.runners.model.FrameworkMethod $ 1.runReflectiveCall(FrameworkMethod.java:47)           在org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)           在org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)           在org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)           在org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)           在org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)           在org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)           在org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)           在org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)           在org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)           在org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)           在org.junit.runners.ParentRunner $ 3.run(ParentRunner.java:238)           在org.junit.runners.ParentRunner $ 1.schedule(ParentRunner.java:63)           在org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)           在org.junit.runners.ParentRunner.access $ 000(ParentRunner.java:53)           在org.junit.runners.ParentRunner $ 2.evaluate(ParentRunner.java:229)           在org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)           在org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)           在org.junit.runners.ParentRunner.run(ParentRunner.java:309)           在org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)           在org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)           在org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)           在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)           在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)           在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)           在org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)       引起:org.hibernate.PersistentObjectException:传递给persist的分离实体:com.quadrimular.fyfe.fulfillment.domain.BoxItem           在org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)           在org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)           在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)           在org.hibernate.jpa.event.internal.core.JpaPersistEventListener $ 1.cascade(JpaPersistEventListener.java:97)           在org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)           在org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)           在org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)           在org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)           at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:432)           at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265)           在org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)           at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:137)           在org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)           在org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)           在org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)           在org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)           在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)           在org.hibernate.jpa.event.internal.core.JpaPersistEventListener $ 1.cascade(JpaPersistEventListener.java:97)           在org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)           在org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)           在org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)           在org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379)           在org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319)           在org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296)           在org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)           在org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)           at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:460)           在org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:294)           在org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)           在org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)           在org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)           在org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)           在org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)           在org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)           在org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)           在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)           在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)           在org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)           ......还有72个

BoxProfile测试方法

@Test
    @ExpectedDatabase(value = "boxProfileData-add.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)
    public void addBoxProfileDatabase() throws Exception {
        BoxProfileItemAssignment itemAssignment = new BoxProfileItemAssignment.Builder(BOX_ITEM_ONE, new BigDecimal("2.88")).build();
        BoxProfile original = new BoxProfile.Builder("example 3").itemAssignments((new HashSet(Arrays.asList(itemAssignment)))).sizes(new HashSet(Arrays.asList(BOX_SIZE))).selected(true).sequencer(3).build();

        BoxProfile returned = boxProfileService.addBoxProfile(original);

        assertNotNull(returned);
        assertThat(returned.getId(), instanceOf(Long.class));
        assertNotNull(returned.getId());
    }

BoxProfileRepository.java

public interface BoxProfileRepository extends JpaRepository<BoxProfile, Long> {

}

BoxProfileServiceImpl.java

@Service
@Transactional("mainTransactionManager")
public class BoxProfileServiceImpl implements BoxProfileService {

    private static final Logger LOG = LoggerFactory
            .getLogger(BoxProfileServiceImpl.class);

    private BoxProfileRepository repo;
    private BoxItemService boxItemService;

    @Autowired
    public BoxProfileServiceImpl(BoxProfileRepository repo, BoxItemService boxItemService) {
        this.repo = repo;
        this.boxItemService = boxItemService;
    }

    @Transactional("mainTransactionManager")
    public BoxProfile addBoxProfile(BoxProfile boxProfile) {
        LOG.debug("Adding boxProfile with information: " + boxProfile);
        BoxProfile toReturn = repo.save(boxProfile);
        LOG.debug("BoxProfile id: " + toReturn);
        return toReturn;
    }
}

BoxProfile.java

    @Entity
    @Table
    public class BoxProfile implements Serializable {

        private static final long serialVersionUID = 9091824819977165224L;

        @Id
        @GeneratedValue
        private Long id;
        private String description;
        private boolean selected;
        private int sequencer;

        @ManyToMany(fetch = FetchType.EAGER)
        @JoinTable(name = "boxProfileSizes", joinColumns = { @JoinColumn(name = "BOX_PROFILE_ID", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "SIZE_ID", referencedColumnName = "id") })
        private Set<BoxSize> sizes;

        @OneToMany(mappedBy = "boxProfile", cascade={CascadeType.PERSIST, CascadeType.REMOVE}, fetch = FetchType.EAGER)
        private Set<BoxProfileItemAssignment> itemAssignments;

        // Modification times
        private Date creationTime;
        private Date modificationTime;

        @PreUpdate
        public void preUpdate() {
            setModificationTime(new Date());
        }

        @PrePersist
        public void prePersist() {
            Date now = new Date();
            setCreationTime(now);
            setModificationTime(now);
        }

        public BoxProfile() {
        }

        private BoxProfile(Builder b) {
            this.description = b.description;
            this.id = b.id;
            this.selected = b.selected;
            this.sequencer = b.sequencer;
            this.sizes = b.sizes;
        }

        public static class Builder {
            // Mandatory Fields
            private final String description;
            // Optional Fields
            private Long id = null;
            private boolean selected = false;
            private int sequencer = -1;

            private Set<BoxSize> sizes = new HashSet<BoxSize>();
            private Set<BoxProfileItemAssignment> itemAssignments = new HashSet<BoxProfileItemAssignment>();

            public Builder(String description) {
                this.description = description;
            }

            public Builder sequencer(int sequencer) {
                this.sequencer = sequencer;
                return this;
            }

            public Builder sizes(Set<BoxSize> sizes) {
                this.sizes = sizes;
                return this;
            }

            public Builder addSize(BoxSize size) {
                this.sizes.add(size);
                return this;
            }

            public Builder itemAssignments(
                    Set<BoxProfileItemAssignment> itemAssignments) {
                this.itemAssignments = itemAssignments;
                return this;
            }

            public Builder id(Long id) {
                this.id = id;
                return this;
            }

            public Builder selected(boolean selected) {
                this.selected = selected;
                return this;
            }

            public BoxProfile build() {
                BoxProfile boxProfile = new BoxProfile(this);
                // Add the new box profile to the box profile assigned fish.
                for (BoxProfileItemAssignment assignment : itemAssignments) {
                    assignment.setBoxProfile(boxProfile);
                }
                // Set the updated fish assignments on the box profile
                boxProfile.setItemAssignements(itemAssignments);

                return boxProfile;
            }
        }

        // Getters setters hashcode equals to string



    }

BoxProfileItemAssignment.java

@Entity
@Table(name = "BOX_PROFILE_ITEM")
public class BoxProfileItemAssignment implements Serializable{

    private static final long serialVersionUID = 3331165661732043732L;

    @EmbeddedId
    private BoxProfileItemAssignmentId id = new BoxProfileItemAssignmentId();

    @MapsId("boxProfileId")
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "BOX_PROFILE_ID", referencedColumnName = "id")
    private BoxProfile boxProfile;

    @MapsId("itemId")
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ITEM_ID", referencedColumnName = "id")
    private BoxItem item;

    private BigDecimal quantity;

    // Modification times
    private Date creationTime;
    private Date modificationTime;

    @PreUpdate
    public void preUpdate() {
        setModificationTime(new Date());
    }

    @PrePersist
    public void prePersist() {
        Date now = new Date();
        setCreationTime(now);
        setModificationTime(now);
    }

    private BoxProfileItemAssignment(Builder b){
        this.boxProfile = b.boxProfile;
        this.item = b.item;
        this.quantity = b.quantity;
        this.id = b.id;
    }

    public BoxProfileItemAssignment(){}

    public static class Builder {

        private final BoxItem item;
        private final BigDecimal quantity;

        private BoxProfile boxProfile;
        private BoxProfileItemAssignmentId id = new BoxProfileItemAssignmentId();

        public Builder(BoxItem item, BigDecimal quantity){
            this.item = item;
            this.quantity = quantity;
        }
        public Builder boxProfile(BoxProfile boxProfile){
            this.boxProfile = boxProfile;
            return this;
        }

        public Builder id(BoxProfileItemAssignmentId id){
            this.id = id;
            return this;
        }

        public BoxProfileItemAssignment build(){
            return new BoxProfileItemAssignment(this);
        }
    }

    // Getters setters hashcode equals to string




}

BoxProfileItemAssignmentId

@Embeddable
public class BoxProfileItemAssignmentId implements Serializable{

    private static final long serialVersionUID = -7936926474216068447L;

    @Column(name = "BOX_PROFILE_ID")
    private Long boxProfileId;
    @Column(name = "ITEM_ID")
    private Long itemId;



    public BoxProfileItemAssignmentId(){}

    private BoxProfileItemAssignmentId(Builder b){
        this.boxProfileId = b.boxProfileId;
        this.itemId = b.itemId;
    }
    public static class Builder{
        private final Long boxProfileId;
        private final Long itemId;

        public Builder(Long boxProfileId, Long itemId){
            this.boxProfileId = boxProfileId;
            this.itemId = itemId;
        }

        public BoxProfileItemAssignmentId build(){
            return new BoxProfileItemAssignmentId(this);
        }
    }

    // Getters setters hashcode equals to string




}

BoxItem.java

@Entity
@Table
public class BoxItem implements Serializable {

    private static final long serialVersionUID = -6146188094809573420L;

    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    private BoxItemType type;
    @NotNull
    private MeasurementUnit unit;
    @NotNull
    @Size(min=2, max=30)
    private String name;
    @NotNull
    private BigDecimal costPerUnit;

    // Modification times
    private Date creationTime;
    private Date modificationTime;

    @PreUpdate
    public void preUpdate() {
        modificationTime = new Date();
    }

    @PrePersist
    public void prePersist() {
        Date now = new Date();
        creationTime = now;
        modificationTime = now;
    }
    public BoxItem(){}

    private BoxItem(Builder b){
        this.type = b.type;
        this.name = b.name;
        this.costPerUnit = b.costPerUnit;
        this.id = b.id;
        this.unit = b.unit;
    }

    public static class Builder{
        private BoxItemType type;
        private MeasurementUnit unit;
        private String name;
        private BigDecimal costPerUnit;

        private Long id;

        public Builder(String name, BoxItemType type, MeasurementUnit unit, BigDecimal costPerUnit){
            this.name = name;
            this.type = type;
            this.unit = unit;
            this.costPerUnit = costPerUnit;
        }

        public Builder id(Long id){
            this.id = id;
            return this;
        }
        public BoxItem build(){
            return new BoxItem(this);
        }
    }

    // Getters setters hashcode equals to string


}

1 个答案:

答案 0 :(得分:3)

问题不是由您的映射造成的,而是由您处理现有&#39;实体。

正如您所说BOX_ITEM_ONE已经存在,但您在测试方法中使用的EntityManger不知道。

在您的情况下,您可能在testsetup期间持有BOX_ITEM_ONE,或者您使用find获取它,但使用的是与测试方法不同的EnittyManager,因此该对象仍然是新的&#39;到你的EnityManger,但至少EM认识到它是一个JPA管理的实体,所以你得到了超然的例外。

如果你手动&#39;使用DB中存在的id和属性创建了BOX_ITEM_ONE,你会得到错误&#39;无法使用相同的主键(或沿着这些行&#39;)进行INSERT,因为EM会尝试持久保存&#39; new object& #39;但已经设置了PrimaryKey。

简单地说,您需要通过将其添加到EM上下文来使EM知道BoxItem。这是合并方法,您只需调用BOX_ITME_ONE = EM.merge(BOX_ITEM_ONE),然后将其添加到新的BoxProfile中。 或者甚至更好,防止对象改变异常&#39;如果在此期间BoxItem已更新,请使用当前的EM找到该对象,BOX_ITEM_ONE = em.find(BobItem.class,BOX_ITEM_ONE.getID())。它不会发出新的sql语句,它只会从JPA上下文中获取对象,因此它不是性能问题。

最后,您可能希望在BoxProfile中的itemAssignments上添加orphanRemoval = true到您的OneToMany anotation,因为如果您从集合中删除它们可能会删除ItemsAssignments,因为它们没有意义他们自己的。