首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >事务在将实体从多个一方持久化时被回滚

事务在将实体从多个一方持久化时被回滚
EN

Stack Overflow用户
提问于 2022-11-12 08:57:47
回答 3查看 349关注 0票数 1

我在数据库里有个协会-

我想把数据保存在这样的表格里-

相应的JPA实体已经以这种方式建模(为了简单起见,省略了getters/setter)-

学生实体-

代码语言:javascript
运行
复制
@Entity 
@Table(name = "student")
public class Student {
    @Id
    @SequenceGenerator(name = "student_pk_generator", sequenceName = 
                              "student_pk_sequence", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 
                               "student_pk_generator")
    @Column(name = "student_id", nullable = false)
    private Long studentId;

    @Column(name = "name", nullable = false)
    private String studentName;

    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
    private Set<StudentSubscription> studentSubscription;
}

STUDENT_SUBSCRIPTION实体-

代码语言:javascript
运行
复制
@Entity
@Table(name = "student_subscription")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class StudentSubscription {
    @Id
    private Long studentId;

    @ManyToOne(optional = false)
    @JoinColumn(name = "student_id", referencedColumnName = "student_id")
    @MapsId
    private Student student;

    @Column(name = "valid_from")
    private Date validFrom;

    @Column(name = "valid_to")
    private Date validTo;
}

LIBRARY_SUBSCRIPTION实体-

代码语言:javascript
运行
复制
@Entity
@Table(name = "library_subscription", 
       uniqueConstraints = {@UniqueConstraint(columnNames = {"library_code"})})
@PrimaryKeyJoinColumn(name = "student_id")
public class LibrarySubscription extends StudentSubscription {

    @Column(name = "library_code", nullable = false)
    private String libraryCode;

    @PrePersist
    private void generateLibraryCode() {
        this.libraryCode = // some logic to generate unique libraryCode
    }

}

COURSE_SUBSCRIPTION实体-

代码语言:javascript
运行
复制
@Entity
@Table(name = "course_subscription", 
       uniqueConstraints = {@UniqueConstraint(columnNames = {"course_code"})})
@PrimaryKeyJoinColumn(name = "student_id")
public class CourseSubscription extends StudentSubscription {

    @Column(name = "course_code", nullable = false)
    private String courseCode;

    @PrePersist
    private void generateCourseCode() {
        this.courseCode = // some logic to generate unique courseCode
    }
}

现在,有一个学生实体已经持久化了id,比方说- 100。现在我想坚持这个学生的图书馆订阅。为此,我使用Spring存储库创建了一个简单的测试-

代码语言:javascript
运行
复制
@Test
public void testLibrarySubscriptionPersist() {
    Student student = studentRepository.findById(100L).get();
    StudentSubscription librarySubscription = new LibrarySubscription();
    librarySubscription.setValidFrom(//some date);
    librarySubscription.setValidTo(//some date);
    librarySubscription.setStudent(student);
    studentSubscriptionRepository.save(librarySubscription);
}

在运行这个测试的时候,我得到了异常-

代码语言:javascript
运行
复制
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.springboot.data.jpa.entity.Student; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.springboot.data.jpa.entity.Student

为了解决这个问题,我在测试中附加了一个@Transactional。这修正了分离实体的上述异常,但是实体StudentSubscription和LibrarySubscription不会被持久化到DB。事实上,事务正在回滚。

把这个异常记录在日志里-

代码语言:javascript
运行
复制
INFO 3515 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@35390ee3 testClass = SpringDataJpaApplicationTests, testInstance = com.springboot.data.jpa.SpringDataJpaApplicationTests@48a12036, testMethod = testLibrarySubscriptionPersist@SpringDataJpaApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@5e01a982 testClass = SpringDataJpaApplicationTests, locations = '{}', classes = '{class com.springboot.data.jpa.SpringDataJpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@18ece7f4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@264f218, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2462cb01, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@928763c, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7c3fdb62, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1ad282e0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]

现在我有几个问题-

  1. 为什么要分离实体异常。当我们从DB获取实体时,Spring必须使用entityManager来获取实体。获取的实体会自动附加到持久性上下文,对吗?

  1. 关于在测试上附加@ transaction,为什么事务要回滚,并且没有实体被持久化。我原以为这两个实体-- StudentSubscription和LibrarySubscription应该使用联合表继承方法持久化。

我试过很多东西,但没有运气。向JPA和Spring数据专家寻求帮助:-)

提前谢谢。

EN

Stack Overflow用户

回答已采纳

发布于 2022-11-15 08:07:44

事务正在回滚,因为测试正在测试方法中执行DB更新。如果事务包含任何更新DB,事务将自动回滚。这里还有使用事务的强制要求,因为只要获取了EntityManager实体,就会关闭它,因此要保持该打开状态,测试必须在事务上下文中。

如果我在我的测试用例中使用了testDB,那么spring可能就不会回滚这个更新了。

将设置一个H2 testDb并在那里执行相同的操作,并发布结果。

谢谢你们的快速帮助。:-)

票数 0
EN
查看全部 3 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74411600

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档