@Entity
public class Device {
private long id;
private String deviceName; //column D_NAME
private Set<LoginDate> loginDates;
}
/**
Audit table being filled by some other processes on the network
*/
@Entity
public class LoginDate {
private long id; //pk
private String deviceName; //column D_NAME
private Date loginDate;
}
我的目标是在报告屏幕上显示设备的最后登录日期;我已经在Device
和LoginDate
表之间实现了OneToMany关系,它使用OrderBy按降序排列loginDates,这样我就可以选择要在屏幕上显示的第一个项目。但当登录次数越来越多时,这是一个有点昂贵的操作。我应该做些什么来实现只选择最后一个登录日期;
我可以使用Hibernate的
@Formula
通过它的构造函数和相关参数。但这将触发选择报告屏幕中的每个项目。我可以在业务层上实现另一种方法,这样一旦列表已加载;我可以启动另一个具有相关D_NAMES的HQL来获取D_NAME、loginDate配对并循环通过它们。哪个是从数据库中选择一个,但在应用程序上进行映射一边
那么,你有其他建议吗?比如用联接查询来同时选择设备和它的最后登录日期(而不是日期(?我认为这应该在某种程度上需要加入网站上的where子句,但后来我遇到了困难,无法想出如何做到这一点。
你的建议是什么?
顺便说一句,我没有发布设备的所有属性,但正如你可能意识到的那样,那里的许多属性和大多数其他属性都是通过使用HQL和LEFT OUTER JOIN连接到其他相关表/实体的select来获取的。
我通常创建一个数据库视图,比如device_summary_data,然后使用@SecondaryTable
注释将其映射到device(或使用@OneToOne
作为另一个实体(。
https://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTable.html
视图:
create view device_summary_data as
select d.id as device_id, max(l.login_date), count(l.device_name)
from logins l
inner join devices d on d.D_NAME = l.D_NAME
group by d.id
实体:
@Entity
@SecondaryTable(name = "device_summary_data",
pkJoinColumns=@PrimaryKeyJoinColumn(name="device_id", referencedColumnName = "id"))
public class Device {
private long id;
private String deviceName;
private Set<LoginDate> loginDates;
@Column(table="device_summary_data", insertable=false, updateable = false)
private Date lastLogin;
@Column(table="device_summary_data", insertable=false, updateable = false)
private Integer numberOfLogins;
}
与Hibernate的@Formula
:相比的优势
- 适用于任何JPA实现
- 过滤、排序和分页可以根据任何其他属性应用于DB级别
我会通过反转映射并生成一些JPQL可分页/HQL查询来实现这一点。
所以你的LoginDate
应该是这样的:
@Entity
public class LoginDate {
private long id; //pk
@ManyToOne
private Device device;
private Date loginDate;
}
而自定义查询(JPQL(则类似于
SELECT ld FROM LoginDate ld WHERE device=:device ORDER BY ld.loginDate DESC
根据您的其他库/框架,您需要使JPQL查询仅限于一个结果,除非您能够使用简单的native的查询,如:
SELECT ld.logindate
FROM logindate ld
WHERE ld.device_id=:device_id
ORDER BY ld.logindate DESC
LIMIT 1
您可能仍然将Set<LoginDate>
留给Device
,但出于其他目的将fetch=FetchType.LAZY
添加到其中。