【问题标题】:Unable to save data with Many to Many mapping with join table无法使用连接表的多对多映射保存数据
【发布时间】:2020-06-09 15:51:51
【问题描述】:

我有具有多对多映射的用户和课程实体。连接表Registration 包含两个额外的字段,因此需要它自己的表。当我尝试保存用户时,出现以下错误。我不知道我哪里错了。

Caused by: org.hibernate.PropertyAccessException: Could not set field value [2] value by reflection : [class com.classpath.edukart.model.UserCourseId.courseId] setter of com.classpath.edukart.model.UserCourseId.courseId
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:72) ~[hibernate-org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    ... 37 common frames omitted
Caused by: java.lang.NullPointerException: null
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57) ~[na:1.8.0_191]
    at sun.reflect.UnsafeLongFieldAccessorImpl.set(UnsafeLongFieldAccessorImpl.java:75) ~[na:1.8.0_191]
    at java.lang.reflect.Field.set(Field.java:764) ~[na:1.8.0_191]
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:52) ~[hibernate-core-5.4.15.Final.jar:5.4.15.Final]
    ... 89 common frames omitted

User

@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@EqualsAndHashCode(of = "userId")
@Builder
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = AUTO)
    private long userId;

    @Column(nullable = false)
    private String firstName;

    private String lastName;

    @Column(unique = true)
    private String emailAddress;

    @OneToMany(mappedBy = "user", cascade = ALL)
    private Set<Registration> courses;


    public void registerCourse(Course course){
        Registration registration = new Registration(this, course);
        if(course.getUsers() == null){
            course.setUsers(new HashSet<>());
        }
        if (this.courses == null){
            this.courses = new HashSet<>();
        }
        course.getUsers().add(registration);
        this.courses.add(registration);
    }
}

Course

@Entity
@Table(name = "courses")
@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = {"id", "name"})
@ToString(of = {"id", "name", "price","duration", "rating"})
public class Course {

    @Id
    @GeneratedValue(strategy = AUTO)
    private long id;

    private String name;

    private double price;

    private int duration;

    private double rating;

    @OneToMany(mappedBy = "course")
    private Set<Registration> users;
}

Registration

@Entity
@Table(name = "users_courses")
@Setter
@Getter
@EqualsAndHashCode(of = {"user", "course"})
public class Registration {

    @EmbeddedId
    private UserCourseId id;

    @ManyToOne(fetch = LAZY)
    @MapsId("userId")
    private User user;

    @ManyToOne(fetch = LAZY)
    @MapsId("courseId")
    private Course course;

    @Column(name = "registration_date")
    private LocalDate registeredDate;

    private int percentageCompleted;

    private Registration(){}

    public Registration(User user, Course course){
        this.user = user;
        this.course = course;
    }
}

UserCourseId

@Embeddable
@EqualsAndHashCode
public class UserCourseId implements Serializable {

    @Column(name="user_id")
    private long userId;

    @Column(name = "course_id")
    private long courseId;

    public UserCourseId(){}

    public UserCourseId (long userId, long courseId){
        this.userId = userId;
        this.courseId = courseId;
    }

    public long getUserId() {
        return userId;
    }

    public void setUserId(long userId) {
        this.userId = userId;
    }

    public long getCourseId() {
        return courseId;
    }

    public void setCourseId(long courseId) {
        this.courseId = courseId;
    }
}

客户端程序

@Override
@Transactional
public void run(String... args) {
    User johnDoe = User.builder()
                        .firstName("John")
                        .lastName("Doe")
                        .emailAddress("johndoe22@gmail.com")
                        .address(new HashSet<>()).build();


    Course java = Course.builder().name("Java").duration(24).price(15_000).build();
    Course python = Course.builder().name("Python").duration(14).price(25_000).build();

    johnDoe.registerCourse(java);
   // johnDoe.registerCourse(python);

    userRepository.save(johnDoe);


    log.info("Before saving to the db :: {}", johnDoe.getCourses());
}

【问题讨论】:

    标签: hibernate jpa spring-data-jpa many-to-many


    【解决方案1】:

    这是错误的:

    @Column(name="user_id")
    private long userId;
    
    @Column(name = "course_id")
    private long courseId;
    

    您不想要 user_id/course_id。你想要:

    @ManyToOne User user;
    @ManyToOne Course course;
    

    无意冒犯,但我只是在帮助您更快更痛苦地朝自己的脚开枪。由于您没有覆盖 equals(Object o) 和 hashCode() 这很快就会爆炸。

    【讨论】:

    • 但是我使用了 Lombok 包中的 EqualsAndHashCode。这不会照顾吗?
    • 简单的回答:不。更长的回答:equals的实现必须匹配数据库中的相等概念。因此,仅当两个实体映射到表中的同一行时才应将它们视为相等 - 或者实际上:如果它们具有相同的主键。这不是 (!) Lombok 默认所做的。也许有人可以编写一个 Lombok 扩展来处理 JPA Id,但 AFAIK 这样的扩展不存在。你艾米想看看这篇博文以更详细地解释这些问题:mdeinum.github.io/2019-02-13-Lombok-Data-Ojects-Arent-Entities
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    相关资源
    最近更新 更多