0

Initial data. Specialty has many subjects.

Specialty.java

@Entity
@Table(name = "specialties")
public class Specialty implements Serializable {

    private Long id;
    private Set<Subject> subjects;

    @Id
    @GeneratedValue
    @Column(name = "specialty_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @OneToMany(mappedBy = "specialty")
    @Cascade(CascadeType.ALL)
    public Set<Subject> getSubjects() {
        return subjects;
    }

    public void setSubjects(Set<Subject> subjects) {
        this.subjects = subjects;
    }

    public void addSubject(Subject subject) {
        addSubject(subject, true);
    }

    public void addSubject(Subject subject, boolean set) {
        if (subject != null) {
            getSubjects().add(subject);
            if (set) {
                subject.setSpecialty(this, false);
            }
        }
    }

    public void removeSubject(Subject subject) {
        getSubjects().remove(subject);
        subject.setSpecialty(null);
    }
}

A specialty can't be null for a subject. I want it works next way: when I save/update/detach a subject the same actions have to be applied to a specialty. When I delete a subject nothing happens with a specialty.

Subject.java

@Entity
@Table(name = "subjects")
public class Subject implements Serializable {
    private Long id;
    private Specialty specialty;

    @Id
    @GeneratedValue
    @Column(name = "subject_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne
    @JoinColumn(name = "specialty_id")
    @Cascade({CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH})
    public Specialty getSpecialty() {
        return specialty;
    }

    public void setSpecialty(Specialty specialty) {
        setSpecialty(specialty, true);
    }

    public void setSpecialty(Specialty specialty, boolean add) {
        this.specialty = specialty;
        if (specialty != null && add) {
            specialty.addSubject(this, false);
        }
    }
}

I write an integration tests. generateSpecialty() and generateSubject() it's just the util methods.

@Before
public void initEntitities() {
    specialty = generateSpecialty();
    subject = generateSubject();
    subject.setSpecialty(specialty);

    subjectRepository.save(subject);
    entityManager.detach(subject);
}

@Test
public void testSaveSpecialtyViolate2of3UniqueField() {
    Subject subject1 = new Subject();
    subject1.setSemester(1);
    subject1.setUkrName("Тест матан UPD");
    subject1.setEngName("XXX");
    subject1.setCode("MT 23.O8");
    subject1.setCredit(6F);
    subject1.setSpecialty(specialty);

    subjectService.save(subject1);

    assertNotNull(subjectRepository.findOne(subject.getId()));
    assertNotEquals(subject.getUkrName(), subject1.getUkrName());
    assertEquals(subject.getSemester(), subject1.getSemester());
    assertEquals(subject.getSpecialty().getId(),
subject1.getSpecialty().getId());
    }

You can see the result of the test on this image.

Exception had been thrown here subjectService.save(subject1);

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: diploma.entity.Specialty

In start of the test the specialty is detached (you can see it on the image. I use entityManager.contains(specialty) to check it). I think the problem is in next: when Hibernate try to save the subject1 it firstly select the specialty from the db and the specialty changes its state to pesist. Do you have any ideas how could I fix it and why it happens?

I had applied the first advice from this link but it didn't help me

1 Answers1

0

I think it might be because you are reusing specialty in "subject1.setSpecialty(specialty)" and it probably got detached in your "initEntitities()" with the subject.

Simon Berthiaume
  • 643
  • 4
  • 11
  • I'm sorry, I wasn't right and you were. When I had removed CascadeType.Detach from the Specialty class It didn't help me. But when I REMOVED `entityManager.detach(subject)`, it helped me. As I understand the **subject** from the init method has detach state and the specialty is persist. But I don't understand why the subject is detach. Because the Specialty class has CascadType.All and when I get a specialty from a db all subjects for this specialty will be uploaded also (so I think they also have to have the persist state instead of detach). – Valery Didenko Feb 16 '18 at 09:35
  • Did you try to remove the CascadeType.DETACH from the Subject class since it's the one you are specifically detaching? – Simon Berthiaume Feb 16 '18 at 11:56
  • Yeah, I tried to remove the CascadeType.DETACH from the Subject class. Even If I remove it I have the same error. – Valery Didenko Feb 16 '18 at 19:01
  • Have you tried removing the "@Cascade(CascadeType.ALL)" from the Specialty class to see if it could have any impact? – Simon Berthiaume Feb 17 '18 at 12:24