skip to Main Content

I have a springboot project and I use postgresql and hibernate.

I have several entities, 2 of them are below:

Company Entity:

@Entity
@Table
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Getter
@Setter
public class Company extends BaseDatedEntity {

   ....... fields

   @ManyToMany(
        fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST}
   )
   @JoinTable(
        name = "company_interest",
        joinColumns = {@JoinColumn(name = "company_id")},
        inverseJoinColumns = {@JoinColumn(name = "interest_id")}
   )
   private List<Interest> interests = new ArrayList<>();

}

Interest Entity:

@Entity
@Table
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
@Getter
@Setter
public class Interest extends BaseDatedEntity {
    @Column(unique = true)
    private String name;

    public boolean equals(Interest interest){
        return this.name.equals(interest.getName());
    }
}

When I post a new company, it also persists interest. This is wanted and expected result. And if I delete the specified company with delete request, my company information is removed from database along with the relations in company_interest join table. In Interest table, interest records are still there, because these are not related with the company itself. This is also expected and ok.

But when I add a new company again with the interest which is already recorded in the database, I get detached entity passed to persist exception.

1- I want to add company even if its interest is already in database, just connect it in join table.

2- If the interest is not in the db, then persist it in the Interest table.

Whole exception log:

14:44:50.427 [http-nio-8081-exec-4] WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.aedesium.domain.entity.company.Interest; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.aedesium.domain.entity.company.Interest]

I believe this has a simple solution. But I could not figure it out.

Thanks already.

2

Answers


  1. By calling persist (or Spring’s save) on a new Company which references existing Interest instances, because of the cascade persist option being used, your are essentially calling persist on those Interest instances. JPA does not allow calling persist on a detached (existing) entity, and requires providers to throw an exception indicating there is a problem.

    You need to determine what you want to do with the data in the Interest instances.

    1. If you want the data to be merged into the database, you should probably use the CascadeType.MERGE type on the relationship, and then switch to using a direct EntityManager.merge on the company instance. JPA will still insert the Company instance, but cascade the merge operation, so find that your Interest instances exist and merge the data into the database.
    2. If you don’t want what could be stale Interest data to overwrite what is in the database and are just looking to set the database relationship, don’t use cascadeType.Persist. JPA won’t persist Interest instances, and will only use the identity within those instances to set the relationship in the DB.

    Note though that you may want different semantics depending on what your application might be sending for Insterest data. Best practice (for me anyway) is to inspect the relationships per use case and handle instances directly. In some cases, this might mean iterating over the Interest data passed in through a Company, calling persist if required (if there is no ID), or reading them in and selectively merging partial values from the passed in copy to the managed instance, then setting the managed instance in the Company to be saved.

    Login or Signup to reply.
  2. Seems like you are missing the @Transactional annotation on your service method where you are dealing with repository actions. since the fetch type is set to lazy, session needs to be open in order for ORM to be aware of the other entities you are trying to access.

    All Entities are saved successfully because you have set a Cascade type on the parent entity and so it will ‘cascade’ that action to all child entities. You are not fetching anything at that moment, so there is no error you are experiencing afterwards.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search