简体   繁体   English

设置 JPA 实体的问题 (Spring Boot)

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

I am working on a small test project that will persist data to a simple database.我正在做一个小型测试项目,它将数据持久化到一个简单的数据库中。 I am running into confusion and issues with setting up my data entities and could use some help.我在设置数据实体时遇到了困惑和问题,可以使用一些帮助。

I am developing this as a Spring Boot Java project and am using JPA for the persistence capabilities.我正在将其开发为 Spring Boot Java 项目,并使用 JPA 来实现持久性功能。 I am not very familiar with JPA.我对JPA不是很熟悉。

Consider the following mock schema:考虑以下模拟模式:

Task:任务:

<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)

TaskNote:任务注:

<simple data fields omitted>

StateChangeHistory:状态变化历史:

<simple data fields omitted>

I am not sure of the correct way to annotate and structure these classes to facilitate this arrangement.我不确定注释和构建这些类以促进这种安排的正确方法。 I am also not sure whether some of these relationships should be uni- or bi-directional in nature.我也不确定这些关系中的一些应该是单向的还是双向的。 I've seen articles about various ways to set up uni- and bi-directional relationships, but I'm still confused and not sure when a particular approach should be used.我看过有关建立单向和双向关系的各种方法的文章,但我仍然感到困惑,不确定何时应使用特定方法。

Here is what I have at this point:这是我目前所拥有的:

Task.java:任务.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: TaskNote.java:

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

StateChangeHistory.java: StateChangeHistory.java:

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

AbstractEntity.java:抽象实体.java:

@MappedSuperclass
public abstract class AbstractEntity {

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

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

When I attemp to run the application (which is still very basic at this point) just to see what happens, there is a ton of console output, but the thing that catches my eye is the following:当我尝试运行应用程序(此时仍然非常基本)只是为了看看会发生什么时,有大量的控制台输出,但引起我注意的是以下内容:

...
[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
...

No idea what this all means, but it's related to my entity classes.不知道这一切意味着什么,但它与我的实体类有关。 I'm also wondering if I'm missing something in my POM file or if there is some kind of configuration that's missing.我还想知道我的 POM 文件中是否缺少某些内容,或者是否缺少某种配置。 As I said, I'm not familiar with the JPA or data persistence in Java in general.正如我所说,我一般不熟悉 Java 中的 JPA 或数据持久性。

The basics.基础知识。 So, like I was saying, there is not enough information for your problem because I am not getting the problem.所以,就像我说的,你的问题没有足够的信息,因为我没有得到这个问题。 However, here is a 2 minute Spring-Data-Jpa lesson.但是,这是一个 2 分钟的 Spring-Data-Jpa 课程。 An abstract entity:一个抽象实体:

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

    public Long getId() {
        return id;
    }
}

A Task:一个任务:

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

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

A TaskNote任务笔记

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

A StateChangeHistory状态变化历史

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

A very important configuration for application.properties. application.properties 的一个非常重要的配置。

spring.jpa.show-sql=true

An application:一个应用程序:

@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);
}

An output:一个输出:

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

Details:细节:

  1. No need to change the names of fields unless you need to change the names of fields.除非您需要更改字段名称,否则无需更改字段名称。
  2. No need to initialize List for @OneToMany relations because they are really just read only query fields.无需为@OneToMany关系初始化List ,因为它们实际上只是只读查询字段。 The mappedBy annotation says that the entity TaskNote or StateChangeHistory is the owner of the relation and only changes made to the owner of the relationship are persisted.mappedBy注释说,实体TaskNoteStateChangeHistoryowner的关系,只更改关系的所有者是坚持做。 These entities are created and persisted explicitly as you see.如您所见,这些实体是显式创建和持久化的。
  3. You don't need to declare something LAZY when it is LAZY by default.默认情况下,当它是LAZY时,您不需要声明LAZY
  4. When it is LAZY then the relation won't be read unless you explicitly tell JPA to do so and there is nothing in the default JpaRepository interface that will do that for you.当它是LAZY时,除非您明确告诉 JPA 这样做,否则不会读取该关系,并且默认的JpaRepository接口中没有任何内容可以为您执行此操作。
  5. Therefore, when you attempt to use the LAZY read-only field without having explicitly written a query for it you will get the dreaded LazyInitializationException .因此,当您尝试使用LAZY只读字段而未明确为其编写查询时,您将得到可怕的LazyInitializationException
  6. The can be null in reference to a "parent" Task item (can be null) is confusing to me.can be nullreference to a "parent" Task item (can be null)是混淆了我。 It's a foreign key which cannot be null.这是一个不能为空的外键。 See ACID principals.请参阅 ACID 主体。
  7. cascade = CascadeType.ALL doesn't mean anything if it's not on the owner of the relation and so adding it has no effect on this example. cascade = CascadeType.ALL如果它不在关系的所有者上,则没有任何意义,因此添加它对本示例没有影响。
  8. Yes, it's true that you can play around with @Join annotations and make cascade annotations do funny things, but you are much better off getting a handle on the basics before trying funny stuff.是的,这是真的,你可以玩弄@Join注释,使cascade注释做有趣的事情,但你不敢轻易尝试的有趣的东西之前获得基础知识的手柄好得多。
  9. Hope this helps.希望这可以帮助。

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

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