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