ManyToMany 关系的非拥有部分在尝试通过它持久化实体时似乎不起作用

The non-owning part of ManyToMany relationship seems to not work when trying to persist entities through it

这可能看起来很长,但这是一个简单的问题。其中大部分是您不必阅读的标准样板代码,但我将其添加到此处以防我在学习教程时出错。

实际问题:我在粗体.

中指定

我正在与 2 个 table 建立多对多关系:subjectsstudents。我已经为 subjectsstudents 定义了数据库模式,并为多对多关系定义了单独的 table subject_student

table架构如下:

create table subjects
(
    id   int          not null auto_increment,
    name varchar(100) not null unique,
    primary key (id)
);


create table students
(
    id          int          not null auto_increment,
    name        varchar(100) not null,
    passport_id int          not null unique,
    primary key (id),
    foreign key (passport_id) references passports (id)
);


create table subject_student
(
    subject_id int not null,
    student_id int not null,
    primary key (subject_id, student_id),
    foreign key (subject_id) references subjects (id),
    foreign key (student_id) references students (id)
);

实体如下:

@Entity
@Table(name = "students")
public class Student {

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

    @Column(nullable = false, length = 100)
    private String name;

    @OneToOne(fetch = FetchType.LAZY)
    private Passport passport;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "subject_student",
            joinColumns = @JoinColumn(name = "student_id"), // join column on owning side
            inverseJoinColumns = @JoinColumn(name = "subject_id") // join column on other side
    )
    @ToString.Exclude
    private List<Subject> subjects = new ArrayList<>();

    // constructors
    
    public void addSubject(Subject subject) {
        subjects.add(subject);
    }

    public void removeSubject(Subject subject) {
        subjects.remove(subject);
    }
}

@Entity
@Table(name = "subjects")
public class Subject {

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

    @Column(nullable = false, length = 100, unique = true)
    private String name;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "subject")
    private List<Review> reviews = new ArrayList<>();

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "subjects")
    @ToString.Exclude
    private List<Student> students = new ArrayList<>();

    public Subject(String name) {
        this.name = name;
    }

    public void addReview(Review review) {
        reviews.add(review);
    }

    public void addStudent(Student student) {
        students.add(student);
    }
}

现在问题来了: 这里 Student 实体是拥有部分。

我想将学生添加到特定科目。所以我做了以下操作:

@Transactional
public class SubjectRepository {

    @PersistenceContext
    EntityManager em;
    
    public void addStudentsToSubject(int subjectId, List<Student> students) {

        Subject subject = findById(subjectId);

        students.forEach(student -> {

            subject.addStudent(student);

            student.addSubject(subject);

            em.persist(student);

        });

        em.persist(subject);
    }
}

但是当我 运行 代码时,它 运行 没问题,但最终 JPA 回滚。所以当我看到数据库时,加入的 table 没有新行。只有当我尝试将学生添加到主题时才会发生这种情况,即 尝试通过非拥有方 。正确的方法(给学生添加科目很好。这也和这段代码非常相似)。

这让我很困惑。

现在我有一个怀疑: 联接 table 是 subject_student,但根据惯例它应该是 student_subject。那是罪魁祸首吗?

或者是否有更深层次的原因导致这不起作用?

我也在此处添加驱动程序代码。

@Test
@Transactional
public void test_addStudentsToSubject() {

    // adding students to non-owning side subject

    int subjectId = 10_002;

    Student s1 = studentRepository.findById(20_002);
    Student s2 = studentRepository.findById(20_003);
    List<Student> students = Arrays.asList(s1, s2);

    subjectRepository.addStudentsToSubject(subjectId, students);
}

@Test
@Transactional
public void getSubjectsAndStudent() {

    int id = 10_002;
    Subject subject = subjectRepository.findById(id);

    log.info("Subject = {}", subject);

    List<Student> students = subject.getStudents();
    log.info("Subject = {}, taken students = {}", subject, students);
}

编辑:为第一次测试添加日志:

2021-12-27 22:54:02.015  INFO 12352 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Starting ManyToManyTests using Java 17 on Ahroo with PID 12352 (started by msi in E:\Code\Tutorials\jpa_hibernate)
2021-12-27 22:54:02.017  INFO 12352 --- [           main] s.l.j.r.relationship.ManyToManyTests     : No active profile set, falling back to default profiles: default
2021-12-27 22:54:02.551  INFO 12352 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-12-27 22:54:02.567  INFO 12352 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9 ms. Found 0 JPA repository interfaces.
2021-12-27 22:54:03.047  INFO 12352 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-12-27 22:54:03.089  INFO 12352 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.6.3.Final
2021-12-27 22:54:03.209  INFO 12352 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-12-27 22:54:03.322  INFO 12352 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-12-27 22:54:03.615  INFO 12352 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-12-27 22:54:03.636  INFO 12352 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2021-12-27 22:54:04.258  INFO 12352 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-12-27 22:54:04.335  INFO 12352 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-12-27 22:54:05.102  INFO 12352 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Started ManyToManyTests in 3.376 seconds (JVM running for 4.431)
2021-12-27 22:54:05.103  INFO 12352 --- [           main] s.l.j.JpaHibernateApplication            : ----------------------------------------------------------------------------------------------------------
2021-12-27 22:54:05.173  INFO 12352 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@2a225dd7 testClass = ManyToManyTests, testInstance = spring.learn.jpa_hibernate.repository.relationship.ManyToManyTests@155318b5, testMethod = test_addStudentsToSubject_Method2@ManyToManyTests, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@61eaec38 testClass = ManyToManyTests, locations = '{}', classes = '{class spring.learn.jpa_hibernate.JpaHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3d1cfad4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2e55dd0c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@625732, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@4e096385, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@793be5ca, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1554909b], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@41d53813]; rollback [true]
Hibernate: select student0_.id as id1_5_0_, student0_.name as name2_5_0_, student0_.passport_id as passport3_5_0_ from students student0_ where student0_.id=?
Hibernate: select student0_.id as id1_5_0_, student0_.name as name2_5_0_, student0_.passport_id as passport3_5_0_ from students student0_ where student0_.id=?
Hibernate: select subject0_.id as id1_7_0_, subject0_.name as name2_7_0_ from subjects subject0_ where subject0_.id=?
Hibernate: select subjects0_.student_id as student_1_6_0_, subjects0_.subject_id as subject_2_6_0_, subject1_.id as id1_7_1_, subject1_.name as name2_7_1_ from subject_student subjects0_ inner join subjects subject1_ on subjects0_.subject_id=subject1_.id where subjects0_.student_id=?
Hibernate: select subjects0_.student_id as student_1_6_0_, subjects0_.subject_id as subject_2_6_0_, subject1_.id as id1_7_1_, subject1_.name as name2_7_1_ from subject_student subjects0_ inner join subjects subject1_ on subjects0_.subject_id=subject1_.id where subjects0_.student_id=?
2021-12-27 22:54:05.435  INFO 12352 --- [           main] o.h.c.i.AbstractPersistentCollection     : HHH000496: Detaching an uninitialized collection with queued operations from a session: [spring.learn.jpa_hibernate.entity.relationship.Subject.students#10002]
2021-12-27 22:54:05.437  INFO 12352 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    482901 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    15821999 nanoseconds spent preparing 5 JDBC statements;
    14897401 nanoseconds spent executing 5 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2021-12-27 22:54:05.437  INFO 12352 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@2a225dd7 testClass = ManyToManyTests, testInstance = spring.learn.jpa_hibernate.repository.relationship.ManyToManyTests@155318b5, testMethod = test_addStudentsToSubject_Method2@ManyToManyTests, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@61eaec38 testClass = ManyToManyTests, locations = '{}', classes = '{class spring.learn.jpa_hibernate.JpaHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3d1cfad4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2e55dd0c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@625732, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@4e096385, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@793be5ca, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1554909b], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
2021-12-27 22:54:05.450  INFO 12352 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-12-27 22:54:05.452  INFO 12352 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-12-27 22:54:05.460  INFO 12352 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

第二次测试的日志(检索修改后的值):

22:56:51.185 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}
2021-12-27 22:56:51.463  INFO 12240 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Starting ManyToManyTests using Java 17 on Ahroo with PID 12240 (started by msi in E:\Code\Tutorials\jpa_hibernate)
2021-12-27 22:56:51.465  INFO 12240 --- [           main] s.l.j.r.relationship.ManyToManyTests     : No active profile set, falling back to default profiles: default
2021-12-27 22:56:51.996  INFO 12240 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-12-27 22:56:52.016  INFO 12240 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 12 ms. Found 0 JPA repository interfaces.
2021-12-27 22:56:52.507  INFO 12240 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-12-27 22:56:52.557  INFO 12240 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.6.3.Final
2021-12-27 22:56:52.683  INFO 12240 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-12-27 22:56:52.790  INFO 12240 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-12-27 22:56:53.099  INFO 12240 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-12-27 22:56:53.121  INFO 12240 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2021-12-27 22:56:53.781  INFO 12240 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-12-27 22:56:53.857  INFO 12240 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-12-27 22:56:54.656  INFO 12240 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Started ManyToManyTests in 3.47 seconds (JVM running for 4.387)
2021-12-27 22:56:54.658  INFO 12240 --- [           main] s.l.j.JpaHibernateApplication            : ----------------------------------------------------------------------------------------------------------
2021-12-27 22:56:54.723  INFO 12240 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@2a225dd7 testClass = ManyToManyTests, testInstance = spring.learn.jpa_hibernate.repository.relationship.ManyToManyTests@37393dab, testMethod = getSubjectsAndStudent@ManyToManyTests, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@61eaec38 testClass = ManyToManyTests, locations = '{}', classes = '{class spring.learn.jpa_hibernate.JpaHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3d1cfad4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2e55dd0c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@625732, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@4e096385, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@793be5ca, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1554909b], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@4cbb11e4]; rollback [true]
Hibernate: select subject0_.id as id1_7_0_, subject0_.name as name2_7_0_ from subjects subject0_ where subject0_.id=?
2021-12-27 22:56:54.949  INFO 12240 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Subject = Subject(id=10002, name=History)
Hibernate: select students0_.subject_id as subject_2_6_0_, students0_.student_id as student_1_6_0_, student1_.id as id1_5_1_, student1_.name as name2_5_1_, student1_.passport_id as passport3_5_1_ from subject_student students0_ inner join students student1_ on students0_.student_id=student1_.id where students0_.subject_id=?
2021-12-27 22:56:54.951  INFO 12240 --- [           main] s.l.j.r.relationship.ManyToManyTests     : Subject = Subject(id=10002, name=History), taken students = [Student(id=20001, name=Adam)]
2021-12-27 22:56:54.976  INFO 12240 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    497999 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    18084498 nanoseconds spent preparing 2 JDBC statements;
    11998799 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2021-12-27 22:56:54.977  INFO 12240 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@2a225dd7 testClass = ManyToManyTests, testInstance = spring.learn.jpa_hibernate.repository.relationship.ManyToManyTests@37393dab, testMethod = getSubjectsAndStudent@ManyToManyTests, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@61eaec38 testClass = ManyToManyTests, locations = '{}', classes = '{class spring.learn.jpa_hibernate.JpaHibernateApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3d1cfad4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2e55dd0c, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@625732, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@4e096385, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@793be5ca, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1554909b], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
2021-12-27 22:56:54.989  INFO 12240 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-12-27 22:56:54.991  INFO 12240 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-12-27 22:56:54.999  INFO 12240 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

persist 仅用于保存新实体。如果你对数据库id做了findById,这意味着该实体已经存在于数据库中,所以你不需要持久化。

如果您尝试保留已创建的实体,您应该会得到一个 EntityExistsException 并且那一定是回滚的肇事者。

实际上,如果您将方法(或class)标记为@Transactional,这意味着在它结束时,如果没有异常,实体更改将持久保存在数据库中,而无需做其他事情。

如果实体已经在另一个持久化上下文(即 EntityManager 实例)中获得,您应该使用 merge 将它们附加到当前持久化上下文。这看起来是你 Student 列表的情况。

所以,您的方法应该如下所示:

    public void addStudentsToSubject(int subjectId, List<Student> students) {
        Subject subject = findById(subjectId);
        students.forEach(student -> {
            em.merge(student);
            subject.addStudent(student);
            student.addSubject(subject);
        });
    }

更新

Spring 测试默认回滚事务。只需将 @Rollback(false) 添加到您的 @Test 应该是 enough.You 也可以将其添加到 class 级别。