Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
129 views
in Technique[技术] by (71.8m points)

java - How to save a new entity that refers existing entity in Spring JPA?

Imagine the following models:

Employee:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "Emp_Id"), inverseJoinColumns = @JoinColumn(name = "Proj_id"))
private Set<Project> projects = new HashSet<Project>();

Project:

@ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<Employee>();

Now if I create a new employee that refers to an existing project and try to persist that employee, I get an error:

detached entity passed to persist: Project

I create the employee as follows:

public void createNewEmployee(EmployeeDTO empDTO) {

  Employee emp = new Employee();
  // add stuff from DTO, including projects

  repository.saveAndFlush(emp);  // FAILS
}

and I update existing ones like this:

public void updateEmployee(EmployeeDTO empDTO) {

   Employee emp = repository.findOne(empDTO.getId());
   // set stuff from DTO, including projects

   repository.saveAndFlush(emp);  // WORKS!
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I guess you're interacting with the repository without expanding the transaction boundaries appropriately. By default, the transaction (and thus session) boundary is at the repository method level. This causes the Project instance to be detached from the EntityManager, so that it cannot be included in a persist operation.

The solution here is to extend the transaction boundary to the client:

@Component
class YourRepositoryClient {

  private final ProjectRepository projects;
  private final EmployeeRepository employees;

  // … constructor for autowiring

  @Transactional
  public void doSomething() {
    Project project = projects.findOne(1L);
    Employee employee = employees.save(new Employee(project));
  }
}

This approach causes the Project instance stay a managed entity and thus the persist operation to be executed for the fresh Employee instance being handled correctly.

The difference with the two repository interactions is that in the second case you'll have a detached instance (has already been persisted, has an id set), where as in the first example you have a completely unmanaged instances that does not have an id set. The id property is what causes the repository to differentiate between calling persist(…) and merge(…). So the first approach will cause a persist(…) to be triggered, the second will cause a merge(…).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

Just Browsing Browsing

[2] html - How to create even cell spacing within a

1.4m articles

1.4m replys

5 comments

56.9k users

...