Java Quarkus:列表中存在重复条目<;课程>;同一实体对象的

Java Quarkus:列表中存在重复条目<;课程>;同一实体对象的,java,postgresql,rest,jpa,quarkus,Java,Postgresql,Rest,Jpa,Quarkus,我目前正在进行一个学校项目,该项目允许用户将音频课程添加到他们的图书馆,并收听每门课程的课程。我将试着用一个例子来解释这个问题: 当我使用POST请求创建一个新的课程实体时,还没有添加任何课程。 创建课程后,我还通过POST请求将课程添加到课程中。 因此,我的数据库(PostgreSQL)在课程表中有一个条目,在课程表中有两个条目 和一个连接这两者的参考表(hibernate生成) 当我尝试向用户添加课程时,问题就出现了。它在添加课程时不会抛出警告或期望,而在用户合并后的结果也会提供期望的结果:

我目前正在进行一个学校项目,该项目允许用户将音频课程添加到他们的图书馆,并收听每门课程的课程。我将试着用一个例子来解释这个问题:

当我使用POST请求创建一个新的课程实体时,还没有添加任何课程。 创建课程后,我还通过POST请求将课程添加到课程中。 因此,我的数据库(PostgreSQL)在课程表中有一个条目,在课程表中有两个条目 和一个连接这两者的参考表(hibernate生成)

当我尝试向用户添加课程时,问题就出现了。它在添加课程时不会抛出警告或期望,而在用户合并后的结果也会提供期望的结果:

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": "jane@doe.co",
  "id": 2,
  "password": "password",
  "subscriptions": [],
  "userName": "jane"
}

Response code: 200 (OK); Time: 80ms; Content length: 674 bytes
但在我再次请求获取用户的所有信息后,我得到的是:

{
  "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": "jane@doe.co",
  "id": 2,
  "password": "password",
  "subscriptions": [],
  "userName": "jane"
}

Response code: 200 (OK); Time: 50ms; Content length: 1247 bytes
它只显示课程乘以课程的课程数。在这种情况下,有两个教训。 如果我要添加另一个课程,那么它会像这样继续下去,即使在数据库中没有对此的多个引用

以下是我使用的代码:

@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();
    }
}

起初,我认为课程和课程是双向的,认为这导致了问题,我把它改成了单向的,因为只有课程知道课程和课程 只知道自己,但这并没有解决问题

package com.audally.backend.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import org.jboss.resteasy.spi.touri.MappedBy;

import javax.annotation.processing.Generated;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@Table(name = "users",schema = "audally")
public class User implements Serializable {
    @Id
    @SequenceGenerator(
            name = "userSequence",
            sequenceName = "user_id_seq",
            initialValue = 3,
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
            ,generator = "userSequence")
    public Long id;
    @NotNull
    @NotEmpty(message = "Not allowed to be empty!")
    public String userName;
    @NotNull
    @Email
    public String email;
    @NotNull
    @NotEmpty
    public String password;
    /*
    @NotNull
    public Date joinDate;
    */
    @OneToMany(mappedBy = "user")
    public List<Subscription> subscriptions = new ArrayList<Subscription>();
    @ManyToMany(fetch = FetchType.EAGER)
    public List<Course> courses = new ArrayList<Course>();
    public User(){ }

    public void copyProperties(User user) {
        this.email = user.email;
        this.subscriptions = user.subscriptions;
        this.courses = user.courses;
        this.userName = user.userName;
        this.password = user.password;
    }

    public void addCourses(Course courses) {
        this.courses.add(courses);
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Course> getCourses() {
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                ", subscriptions=" + subscriptions.toArray().toString() +
                ", courses=" + courses.toArray().toString() +
                '}';
    }
}

package com.audaly.backend.entity;
导入io.quarkus.hibernate.orm.panache.PanacheEntity;
导入org.jboss.resteasy.spi.touri.MappedBy;
导入javax.annotation.processing.Generated;
导入javax.persistence.*;
导入javax.validation.constraints.Email;
导入javax.validation.constraints.NotEmpty;
导入javax.validation.constraints.NotNull;
导入java.io.Serializable;
导入java.util.ArrayList;
导入java.util.Date;
导入java.util.List;
@实体
@表(name=“users”,schema=“audaly”)
公共类用户实现可序列化{
@身份证
@序列发生器(
name=“userSequence”,
sequenceName=“用户id顺序”,
初始值=3,
allocationSize=1
)
@生成值(
策略=GenerationType.IDENTITY
,generator=“userSequence”)
公共长id;
@NotNull
@NotEmpty(message=“不允许为空!”)
公共字符串用户名;
@NotNull
@电子邮件
公共字符串电子邮件;
@NotNull
@空空如也
公共字符串密码;
/*
@NotNull
公开日期;
*/
@OneToMany(mappedBy=“用户”)
公共列表订阅=新建ArrayList();
@ManyToMany(fetch=FetchType.EAGER)
public List courses=new ArrayList();
公共用户(){}
公共void copyProperties(用户){
this.email=user.email;
this.subscriptions=user.subscriptions;
this.courses=user.courses;
this.userName=user.userName;
this.password=user.password;
}
公共课程(课程){
本.课程.添加(课程);
}
公共字符串getUserName(){
返回用户名;
}
public void setUserName(字符串用户名){
this.userName=用户名;
}
公共字符串getEmail(){
回复邮件;
}
公用电子邮件(字符串电子邮件){
this.email=电子邮件;
}
公共字符串getPassword(){
返回密码;
}
public void setPassword(字符串密码){
this.password=密码;
}
公开课程名单{
返回课程;
}
公共课程(列出课程){
本课程=课程;
}
@凌驾
公共字符串toString(){
返回“用户{”+
“id=”+id+
“,userName='”+userName+'\''+
“,email='”+email+'\''+
,password=''+password+'\''+
“,subscriptions=“+subscriptions.toArray().toString()+
“,courses=“+courses.toArray().toString())+
'}';
}
}
如果我遗漏了什么或者有人想知道什么,我很乐意提供。
我只想要这个奇怪的虫子?离开,让我的代码完成它应该做的工作…

在朋友的帮助下解决了这个问题

对于任何想知道的人来说,问题是我使用的是FetchType.earge而不是Lazy
这导致显示了多个课程。每次增加一节新课时,它可能都会取消课程,这就是事情的结局。

欢迎收看StackOverflow!:)如果我理解正确的话,那么基本的问题是您正在执行多个
GET
请求,但每次的答案都不同——即课程重复。正确吗?thx^^,不一定,因此,当我执行加载到用户课程中的get请求时,它会显示JSON数组课程内的课程乘以该课程的课程数。换句话说。是和否?将课程添加到用户后会显示正确答案(返回response.ok(userRepository.findById(uid)).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;
    }
}

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 Course implements Serializable {
    @Id
    @SequenceGenerator(
            name = "courseSequence",
            sequenceName = "course_id_seq",
            initialValue = 4,
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
            ,generator = "courseSequence")
    public Long id;
    @NotNull
    public String name;
    @Size(max = 400)
    public String description;
    @URL
    public String pictureUrl;
    @OneToMany(fetch = FetchType.EAGER)
    public List<Lesson> lessons = new ArrayList<Lesson>();

    public Course(){

    }
    public void addLessons(Lesson lesson){
        this.lessons.add(lesson);
    }
    public List<Lesson> getLessons(){
        return this.lessons;
    }
    public void copyProperties(Course course) {
        this.name = course.name;
        this.lessons = course.lessons;
        this.description = course.description;
        this.pictureUrl = course.pictureUrl;
    }

}
package com.audally.backend.entity;

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

import javax.json.bind.annotation.JsonbDateFormat;
import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.*;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.sql.Time;
import java.time.LocalTime;

@Entity
@Table(name = "lessons",schema = "audally")
public class Lesson implements Serializable {
    @Id
    @SequenceGenerator(
            name = "lessonSequence",
            sequenceName = "lesson_id_seq",
            initialValue = 1,
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
            ,generator = "lessonSequence")
    public Long id;
    @NotNull
    public String name;
    @Size(max = 400)
    public String description;
    @URL
    public String audioUrl;
    @JsonbDateFormat("HH:mm:ss")
    public LocalTime duration;
    /*@ManyToOne
    @JsonbTransient
    @JoinColumn(name = "course_id")
    public Course course;
    */
    public void copyProperties(Lesson lesson) {
        this.name = lesson.name;
        this.duration = lesson.duration;
        this.audioUrl = lesson.audioUrl;
        this.description = lesson.description;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getAudioUrl() {
        return audioUrl;
    }

    public void setAudioUrl(String audioUrl) {
        this.audioUrl = audioUrl;
    }

    public LocalTime getDuration() {
        return duration;
    }

    public void setDuration(LocalTime duration) {
        this.duration = duration;
    }
/*
    public Course getCourse() {
        return course;
    }

    public void setCourse(Course course) {
        this.course = course;
    }*/
}
package com.audally.backend.entity;

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import org.jboss.resteasy.spi.touri.MappedBy;

import javax.annotation.processing.Generated;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@Table(name = "users",schema = "audally")
public class User implements Serializable {
    @Id
    @SequenceGenerator(
            name = "userSequence",
            sequenceName = "user_id_seq",
            initialValue = 3,
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.IDENTITY
            ,generator = "userSequence")
    public Long id;
    @NotNull
    @NotEmpty(message = "Not allowed to be empty!")
    public String userName;
    @NotNull
    @Email
    public String email;
    @NotNull
    @NotEmpty
    public String password;
    /*
    @NotNull
    public Date joinDate;
    */
    @OneToMany(mappedBy = "user")
    public List<Subscription> subscriptions = new ArrayList<Subscription>();
    @ManyToMany(fetch = FetchType.EAGER)
    public List<Course> courses = new ArrayList<Course>();
    public User(){ }

    public void copyProperties(User user) {
        this.email = user.email;
        this.subscriptions = user.subscriptions;
        this.courses = user.courses;
        this.userName = user.userName;
        this.password = user.password;
    }

    public void addCourses(Course courses) {
        this.courses.add(courses);
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Course> getCourses() {
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                ", subscriptions=" + subscriptions.toArray().toString() +
                ", courses=" + courses.toArray().toString() +
                '}';
    }
}