简体   繁体   English

单向ManyToMany只读关系

[英]Unidirectional ManyToMany readonly relationship

I have an entity Student and an entity Course . 我有一个实体学生和一个实体课程 One student can be associated to 0 or more Courses . 一名学生可以与0个或更多课程相关联。 Viceversa, one Course can have associated 0 or more Students . 课程 ,一门课程可以有0个或更多学生

Student entity: 学生实体:

@Data
@Entity(name = "student")
public class Student {

    @Id
    private Integer id;

    private String name;

    @ManyToMany(fetch = EAGER)
    @JoinTable(name = "student_course",
            joinColumns = @JoinColumn(
                    name = "studentId",
                    referencedColumnName = "id",
                    insertable = false,
                    updatable = false
            ),
            inverseJoinColumns = @JoinColumn(
                    name = "courseId",
                    referencedColumnName = "id",
                    insertable = false,
                    updatable = false)
    )
    private Collection<Course> courses;
}

Course entity: 课程实体:

@Data
@Entity(name = "course")
public class Course {

    @Id
    private Integer id;

    private String name;

    @ManyToMany(mappedBy = "courses")
    private Collection<Student> students;
}

and the inverse association in the entity Course . 和实体课程中的反向关联。

Both of those 2 @ManyToMany associations should be readonly. 这两个@ManyToMany关联都应该是readonly。 My problem is that when I try to save a Student Hibernate tries to update also the associated collection. 我的问题是,当我尝试保存学生时, Hibernate尝试更新相关的集合。

Here's what hibernate logs on a student update: 以下是hibernate登录学生更新的内容:

Hibernate: 
/* delete collection model.student.courses */ 
delete from `student_course` 
where `studentId`=?

Hibernate: 
/* insert collection row  */
insert into `student_course` (`studentId`, `courseId`) 
values (?, ?)

As you can see hibernate is trying to update also the table that stores the associations between the two entities. 正如你可以看到Hibernate是试图更新存储在两个实体之间的关联的表。 Those are the queries that I want to avoid. 这些是我想要避免的查询。

Student class: 学生班:

@ManyToMany(fetch = LAZY)
@JoinTable(
    name="STUDENT_COURSE"
    , joinColumns={
        @JoinColumn(name="STUDENT_ID")
        }
    , inverseJoinColumns={
        @JoinColumn(name="COURSE_ID")
        }
    )
private List<Course> courses;

Course class : 课程类:

@ManyToMany(mappedBy="courses")
private List<Student> students;

As you khnow, from the JPA 2.0 spec, the defaults fetch are: 正如您所知,从JPA 2.0规范中,默认提取是:

OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER

And in the latest version of hibernate fetch type is eager by deafult for all the mappings but if we are using JPA annotations then it aligns with JPA defaults. 在最新版本的hibernate中,fetch类型对于所有映射都非常渴望,但如果我们使用JPA注释,那么它与JPA默认值一致。

The issue is that a list is ordered whereas a set is not. 问题是list是有序的,而set则不是。 The generalized code for updating a list seems to assume that you wish to preserve that order and so it must delete the entire List of relations and reinsert it fresh to preserve the order through the relation PK ids. 用于更新列表的通用代码似乎假设您希望保留该顺序,因此它必须删除整个关系列表并重新插入它以通过关系PK ID保存顺序。 This would be explicitly true if you added an @OrderBy annotation on the join property. 如果在join属性上添加了@OrderBy注释,则显式为true。 In a set this is not necessary so it can just do an insert. 在一个集合中,这不是必需的,因此它可以只进行插入。

@Entity
@Data
@EqualsAndHashCode(of="id")
@NoArgsConstructor
public class E1 {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToMany
    Set<E2> e2s;

@Entity
@Data
@EqualsAndHashCode(of="id")
@NoArgsConstructor
public class E2 {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToMany(mappedBy="e2s")
    Set<E1> e1s;

And to use them: 并使用它们:

E1 e1 = new E1();
E2 e2 = new E2();
Set<E2> es2 = new HashSet<>();
es2.add(e2);
e1.setE2s(es2);

em.persist(e1);
em.persist(e2);
tx.commit();

// now modify the list
em.clear();

// fetch relations to avoid lazy fetch exception
e1 = em.createQuery("select e from E1 e left outer join fetch e.e2s where e.id = :id", E1.class)
        .setParameter("id", 1L)
        .getSingleResult();

tx.begin();
e2 = new E2();
e1.getE2s().add(e2);
em.persist(e2);
em.merge(e1);
tx.commit();

This gives me the following logs 这给了我以下日志

create table E1_E2 (e1s_id bigint not null, e2s_id bigint not null, primary key (e1s_id, e2s_id))
create table E1 (id bigint generated by default as identity (start with 1), primary key (id))
create table E2 (id bigint generated by default as identity (start with 1), primary key (id))
alter table E1_E2 add constraint FKky9vffxlkk0u9t0ynqfsvanrt foreign key (e2s_id) references E2
alter table E1_E2 add constraint FKgnbwe4qtab0mt1caqxrrp8gqd foreign key (e1s_id) references E1
insert into E1 (id) values (default)
insert into E2 (id) values (default)
insert into E1_E2 (e1s_id, e2s_id) values (?, ?)
select e1x0_.id as id1_2_0_, e2x2_.id as id1_4_1_, e2s1_.e1s_id as e1_3_0__, e2s1_.e2s_id as e2_3_0__ from E1 e1x0_ left outer join E1_E2 e2s1_ on e1x0_.id=e2s1_.e1s_id left outer join E2 e2x2_ on e2s1_.e2s_id=e2x2_.id where e1x0_.id=?
insert into E2 (id) values (default)
insert into E1_E2 (e1s_id, e2s_id) values (?, ?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM