简体   繁体   中英

JPA many to many Cascade issue

I have two entities and I want to remove the child from the parent set when the child is deleting. The delete function deletes the child entity but it doesn't remove the record from the ParentChild table.

So I have the following issue:

2021-01-04 11:31:35.601 ERROR 14264 --- [  XNIO-1 task-2] c.f.timesheet.web.rest.AppUserResource   : Exception in getAppUser() with cause = 'javax.persistence.EntityNotFoundException: Unable to find com.freemind.timesheet.domain.Job with id 3351' and exception = 'Unable to find com.freemind.timesheet.domain.Job with id 3351; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.freemind.timesheet.domain.Job with id 3351'

And I also have this one:

2021-01-04 11:31:35.598  WARN 14264 --- [  XNIO-1 task-2] o.h.e.loading.internal.LoadContexts      : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@55a124f7<rs=HikariProxyResultSet@372007568 wrapping org.postgresql.jdbc.PgResultSet@3901bf09>

Entities:

Job (child):

@ManyToMany(mappedBy = "jobs", cascade = CascadeType.REFRESH,fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@JsonIgnore
private Set<AppUser> appUsers = new HashSet<>();

...

 public void removeAppUsers() {
        if (this.appUsers.size() > 0) 
            for (AppUser ap : this.appUsers) {
                log.debug("Request to delete Job from User : {}", ap);
                ap.removeJob(this);
                log.debug("Request to delete Job from User : {}", ap);
            }
    }

AppUser(parent):

@ManyToMany(cascade = CascadeType.REFRESH,fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@JoinTable(
    name = "app_user_job",
    joinColumns = @JoinColumn(name = "internal_user_id"),
    inverseJoinColumns = @JoinColumn(name = "job_id", referencedColumnName = "id")
)
private Set<Job> jobs = new HashSet<>();

...

  public AppUser removeJob(Job job) {
        this.jobs.remove(job);
        job.getAppUsers().remove(this);
        return this;
    }

the service (@Service, @Transactionnal):

public void delete(Long id) {
    log.debug("Request to delete Job : {}", id);
    Job j=jobRepository.getOne(id);
    j.removeAppUsers();
    jobRepository.deleteById(id);
}

What Am I doing wrong? Thank you

I tested this using code shown below. Problem seems to be that you are not saving the AppUser instance.

AppUser class

package no.mycompany.myapp.misc;

import lombok.Data;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Data
@Entity
public class AppUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany
    @JoinTable(
            name = "app_user_job",
            joinColumns = @JoinColumn(name = "internal_user_id"),
            inverseJoinColumns = @JoinColumn(name = "job_id", referencedColumnName = "id")
    )
    private Set<Job> jobs = new HashSet<>();

    Job addJob(Job job) {
        job.getAppUsers().add(this);
        this.jobs.add(job);
        return job;
    }

    void removeJob(Job job) {
        this.jobs.remove(job);
    }
}

AppUser repo

package no.mycompany.myapp.misc;

import org.springframework.data.jpa.repository.JpaRepository;

public interface AppUserRepo extends JpaRepository<AppUser, Long> {}

Job class

package no.mycompany.myapp.misc;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
@Entity
public class Job {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany(mappedBy = "jobs")
    private Set<AppUser> appUsers = new HashSet<>();
}

Job repo

package no.mycompany.myapp.misc;

import org.springframework.data.jpa.repository.JpaRepository;

public interface JobRepo extends JpaRepository<Job, Long> {}

Tests

package no.mycompany.myapp.misc;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@DataJpaTest
public class AppUserRepoTest {

    @Autowired
    TestEntityManager testEntityManager;

    @Autowired
    AppUserRepo appUserRepo;

    @Autowired
    JobRepo jobRepo;

    @Test
    public void test() {

        var appUser = testEntityManager.persist(createValidAppUser());

        var job1 = testEntityManager.persist(createValidJob());
        var job2 = testEntityManager.persist(createValidJob());
        var job3 = testEntityManager.persist(createValidJob());

        var appUserInDb = appUserRepo.getOne(appUser.getId());

        // add 3 jobs to AppUser instance
        var job1InDb = appUserInDb.addJob(jobRepo.getOne(job1.getId()));
        var job2InDb = appUserInDb.addJob(jobRepo.getOne(job2.getId()));
        appUserInDb.addJob(jobRepo.getOne(job3.getId()));
        appUserRepo.save(appUserInDb);

        // verify that AppUser instance has 3 jobs
        appUser = testEntityManager.find(AppUser.class, appUser.getId());
        assertThat(appUser.getJobs().size()).isEqualTo(3);

        // verify that job #1 instance has 1 AppUser instance
        job1 = testEntityManager.find(Job.class, job1.getId());
        assertThat(job1.getAppUsers().size()).isEqualTo(1);

        // remove 2 jobs from AppUser instance
        appUserInDb = appUserRepo.getOne(appUser.getId());
        appUserInDb.removeJob(job1InDb);
        jobRepo.delete(job1InDb);

        appUserInDb.removeJob(job2InDb);
        jobRepo.delete(job2InDb);

        appUserRepo.save(appUserInDb);

        // verify that AppUser instance has 1 job
        appUser = testEntityManager.find(AppUser.class, appUser.getId());
        assertThat(appUser.getJobs().size()).isEqualTo(1);

        // verify that job instances are deleted in db
        assertThat(testEntityManager.find(Job.class, job1.getId())).isNull();
        assertThat(testEntityManager.find(Job.class, job2.getId())).isNull();
    }

    private static AppUser createValidAppUser() {
        return new AppUser();
    }

    private static Job createValidJob() {
        return new Job();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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