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

Categories

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

java - Quarkus: Duplicate entries inside the list<Course> of the same Entity object

I am currently working on a school project that allows the user to add audio courses to their library and listen to the lessons of each Course. I'll try to explain the problem with an example:

When I create a new course entity with my POST-Request, there are no lessons added in yet. After I created the course, I add lessons to the course also via POST-Request. So, my database (PostgreSQL) would have one entry in the course table, two entries in the lesson table and a reference table to connect those two (hibernate generated)

The problem comes when I try to add the course to the user. It doesn't throw a warning or expectation when it adds the course and the result after the merge of the user also delivers the expected result:

HTTP/1.1 200 OK
Content-Length: 674
Content-Type: application/json

{
  "courses": [
    {
      "description": "A memoir by the creator of NIKE",
      "id": 2,
      "lessons": [
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 1,
          "name": "TutorialNr2"
        },
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 2,
          "name": "TutorialNr1"
        }
      ],
      "name": "Shoe Dog - Phil Knight",
      "pictureUrl": "https://images.unsplash.com/photo-1556906781-9a412961c28c?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=334&q=80"
    }
  ],
  "email": "[email protected]",
  "id": 2,
  "password": "password",
  "subscriptions": [],
  "userName": "jane"
}

Response code: 200 (OK); Time: 80ms; Content length: 674 bytes

But after I make another request to get everything of the user, I get this instead:

{
  "courses": [
    {
      "description": "A memoir by the creator of NIKE",
      "id": 2,
      "lessons": [
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 1,
          "name": "TutorialNr2"
        },
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 2,
          "name": "TutorialNr1"
        }
      ],
      "name": "Shoe Dog - Phil Knight",
      "pictureUrl": "https://images.unsplash.com/photo-1556906781-9a412961c28c?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=334&q=80"
    },
    {
      "description": "A memoir by the creator of NIKE",
      "id": 2,
      "lessons": [
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 1,
          "name": "TutorialNr2"
        },
        {
          "audioUrl": "http://localhost:8080",
          "description": "A Tutorial on how to create your own Audally's",
          "duration": "00:12:54",
          "id": 2,
          "name": "TutorialNr1"
        }
      ],
      "name": "Shoe Dog - Phil Knight",
      "pictureUrl": "https://images.unsplash.com/photo-1556906781-9a412961c28c?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=334&q=80"
    }
  ],
  "email": "[email protected]",
  "id": 2,
  "password": "password",
  "subscriptions": [],
  "userName": "jane"
}

Response code: 200 (OK); Time: 50ms; Content length: 1247 bytes

It just displays the course times the number of lessons that the course has. in this case two lessons. if I were to add another lesson then it would go on like that even though there are no multiple references of this in the database.

Here's my code that I use:

@Path("/courses")
@Produces(APPLICATION_JSON)
@Transactional
@ApplicationScoped
public class CourseResource {

    @Inject
    CourseRepository courseRepository;

    @GET
    public List<Course> getAll(){
        return courseRepository.findAll().list();
    }

    @POST
    public Response addCourse(Course course){
        if(courseRepository.findAll().stream().anyMatch(course1 ->
                        course1.name.equals(course.name) &&
                        course1.description.equals(course.description) &&
                        course1.lessons.equals(course.lessons))){
            return Response
                    .status(406,"Course already exists!")
                    .build();
        }
        Course entry = new Course();
        entry.copyProperties(course);
        courseRepository.persist(entry);
        return Response.ok(entry).build();
    }
}

@Path("/lessons")
@Produces(APPLICATION_JSON)
@Transactional
@ApplicationScoped
public class LessonResource {
    @Inject
    LessonRepository lessonRepository;
    @Inject
    CourseRepository courseRepository;

    @POST
    @Path("addLessonsToCourse/{cid}")
    public Response addLessonToCourse(@PathParam("cid") Long cid,Lesson[] lessons){
        Course read = courseRepository.findById(cid);
        if(read == null)return Response.noContent().build();
        Arrays.stream(lessons)
                .forEach(l -> {
                    Lesson created = new Lesson();
                    created.copyProperties(l);
                    lessonRepository.persist(created);
                    read.addLessons(lessonRepository.findById(created.id));
                });
        courseRepository.getEntityManager().merge(read);
        return Response.ok(courseRepository.findById(cid)).build();
    }
}
package com.audally.backend.boundary;


import io.quarkus.security.identity.SecurityIdentity;
import org.jboss.resteasy.annotations.cache.NoCache;
import javax.annotation.security.RolesAllowed;
import com.audally.backend.control.CourseRepository;
import com.audally.backend.control.UserRepository;
import com.audally.backend.entity.Course;
import com.audally.backend.entity.User;
import org.jose4j.json.internal.json_simple.JSONObject;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.json.bind.annotation.JsonbTransient;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;

@Path("/users")
@Produces(APPLICATION_JSON)
@Transactional
@ApplicationScoped
public class UserResource {
    @Inject
    UserRepository userRepository;
    @Inject
    CourseRepository courseRepository;
    @JsonbTransient
    private JSONObject businessuser;

    @GET
    @Path("/{UserId}")
    public Response getUser(@PathParam("UserId") Long uid){
        return Response.ok(userRepository.findById(uid)).build();
    }

    @GET
    @Path("/{UserId}/courses")
    public Response getCoursesOfUser(@PathParam("UserId") Long uid){
        User user = userRepository.findById(uid);
        if(user == null){
            return Response
                    .status(202,"Course already exists in the User!")
                    .build();
        }
        businessuser = new JSONObject();
        businessuser.merge("courses",user.courses.stream().filter(distinctByKey(course -> course.name))
                .collect(Collectors.toList()),(o, o2) -> o = o2);
        return Response.ok(businessuser.get("courses")).build();
    }
    @POST
    @Path("{UserId}/courses/{CourseId}")
    public Response addCourseToUser(@PathParam("UserId") Long uid
            ,@PathParam("CourseId") Long cid){
        User user = userRepository.findById(uid);
        Course course = courseRepository.findById(cid);
        if(user.courses.contains(course)){
            return Response
                    .status(406,"Course already exists in the User!")
                    .build();
        }
        if(user == null){
            return Response
                    .status(204,"User was not found!")
                    .build();
        }
        else if(course == null){
            return Response
                    .status(204,"Course was not found!")
                    .build();
        }
        user.addCourses(course);
        userRepository.getEntityManager().merge(user);
        return Response.ok(userRepository.findById(uid)).build();
    }
    @POST
    @Transactional
    @Path("addUser")
    public Response addUser(User user){
        User entry = new User();
        if(userRepository.find("email",user.email).count() == 1){
            return Response
                    .status(406,"User email already exists!")
                    .build();
        }
        entry.copyProperties(user);
        userRepository.persist(entry);
        return Response.ok(entry).build();
    }

    public static <T> Predicate<T> distinctByKey(
            Function<? super T, ?> keyExtractor) {

        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

I hotfixed the problem by throwing a distinct (in the method: getCoursesOfUser) to every course that is the same in there but I just want to know if there is a better way to fix this problem?

For reference: All Repositories implement PanacheRepositoryBase<Enitity,Long> and have no further code added to them.

package com.audally.backend.entity;


import io.quarkus.hibernate.orm.panache.PanacheEntity;
import org.hibernate.validator.constraints.URL;

import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.transaction.Transactional;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "courses",schema = "audally")
public class 

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

1 Answer

0 votes
by (71.8m points)

Fixed the problem with a little help from my friends.

The problem for anyone wondering was, that I was using FetchType.Eager instead of Lazy Which resulted in the multiple Courses being shown. It probably pulled the course, each time a new lesson was added and that's how things ended up then.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...