簡體   English   中英

設置 JPA 實體的問題 (Spring Boot)

[英]Issues with setting up JPA entities (Spring Boot)

我正在做一個小型測試項目,它將數據持久化到一個簡單的數據庫中。 我在設置數據實體時遇到了困惑和問題,可以使用一些幫助。

我正在將其開發為 Spring Boot Java 項目,並使用 JPA 來實現持久性功能。 我對JPA不是很熟悉。

考慮以下模擬模式:

任務:

<simple data fields omitted>
collection of TaskNote items
collection of StateChangeHistory items
reference to a "parent" Task item (can be null)
collection of Task "child" items (may be empty)

任務注:

<simple data fields omitted>

狀態變化歷史:

<simple data fields omitted>

我不確定注釋和構建這些類以促進這種安排的正確方法。 我也不確定這些關系中的一些應該是單向的還是雙向的。 我看過有關建立單向和雙向關系的各種方法的文章,但我仍然感到困惑,不確定何時應使用特定方法。

這是我目前所擁有的:

任務.java:

@Entity
public class Task extends AbstractEntity {
  // Simple fields omitted.
  
  @OneToMany(mappedBy = "taskId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  private List<TaskNote> notes = new LinkedList<>();

  @OneToMany(mappedBy = "taskId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  private List<StateChangeHistory> stateHistory = new LinkedList<>();
  
  // Have not yet attempted to implement parent and children.
}

TaskNote.java:

@Entity
public class TaskNote extends AbstractEntity {
  // Simple fields omitted.
  
  @ManyToOne
  @JoinColumn(name = "taskId")
  private Task task;
}

StateChangeHistory.java:

@Entity
public class StateChangeHistory extends AbstractEntity {
  // Simple fields omitted.
  
  @ManyToOne
  @JoinColumn(name = "taskId")
  private Task task;  
}

抽象實體.java:

@MappedSuperclass
public abstract class AbstractEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;

  public Long getId() {
    return id;
  }
  
  // Other items omitted.
  
}

當我嘗試運行應用程序(此時仍然非常基本)只是為了看看會發生什么時,有大量的控制台輸出,但引起我注意的是以下內容:

...
[WARN ] 2020-11-19 14:06:17.747 [restartedMain] AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.mycompany.tasks.backend.entity.TaskNote.taskId in com.mycompany.tasks.backend.entity.Task.notes
...
[ERROR] 2020-11-19 14:06:17.794 [restartedMain] SpringApplication - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.mycompany.tasks.backend.entity.TaskNote.taskId in com.mycompany.tasks.backend.entity.Task.notes
...

不知道這一切意味着什么,但它與我的實體類有關。 我還想知道我的 POM 文件中是否缺少某些內容,或者是否缺少某種配置。 正如我所說,我一般不熟悉 Java 中的 JPA 或數據持久性。

基礎知識。 所以,就像我說的,你的問題沒有足夠的信息,因為我沒有得到這個問題。 但是,這是一個 2 分鍾的 Spring-Data-Jpa 課程。 一個抽象實體:

@MappedSuperclass
public abstract class AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    public Long getId() {
        return id;
    }
}

一個任務:

@Entity
public class Task extends AbstractEntity {
  @OneToMany(mappedBy = "task")
  List<TaskNote> notes;

  @OneToMany(mappedBy = "task")
  List<StateChangeHistory> stateHistory;
}

任務筆記

@Entity
public class TaskNote extends AbstractEntity {
  @ManyToOne
  Task task;
}

狀態變化歷史

@Entity
public class StateChangeHistory extends AbstractEntity {
  @ManyToOne
  Task task;  
}

application.properties 的一個非常重要的配置。

spring.jpa.show-sql=true

一個應用程序:

@Override
public void run(ApplicationArguments args) throws Exception {
    save();
    Task task = taskRepo.findById(1L).get();
    System.out.println("after findById");
    System.out.println(task);
    System.out.println("after print task");
    System.out.println(task.notes);
}
private void save() {
    // save ... 
    Task task = taskRepo.save(new Task());
    TaskNote taskNote = new TaskNote();
    taskNote.task = task;
    taskNoteRepo.save(taskNote);
    StateChangeHistory stateChangeHistory = new StateChangeHistory();
    stateChangeHistory.task = task;
    stateChangeHistoryRepo.save(stateChangeHistory);
}

一個輸出:

2020-11-19 15:06:19.114  INFO 2156 --- [           main] jpatest.JpatestApplication               : Started JpatestApplication in 1.798 seconds (JVM running for 2.252)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into task (id) values (?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into task_note (task_id, id) values (?, ?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into state_change_history (task_id, id) values (?, ?)
Hibernate: select task0_.id as id1_1_0_ from task task0_ where task0_.id=?
after findById
jpatest.Task@fd53053
after print task
2020-11-19 15:06:19.171  INFO 2156 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-11-19 15:06:19.185 ERROR 2156 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) [spring-boot-2.4.0.jar:2.4.0]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:785) [spring-boot-2.4.0.jar:2.4.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333) [spring-boot-2.4.0.jar:2.4.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) [spring-boot-2.4.0.jar:2.4.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) [spring-boot-2.4.0.jar:2.4.0]
    at jpatest.JpatestApplication.main(JpatestApplication.java:13) [classes/:na]
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: jpatest.Task.notes, could not initialize proxy - no Session

細節:

  1. 除非您需要更改字段名稱,否則無需更改字段名稱。
  2. 無需為@OneToMany關系初始化List ,因為它們實際上只是只讀查詢字段。 mappedBy注釋說,實體TaskNoteStateChangeHistoryowner的關系,只更改關系的所有者是堅持做。 如您所見,這些實體是顯式創建和持久化的。
  3. 默認情況下,當它是LAZY時,您不需要聲明LAZY
  4. 當它是LAZY時,除非您明確告訴 JPA 這樣做,否則不會讀取該關系,並且默認的JpaRepository接口中沒有任何內容可以為您執行此操作。
  5. 因此,當您嘗試使用LAZY只讀字段而未明確為其編寫查詢時,您將得到可怕的LazyInitializationException
  6. can be nullreference to a "parent" Task item (can be null)是混淆了我。 這是一個不能為空的外鍵。 請參閱 ACID 主體。
  7. cascade = CascadeType.ALL如果它不在關系的所有者上,則沒有任何意義,因此添加它對本示例沒有影響。
  8. 是的,這是真的,你可以玩弄@Join注釋,使cascade注釋做有趣的事情,但你不敢輕易嘗試的有趣的東西之前獲得基礎知識的手柄好得多。
  9. 希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM