简体   繁体   English

Spring Data JPA多对多,带有额外的列

[英]Spring Data JPA Many to Many with extra column

I'm trying to do an many to many relation between two table. 我正在尝试在两个表之间建立多对多关系。 The relation contains extra information (a date). 该关系包含其他信息(日期)。 I'm trying to achieve that using Spring Data JPA and I'm testing using unit test. 我正在尝试使用Spring Data JPA实现这一目标,并且正在使用单元测试进行测试。 The test fail giving the error: 测试失败并给出错误:

org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ....JoinEntity with id ....JoinEntity@5934ca1e; nested exception is javax.persistence.EntityNotFoundException: Unable to find ....JoinEntity with id ....JoinEntity@5934ca1e

Here is my code: 这是我的代码:

Entity A: 实体A:

import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name= "tableA")
public class EntityA
{
   /** The primary key. */
   @Id
   private Integer id;

   private String name;

   @OneToMany(mappedBy="a")
   private Set<JoinEntity> bs;

   //... getters and setters
}

Entity B: 实体B:

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name= "tableB")
public class EntityB
{
   /** The primary key. */
   @Id
   private Integer id;

   private String name;

   //... getters and setters
}

Join entity: 加入实体:

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="joinTableAB")
@IdClass(ABId.class)
public class JoinEntity implements Serializable
{

   @Column(name = "join_date")
   private Date date;

   @Id
   @ManyToOne
   @JoinColumn(name = "a_id")
   private EntityA a;

   @Id
   @ManyToOne
   @JoinColumn(name = "b_id")
   private EntityB b;

   //... getters and setters
}

Repository for A: A的存储库:

import org.springframework.data.repository.CrudRepository;

public interface ARepository extends CrudRepository<EntityA,Integer>
{
}

Repository for B: B的存储库:

import org.springframework.data.repository.CrudRepository;

public interface BRepository extends CrudRepository<EntityB,Integer>
{
}

The simple test class: 简单的测试类:

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * Testing for AtmosphericConditionsRepository.
 */
@DataJpaTest
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ARepositoryTest.class})
public class ARepositoryTest
{
   @Autowired
   ARepository aRepository;

   @Autowired
   BRepository bRepository;

   @Test
   public void test()
   {
      EntityA a0 = new EntityA();
      a0.setId(0);
      a0.setName("a0");
      EntityA a1 = new EntityA();
      a1.setId(1);
      a1.setName("a1");

      a0 = aRepository.save(a0);
      a1 = aRepository.save(a1);

      EntityB b0 = new EntityB();
      b0.setId(0);
      b0.setName("b0");
      EntityB b1 = new EntityB();
      b1.setId(1);
      b1.setName("b1");

      b0 = bRepository.save(b0);
      b1 = bRepository.save(b1);

      Set<JoinEntity> joinEntities = new HashSet<>();
      JoinEntity je = new JoinEntity();
      je.setDate(new Date());
      je.setA(a0);
      je.setB(b0);
      joinEntities.add(je);
      a0.setBs(joinEntities);

      aRepository.save(a0);
   }

}

The class specified in @IdClass: @IdClass中指定的类:

import java.io.Serializable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

public class ABId implements Serializable
{
   private EntityA a;

   private EntityB b;

   public ABId()
   {
   }

   public ABId(EntityA pA, EntityB pB)
   {
      a = pA;
      b = pB;
   }

   public EntityA getA()
   {
      return a;
   }

   public EntityB getB()
   {
      return b;
   }

   @Override
   public boolean equals(Object pO)
   {
      if (this == pO)
      {
         return true;
      }
      if (pO == null || getClass() != pO.getClass())
      {
         return false;
      }

      ABId abId = (ABId) pO;

      if (a != null ? !a.equals(abId.a) : abId.a != null)
      {
         return false;
      }
      return b != null ? b.equals(abId.b) : abId.b == null;
   }

   @Override
   public int hashCode()
   {
      int result = a != null ? a.hashCode() : 0;
      result = 31 * result + (b != null ? b.hashCode() : 0);
      return result;
   }
}

Extended stacktrace: 扩展堆栈跟踪:

2017-10-18 08:30:48.696  INFO 6460 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@de3a06f testClass = ARepositoryTest, testInstance = ....ARepositoryTest@58a90037, testMethod = test@ARepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@76b10754 testClass = ARepositoryTest, locations = '{}', classes = '{interface ....ARepository, interface ....BRepository, class ....JoinEntity, class ....ABId, interface ....ARepository, interface ....BRepository, class ....JoinEntity, class ....ABId}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@4493d195, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@4e1d422d, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@5c8ff52f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3cb1ffe6, org.springframework.boot.test.context.ImportsContextCustomizer@274bc460, org.springframework.boot.test.context.SpringBootTestContextCustomizer@2c039ac6, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6b57696f, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@5e13f156]; rollback [true]
Hibernate: select entitya0_.id as id1_6_0_, entitya0_.name as name2_6_0_ from tablea entitya0_ where entitya0_.id=?
Hibernate: select entitya0_.id as id1_6_0_, entitya0_.name as name2_6_0_ from tablea entitya0_ where entitya0_.id=?
Hibernate: select entityb0_.id as id1_7_0_, entityb0_.name as name2_7_0_ from tableb entityb0_ where entityb0_.id=?
Hibernate: select entityb0_.id as id1_7_0_, entityb0_.name as name2_7_0_ from tableb entityb0_ where entityb0_.id=?
Hibernate: select joinentity0_.a_id as a_id2_2_0_, joinentity0_.b_id as b_id3_2_0_, joinentity0_.join_date as join_dat1_2_0_, entitya1_.id as id1_6_1_, entitya1_.name as name2_6_1_, entityb2_.id as id1_7_2_, entityb2_.name as name2_7_2_ from join_tableab joinentity0_ inner join tablea entitya1_ on joinentity0_.a_id=entitya1_.id inner join tableb entityb2_ on joinentity0_.b_id=entityb2_.id where joinentity0_.a_id=? and joinentity0_.b_id=?
2017-10-18 08:30:49.035  INFO 6460 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test context [DefaultTestContext@de3a06f testClass = ARepositoryTest, testInstance = ....ARepositoryTest@58a90037, testMethod = test@ARepositoryTest, testException = org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ....JoinEntity with id ....ABId@5a9ba131; nested exception is javax.persistence.EntityNotFoundException: Unable to find ....JoinEntity with id ....ABId@5a9ba131, mergedContextConfiguration = [MergedContextConfiguration@76b10754 testClass = ARepositoryTest, locations = '{}', classes = '{interface ....ARepository, interface ....BRepository, class ....JoinEntity, class ....ABId, interface ....ARepository, interface ....BRepository, class ....JoinEntity, class ....ABId}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@4493d195, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@4e1d422d, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@5c8ff52f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3cb1ffe6, org.springframework.boot.test.context.ImportsContextCustomizer@274bc460, org.springframework.boot.test.context.SpringBootTestContextCustomizer@2c039ac6, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6b57696f, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]].

org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ....JoinEntity with id ....ABId@d3513f0a; nested exception is javax.persistence.EntityNotFoundException: Unable to find ....JoinEntity with id ....ABId@d3513f0a

    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:389)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy93.save(Unknown Source)
    at ....ARepositoryTest.test(ARepositoryTest.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: javax.persistence.EntityNotFoundException: Unable to find ....JoinEntity with id ....ABId@d3513f0a
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:144)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:278)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:121)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:89)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1129)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1022)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:639)
    at org.hibernate.type.EntityType.resolve(EntityType.java:431)
    at org.hibernate.type.EntityType.replace(EntityType.java:330)
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:518)
    at org.hibernate.type.CollectionType.replace(CollectionType.java:663)
    at org.hibernate.type.TypeHelper.replace(TypeHelper.java:177)
    at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:401)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:203)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:840)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:822)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:827)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
    at com.sun.proxy.$Proxy85.merge(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:509)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at 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:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    ... 41 more

I missed the Cascade type on the collection :/. 我错过了集合上的Cascade类型:/。

After adding it: 添加后:

@OneToMany(mappedBy="a",cascade = CascadeType.ALL)
private Set<JoinEntity> bs;

It works fine now. 现在工作正常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM