用spring数据jpa设计父子关系的层次结构

时间:2018-04-13 19:30:35

标签: java hibernate spring-boot database-design spring-data-jpa

我正在尝试建立一个待办事项日志管理员。 我正在使用java spring-boot和data-jpa,它建立在hibernate之上。 我希望用户能够让用户使用多个项目。然后,每个项目都有几个任务,并且用户通过完成短原子工作单元(日志条目)来跟踪每个任务花费了多少时间。

到目前为止,我最终建立了这个系统最天真的实现。它看起来像一个到多个层次结构的几个级别:user-> projects-> tasks->条目。当前的db实现基于这样的模式

schema

实体类的代码(为简洁起见,省略了getter 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;
}

我希望能够为用户提供查看用户指定时间范围内所有日志条目的功能。我添加了像这样的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)说这个查询效率低下是否正确?我在考虑从数据库执行这样的提取效率很低,因为查询无法利用索引。由于Entry表中没有外键user_id,因此正在查找每一行并且正在跟踪链入口&gt; task-&gt; project-&gt;用户。我最终得到线性复杂度而不是对数。

2)解决问题的更好方法是什么?可以在Entry表中将外键存储到用户吗?如果我想从特定项目或任务的数据库中获取条目,那么我还必须为这些关系添加外键。那可以吗?

1 个答案:

答案 0 :(得分:2)

您应该检查正在执行的实际SQL。将org.hibernate.SQL日志级别设置为DEBUG,您将看到这些语句。

我认为对于您的查询,您将实现四个表之间的三个内连接。您说查询无法利用索引。它绝对可以。创建以下索引:

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

请参阅Contactenated Indexes中的Use the Index, Luke

使用这些索引,您跨四个表的连接可能会使用索引。我不会为此而放火,但它应该起作用。检查查询计划。

你是对的,ENTRY - &gt; TASK - &gt; PROJECT - &gt; USER将会被追随,但是对于indixes它应该是非常紧张的

您的数据库架构非常规范化,这导致四个表中的三个连接。您可以通过将user_id带到ENTRY来对此架构进行非规范化。这可能会提高查询性能,但老实说,我怀疑这会带来多少。在实际切换到此解决方案之前,您可能希望运行实际基准测试。