Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data one to many mapping

I try to configure OneToMany and ManyToOne mapping in Spring Data project but have some issues.

So I have two entities: Employer and Project. One Employer could have many projects.

Entity classes:

Employer.java

@Entity
@Table (name="Employer")
public class Employer {

    @Id
    @SequenceGenerator(name="my_seq", sequenceName="GLOBAL_SEQUENCE")
    @GeneratedValue(strategy = GenerationType.SEQUENCE ,generator="my_seq")
    @Column (name="employer_id")
    private int id;

    @Column (name="name")
    private String name;

    @JoinColumn (name="employer_id")
    @OneToMany(mappedBy = "employer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Project> project = new HashSet<Project>();

    ......................
}   

Project.java

@Entity
@Table (name="Project")
public class Project {

    @Id
    @SequenceGenerator(name="my_seq", sequenceName="GLOBAL_SEQUENCE")
    @GeneratedValue(strategy = GenerationType.SEQUENCE ,generator="my_seq")
    @Column (name="project_id")
    private int id;

    @Column (name="name")
    private String name;

    @ManyToOne
    @JoinColumn(name="employer_id", nullable = false)
    private Employer employer;
    ......................
}

Repository classes:

public interface EmployerRepository extends JpaRepository<Employer, Integer> {
}

public interface ProjectRepository extends JpaRepository<Project, Integer> {
}

Services:

@Service
public class EmployerServiceImpl implements EmployerService {

    @Autowired
    private EmployerRepository employerRepository;

    @Override
    public List<Employer> getAllEmployers() {
        return employerRepository.findAll();
    }   
}

@Service
public class ProjectServiceImpl implements ProjectService {

    @Autowired
    private ProjectRepository projectRepository;

    @Override
    public List<Project> getAllProjects() {
        return projectRepository.findAll();
    }
}

Controller:

@Controller
public class MainController {

    private EmployerService employerService;

    private ProjectService projectService;

    @Autowired(required = true)
    @Qualifier(value = "employerService")
    public void setEmployerService(EmployerService employerService) {
        this.employerService = employerService;
    }

    @Autowired(required = true)
    @Qualifier(value = "projectService")
    public void setProjectService(ProjectService projectService) {
        this.projectService = projectService;
    }


    @RequestMapping(value="/employers", method=RequestMethod.GET)
    public @ResponseBody List<Employer> getEmployers(@RequestParam(value = "name", required = false) String name) {
        return employerService.getAllEmployers();        
    }
    ..............................
}

Employer table:

EMPLOYER_ID  .  NAME
......................
1            .  Google
2            .  Oracle
3            .  Facebook 

Project table:

PROJECT_ID .    NAME            . EMPLOYER_ID
.......................................
1          . Create web site    .  1
2          . Create reporting   .  2
3          . Create web service .  3
4          . Fixing web site    .  1        

I'm expecting something like this:

[{"id":1,"name":"Google","project":[{"id":1,"name":"Create web site"}, {"id":4,"name":"Fixing web site"}},
 {"id":2,"name":"Oracle","project":{"id":2,"name":"Create reporting"}},
 {"id":3,"name":"Facebook","project":{"id":3,"name":"Create web service"}}]

But getEmployers method from controller class return this one:

[{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site",
"employer":{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site",
"employer":{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site",
"employer":{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site",
"employer":{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site",
"employer":{"id":1,"name":"Oracle","project":[{"id":4,"name":"Fixing web site","employer":
...................

Please sorry if this question discussed many times but I didn't find any suitable answer.

Thanks in advance!

like image 656
Iurii Avatar asked Jul 01 '15 21:07

Iurii


People also ask

How do you define One-To-Many mapping in JPA?

The One-To-Many mapping comes into the category of collection-valued association where an entity is associated with a collection of other entities. Hence, in this type of association the instance of one entity can be mapped with any number of instances of another entity.

How do you use One-To-Many mapping?

One To Many Mapping in Hibernate. In simple terms, one to many mapping means that one row in a table can be mapped to multiple rows in another table. For example, think of a Cart system where we have another table for Items. A cart can have multiple items, so here we have one to many mapping.


1 Answers

You have a bi-directional association between Employers and Projects. Additionally, you have configured both sides of the association to be loaded EAGERly (@ManyToOne associations are fetched EAGERly by default and you have forced the @OneToMany side to be fetched EAGERly as well). Due to this configuration, when the serialization framework loads the Employers, it finds valid Projects that have back-links to the Employers and ends up getting stuck in a cyclic loop.

In order to get the result you want, you will have to mark the @ManyToOne side (on the Project entity) to be fetched LAZYly as @ManyTone(fetch = FetchType.LAZY).

like image 134
manish Avatar answered Sep 29 '22 05:09

manish