简体   繁体   English

使用Spring Data JPA设计父子关系的层次结构

[英]Designing hierarchy of parent child relationships with spring data jpa

I am trying to build a to-do log keeper. 我正在尝试建立待办事项日志管理员。 I am using java spring-boot with data-jpa which is built on hibernate. 我正在将java spring-boot与基于hibernate构建的data-jpa一起使用。 I want a user to have several projects the user works on. 我希望用户拥有该用户从事的多个项目 Every project then has several tasks associated with it and the user tracks how much time was spent per a task by completing short atomic units of work ( log entries ). 然后,每个项目都有几个与之相关的任务 ,并且用户通过完成简短的原子工作单元( 日志条目 )来跟踪每个任务花费了多少时间。

So far I ended up building the most naive implementation of this system. 到目前为止,我最终构建了该系统的最幼稚的实现。 It looked like several levels of one to many hierarchy: user->projects->tasks->entries. 它看起来像是一对多层次结构中的几个级别:用户->项目->任务->条目。 The current db implementation is based on a schema like this 当前的数据库实现基于这样的模式

图式

Code for entity classes (getters setters constructors and some annotations are omitted for brevity): 实体类的代码(为简洁起见,省略了getters setters构造函数和一些注释):

@MappedSuperclass
public abstract class AbstractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

@Entity
public class User extends AbstractEntity {
    @Column
    private String name;

    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Project> projects;
}

@Entity
public class Project extends AbstractEntity {
    @Column
    private String name;

    @OneToMany(mappedBy = "project", fetch = FetchType.LAZY)
    private List<Task> tasks;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

@Entity
public class Task extends AbstractEntity {
    @Column
    private String name;

    @OneToMany(mappedBy = "task", fetch = FetchType.LAZY)
    private List<Entry> entries;

    @ManyToOne
    @JoinColumn(name = "project_id")
    private Project project;
}

@Entity
public class Entry extends AbstractEntity {   
    @Column
    private Integer duration;

    @Column
    private LocalDateTime finish;

    @ManyToOne
    @JoinColumn(name = "task_id")
    private Task task;
}

I want to be able to provide functionality for a user to view all the log entries in a user specified time frame . 我希望能够为用户提供在用户指定的时间范围内查看所有日志条目的功能 I added jpa repository like this: 我像这样添加了jpa存储库:

public interface EntryRepository extends JpaRepository<Entry, Integer> {

    @Query("SELECT e FROM Entry e WHERE (e.task.project.user.id=:user_id) AND " +
           "(e.finish BETWEEN :from AND :to)")
    List<Entry> getAllForUserInDateRange(@Param("from") LocalDateTime from, 
                                         @Param("to") LocalDateTime to, 
                                         @Param("user_id") int userId);
}

1) Is it correct to say that this query is inefficient? 1)说此查询效率低下是否正确? I was thinking performing a fetch like this from a database is inefficient because the query cannot take advantage of indexes. 我当时认为从数据库执行这样的获取效率不高,因为查询无法利用索引。 Since there is no foreign key user_id in the Entry table every row is being looked up and the chain entry->task->project->user is being followed. 由于Entry表中没有外键user_id,因此正在查找每一行,并遵循链入口->任务->项目->用户。 I end up with linear complexity instead of logarithmic. 我以线性复杂度而不是对数结尾。

2) What is a better way to solve the problem? 2)解决该问题的更好方法是什么? Is it ok to store the foreign key to the user in the Entry table? 将外键存储到用户表中可以吗? If I will want to fetch entries from the database for a particular project or a task, then I will have to add foreign keys to these relationships as well. 如果我想从数据库中获取特定项目或任务的条目,那么我也必须向这些关系添加外键。 Is that ok? 这可以吗?

You should check real SQL which is being executed. 您应该检查正在执行的真实SQL。 Set org.hibernate.SQL log level to DEBUG and you'll see the statements. org.hibernate.SQL日志级别设置为DEBUG ,您将看到以下语句。

I think for your query you will actuall get three inner joins between four tables. 我认为对于您的查询,您实际上将在四个表之间获得三个内部联接。 You say the query cannot take advantage of indexes. 您说查询无法利用索引。 It absolutely can. 绝对可以。 Create following indexes: 创建以下索引:

  • USER (ID)
  • PROJECT (USED_ID, ID)
  • TASK (PROJECT_ID, ID)
  • ENTRY(TASK_ID, ID)

See Contactenated Indexes from Use the Index, Luke . 请参阅使用索引,Luke中的 接触索引

With these indexes your joins across four tables will likely use indexes. 使用这些索引,您在四个表之间的联接可能会使用索引。 I won't put my hand in fire for this, but it should work. 我不会为此着火,但是应该可以。 Check the query plan. 检查查询计划。

You are right that the chain ENTRY -> TASK -> PROJECT -> USER will be followed, but it should be quite faset with indixes 你是正确的,链ENTRY - > TASK - > PROJECT - > USER将接踵而至,但它应该是相当faset与indixes

Your database schema is pretty normalized, which results in three joins across four tables. 您的数据库架构已非常规范化,这导致在四个表之间进行三个联接。 You could denormalize this schema by bringing, say, user_id to the ENTRY . 您可以通过将user_id引入ENTRY来对此模式进行非规范化。 This may improve query performance, but honestly I doubt this will bring much. 这可能会提高查询性能,但老实说,我怀疑这会带来很多好处。 You may want to run real-world benchmark before actually switching to this solution. 在实际切换到此解决方案之前,您可能需要运行实际基准测试。

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

相关问题 Spring JPA-数据完整性关系 - Spring JPA - Data integrity relationships 春天在OneToMany JPA Relationship中使用子数据获取父数据 - Fetching parent data using Child Data in OneToMany JPA Relationship in spring 从子级到父级的Spring-Data-Jpa级联 - Spring-Data-Jpa Cascading from child to parent "Spring Data JPA 在不获取父级的情况下保存子级" - Spring Data JPA save child without fetch the parent @OneToMany 和 @ManyToOne Spring 数据 JPA 子不更新父 - @OneToMany and @ManyToOne Spring Data JPA Child does not update Parent Spring Data Jpa OneToMany 同时保存子实体和父实体? - Spring Data Jpa OneToMany save child and parent entities at the same time? Spring 数据 JPA:删除 OneToMany 子节点而不经过父节点 - Spring Data JPA: delete OneToMany child without going through the parent Spring Data JPA通过从父级获取id与父级实体一起插入子级 - Spring Data JPA insert with child along with parent entity by taking id from parent Spring Data JPA-在显式删除子级后加载父级将返回具有已删除子级的子级的集合 - Spring Data JPA - loading parent after explicit child deletion returns collection of children with deleted child Spring Data Jpa:从父实体持久化子实体不会更新子身份 - Spring Data Jpa: persisting child entities from parent entity does not update child identity
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM