2017-10-15 192 views
0

我是jpaspring-data的新手,請原諒我對此事的無知。JPA ManyToMany映射問題(無法將相同的實體映射到另一個)

我有兩個實體,我無法將它們與多對多關係映射到對方:

部:

@Entity 
@Table(name = "Department") 
public class Department { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "id") 
    private Integer   id; 

    @Column(name = "name") 
    private String   name; 

    @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL }) 
    @JoinTable(name = "department_week_days", joinColumns = @JoinColumn(name = "department_id"), inverseJoinColumns = @JoinColumn(name = "week_day_id")) 
    private List<WeekDay> weekDays; 

    public Department(String name) { 
     this.name = name; 
     weekDays = new ArrayList<WeekDay>(); 
    } 

    protected Department() { 
    } 

    public void addWeekDay(WeekDay day) { 
     weekDays.add(day); 
     day.getDepartments().add(this); 
    } 

    // omitted setters and getters 

} 

平日:

@Entity 
public class WeekDay { 

    public static enum WeekDays { 

     MO("Monday", 1), TU("Tuesday", 2), WE("Wednesday", 3), TH("Thursday ", 4), FR("Friday", 5), SA("Saturday", 6), SU("Sunday", 7); 

     private String name; 
     private int  dayNum; 

     private WeekDays(String name, int dayNum) { 
      this.name = name; 
      this.dayNum = dayNum; 
     } 

     public int asInt() { 
      return dayNum; 
     } 

     @Override 
     public String toString() { 
      return name; 
     } 
    } 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "id") 
    private Integer    id; 

    @Column(name = "name") 
    private String    name; 

    @Transient 
    private int     numericOrder; 

    @Embedded 
    private WorkingHours  workingHours; 

    @Column(name = "minimumEmployees") 
    private Integer    minimumEmployees; 

    @Column(name = "maximumEmployees") 
    private Integer    maximumEmployees; 

    @ManyToMany(mappedBy = "weekDays") 
    private List<Department> departments; 

    public WeekDay(WeekDays day, WorkingHours workingHours, Integer minimumEmployees, Integer maximumEmployees) { 

     this.name = day.toString(); 
     this.numericOrder = day.asInt(); 
     this.workingHours = workingHours; 
     this.minimumEmployees = minimumEmployees; 
     this.maximumEmployees = maximumEmployees; 

     departments = new ArrayList<Department>(); 

    } 

    protected WeekDay() { 
    } 

    // omitted setters and getters 

} 

我的問題是,我不能添加相同WeekDay兩個不同Department的 例如:

WeekDay day = WeekDayFactory.create(WeekDayFactory.SA, WeekDayFactory.DAY_SHORT, 1, 2); 

Department d1 = new Department("d1"); 
d1.addWeekDay(day); 
repository.save(d1); 

Department d2 = new Department("d2"); 
d2.addWeekDay(day); 
repository.save(d2); 

上述不會讓我補充dayd2爲什麼呢?

如果我創建一個newWeekDay具有相同的值day它的工作原理:

WeekDay day = WeekDayFactory.create(WeekDayFactory.SA, WeekDayFactory.DAY_SHORT, 1, 2); 
WeekDay sameValuesAsDay = WeekDayFactory.create(WeekDayFactory.SA, WeekDayFactory.DAY_SHORT, 1, 2); 

Department d1 = new Department("d1"); 
d1.addWeekDay(day); 
repository.save(d1); 

Department d2 = new Department("d2"); 
d2.addWeekDay(sameValuesAsDay); 
repository.save(d2); 

顯然這會在WeekDay表,違背了ManyToMany映射的目的,因爲我可以再添加一個重複的行數據進入department表,它自己爲每個department

正如我剛開始所說的,我對如何正確地做到這一點沒有真正的想法,所以請原諒我的無知,並指出我正確的方向來解決我的問題。

編輯

堆棧跟蹤:

java.lang.IllegalStateException: Failed to execute CommandLineRunner 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:735) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:716) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:703) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:304) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    at com.kendaya.holidaysplaning.HolidaysPlaningApplication.main(HolidaysPlaningApplication.java:21) [classes/:na] 
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.kendaya.holidaysplaning.entities.WeekDay; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.kendaya.holidaysplaning.entities.WeekDay 
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:299) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.11.7.RELEASE.jar:na] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.7.RELEASE.jar:na] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at com.sun.proxy.$Proxy66.save(Unknown Source) ~[na:na] 
    at com.kendaya.holidaysplaning.HolidaysPlaningApplication.lambda$0(HolidaysPlaningApplication.java:39) [classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:732) [spring-boot-1.5.7.RELEASE.jar:1.5.7.RELEASE] 
    ... 6 common frames omitted 
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.kendaya.holidaysplaning.entities.WeekDay 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:765) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:758) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:431) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:363) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:278) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:109) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final] 
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] 
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] 
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at com.sun.proxy.$Proxy64.persist(Unknown Source) ~[na:na] 
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508) ~[spring-data-jpa-1.11.7.RELEASE.jar:na] 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91] 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91] 
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91] 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504) ~[spring-data-commons-1.13.7.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489) ~[spring-data-commons-1.13.7.RELEASE.jar:na] 
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.7.RELEASE.jar:na] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56) ~[spring-data-commons-1.13.7.RELEASE.jar:na] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.11.RELEASE.jar:4.3.11.RELEASE] 
    ... 17 common frames omitted 
+1

當你說「它不會讓'day'加入'd2'」時,你會得到什麼例外?發佈stacktrace以及。 – Ish

+0

@Ish我希望我的問題是微不足道的,我做錯了。我添加了堆棧跟蹤...希望它可以幫助 –

回答

2

替換代碼據我所看到的,映射是正確的,一切都應該按預期工作。但是,以下錯誤消息

產生的原因:org.hibernate.PersistentObjectException:通過堅持脫離實體:com.kendaya.holidaysplaning.entities.WeekDay

告訴你,你的day實例分離。可能第一個repository.save()調用是在單獨的事務中執行的,因此在調用save()後,持久化上下文被清除。你有兩個選擇來解決這個問題:

  • 要麼重讀day記錄第一save調用後持續第二部門實體之前,或

    Department d2 = new Department("d2"); 
    WeekDay day1 = repository.find(day.getId()); //!!! pseodocode 
    d2.addWeekDay(day1); 
    repository.save(d2); 
    
  • 執行都在同一的repository.save()電話交易,在這種情況下,day實例仍處於管理狀態。

+0

非常感謝,我需要明白這一點! 問題現在已解決: 'd1.addWeekDay(day); d2.addWeekDay(day); repository.save(Arrays.asList(d1,d2));' –

+0

不客氣。 – ujulu

0
Department d1 = new Department("d1"); 
d1.addWeekDay(day); 
repository.save(d1); 

Department d2 = new Department("d2"); 
d2.addWeekDay(sameValuesAsDay); 
repository.save(d2); 

Department d1 = new Department("d1"); 
d1.addWeekDay(day); 
d1 =repository.save(d1); 

Department d2 = new Department("d2"); 
d2.addWeekDay(sameValuesAsDay); 
d2 =repository.save(d2); 
-1

我覺得這個問題,Detached entity passed to persist error,接受的答案將幫助您瞭解什麼是根源。

總之,hibernate的save方法只接受瞬態對象(即用new運算符創建的對象)。當你用save保存對象時,那個對象不再在hibernate會話中(在你的case day對象中),所以它會拋出錯誤。嘗試使用saveOrUpdate方法或按照ujulu的建議,檢索日期對象,然後保留。