How can I get name of Kids as result if I search on one of their Parents using JPA in Java?

Viewed 111

I was thinking to use @JoinTable to be able displaying the name of kid when I search on his parent but a little struggling with it.

The program stores kids and one of their parents (or both) and when I search the name of the parent the program should display the name of kid (or kids).

Two interfaces needed:

  • name of parents and kids (name of parent is unique, so in case if it already exists then we just insert the name of the kid (and its parent_id)
  • when I search on the name of the parent the program displays his/her kid (or kids).

  • PARENTS table:

ID (PK, NN, AI, int) 
          
NAME (NN, String)
      
  • KIDS table: 

ID (PK, NN, AI, int) 
          
NEV (NN, String)
           
PARENTS_ID (NN, int)

The program should use a FK and OneToMany, ManyToOne connection which uses search and save.

I would welcome any suggestions about what is the best practice to finish the code.

Parents.java

@Entity 
public class Parents {
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private long Id;
    @NotNull
    private String firstName;
    @NotNull
    private String lastName;
    @OneToMany(fetch = FetchType.LAZY, mappedBy="parents") 
    private List<Kids> kids;
    // constructor + getters & setters

Kids.java

@Entity
public class Kids {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long Id;
    @NotNull
    private String firstName;
    @NotNull
    private String lastName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parents_id")
    @NotNull
    private Parents parents;

    public Kids() {
    }

    public Kids(String firstName, String lastName, Parents parents) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
        this.parents = parents;
    }
    // getters & setters

data.sql

INSERT INTO Parents (first_name, last_name) VALUES ('Viz','Elek');
INSERT INTO Parents (first_name, last_name) VALUES ('Locsolok','Anna');
INSERT INTO Parents (first_name, last_name) VALUES ('Kezmuves','Kelemen');
INSERT INTO Kids (first_name, last_name, parents_id) VALUES ('Nagy','Zolika', (SELECT id FROM Parents WHERE last_name = 'Elek'));
INSERT INTO Kids (first_name, last_name, parents_id) VALUES ('Cserkesz','Misike', (SELECT id FROM Parents WHERE last_name = 'Elek'));
INSERT INTO Kids (first_name, last_name, parents_id) VALUES ('Mézesb','Ödönke', (SELECT id FROM Parents WHERE last_name = 'Anna'));

KidsRepo.java

public interface KidsRepo extends CrudRepository<Kids, Long> {
   List<Kids> findKidsByParentId(Parents Id);
}

HomeController.java

@Controller
public class HomeController {
    KidsRepo kidsRepository;

    @Autowired
    public void setKidsRepository(KidsRepo kidsRepository) {
        this.kidsRepository = kidsRepository;
    }
}

KidsService.java

@Service
public class KidsService {
    KidsRepo kidsRepository;
    ParentsRepo parentsRepository;

    @Autowired
    public void setKidsRepository(KidsRepo kidsRepository) {
        this.kidsRepository = kidsRepository;
    }
    @Autowired
    public void setParentsRepository(ParentsRepo parentsRepository) {
        this.parentsRepository = parentsRepository;
    }
    public List<Kids> getKids(Parents id) {
        return kidsRepository.findKidsByParentId(id);
    }
}
4 Answers

To get the list of kids by their parent id, you can write a query in KidsRepo as follows

@Query("select k from Kids k where k.parents.id = :parentId")
List<Kids> findKidsByParentId(Long parentId);

Create another Crud Repository that retrieves parents. Then you can call parent.getKids() when you have an object of class Parent.

You are using Spring Data.
So you could create your own method for this purpose:

public interface KidsRepository extends CrudRepository<Kids, Long> {
    List<Kids> findByParent_Id(Long id);
}

Also, you don't need to declare List<Kids> findAll(), this method will be available by default with other common CRUD operations.


UPDATED:

The program stores kids and one of their parents (or both)

You need to do some rewriting of your logic. For the beginning, I suggest just have one parent it will be simpler to understand (later, you could add second parent and relation will be many to many).

Your parent object don't need even to know about any kids. Only kids should store the reference to its parent. Also, some part of the code should be changed according to new details.

Parent class:

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String firstName;
    @NotNull
    private String lastName;
}

Parent controller:

@Controller
@AllArgsConstructor
public class ParentController {
    private KidService kidService;
    private ParentService parentService;

    /**
     * The program stores kids and one of their parents (or both)
     * when I search the name of the parent the program should display the name of kid (or kids).
     */
    private String findParent(@PathVariable String parentName, Model model) {
        Parent parent = parentService.findParent(parentName);
        List<Kid> kids = kidService.findAllKidsForParent(parentName);

        model.addAttribute("parent", parent);
        model.addAttribute("children", kids);
        // render to appropriate view
        return "home";
    }
}

Parent service:

@Service
@AllArgsConstructor
public class ParentService {
    private ParentRepository parentRepository;

    public Parent findParent(String parentName) {
        return parentRepository.findParentByFirstName(parentName);
    }
}

Parent repo:

public interface ParentRepository extends JpaRepository<Parent, Long> {
    Parent findParentByFirstName(String firstName);
}

Following you can find details for Kid.

Kid class:

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Kid {

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

    @NotNull
    private String firstName;
    @NotNull
    private String lastName;

    @NotNull
    @ManyToOne
    private Parent parent;
}

Kid service:

@Service
@AllArgsConstructor
public class KidService {
    private KidRepository kidRepository;

    public List<Kid> findAllKidsForParent(String parentName) {
        return kidRepository.findAllByParent_FirstName(parentName);
    }
}

Kid repo:

public interface KidRepository extends JpaRepository<Kid, Long> {
    List<Kid> findAllByParent_FirstName(String firstName);
}

Also, I use Lombok framework annotations there

Few suggestions about your code:

  • please format it. If you use Intellij you could use Ctrl + Alt + L
  • name the variable from the lowercase letter. id is valid and Id isn't
  • for Hibernate/JPA or other frameworks, ids from numbers should be Object types. Like Long, Integer. Not primitive types: long, int.

Your domain implementation not accept multiple parents but only one, hence you can't have two parents for one kid and your requirements isn't accepted. To implements multiple parents try to change the @OneToMany field in @ManyToMany and collection field (off topic suggestion).

To achieve the goal and find all kids name by own parents you can combine JPA Method name and Java 8 Stream api implementing in two steps.

1 - Put this method in your repository:

// find All kids by parent id (_ to enter into id of the parents is not necessary)
List<Kids> findAllByParentsId(long parentId);

2 - On your service use the repository method name and collect the names:

List<String> kidsName = kidsRepository.findAllByParentsId(42l).stream()
                 .map(Kids::getFirstName)
                 .collect(Collectors.toList());
Related