java - JPA ManyToMany 映射问题(无法将同一实体映射到另一个实体)

标签 java spring jpa spring-data many-to-many

我是jpaspring-data的新手,所以请原谅我对此事的无知。

我有两个实体,但无法通过多对多关系将它们相互映射:

部门:

@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);

上面的内容不允许我将day添加到d2为什么?

如果我创建一个与 day 具有相同值的 new WeekDay ,它会起作用:

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 中即可> 为每个部门列出它自己。

正如我一开始所说的,我不知道如何正确执行此操作,因此请原谅我的无知,并为我指出解决问题的正确方向。

编辑

堆栈跟踪:

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

最佳答案

据我所知,映射是正确的,一切都应该按预期工作。但出现以下错误信息

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.kendaya.holidaysplaning.entities.WeekDay

告诉您您的day实例已分离。可能第一个 repository.save() 调用是在单独的事务中执行的,以便在 save() 调用之后清除持久性上下文。您有两种选择来解决问题:

  • 在保存第二个部门实体之前,在第一次 save 调用后重新读取 day 记录,或者

    Department d2 = new Department("d2");
    WeekDay day1 = repository.find(day.getId()); //!!! pseodocode
    d2.addWeekDay(day1);
    repository.save(d2);
    
  • 在同一事务中执行两个 repository.save() 调用,在这种情况下,day 实例仍处于托管状态。

    <

关于java - JPA ManyToMany 映射问题(无法将同一实体映射到另一个实体),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46755996/

相关文章:

hibernate - 如何将 Java 8 LocalDateTime 与 JPA 和 Hibernate 一起使用

java - 将字符串属性映射到 JSONB

java - UserTransaction 和 EntityTransaction 的区别

java - 《Effective Java》一书中的问题(Joshua Bloch)

java - 从文件读取数组元素时出现 NullPointerException 错误 - Java

java tomcat6 - 添加数据时如何自动刷新页面?

java - 在 spring java 程序中获取消息 "The type org.aopalliance.aop.Advice cannot be resolved."

java - 如何将时间戳附加到 java.util.logging.FileHandler.pattern 的文件名

java - 如果一个线程改变并发集合之一中已有的对象,可以提供什么保证?

java - 无法让 Intellij 使用 AspectJ 编译器