【问题标题】:Hibernate relation with aggregationHibernate 与聚合的关系
【发布时间】:2018-11-06 16:38:18
【问题描述】:
@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;
}

我的目标是在报告屏幕上显示设备的最后登录日期;我已经在DeviceLoginDate 表之间实现了OneToMany 关系,它使用OrderBy 对loginDates 进行降序排序,以便我可以选择要在屏幕上显示的第一项。但是当登录的数量越来越大时,这是一个有点昂贵的操作。我应该怎么做才能只选择最后一次登录日期;

  • 我可以使用 Hibernate 的 @Formula 通过它的 带有相关参数的构造函数。但这将触发数量 选择报告屏幕中的每个项目。

  • 我可以在业务层实现另一种方法,这样一旦 已加载列表;我可以使用相关的 D_NAMES 启动另一个 HQL 来获取 D_NAME、loginDate 配对并循环遍历它们。哪个是 从数据库中选择一个,但在应用程序上进行映射工作 一边。

那么你有什么其他的建议让我建议像连接查询以选择设备并且它是最后一次登录日期(不是日期)吗?我认为这应该以某种方式要求加入站点上的 where 子句,但后来我碰壁了,不知道该怎么做。

你有什么建议?

顺便说一句,我没有发布设备的所有属性,但正如您可能意识到的那样,那里的许多属性和大多数其他属性都是通过使用 HQL 和 LEFT OUTER 连接到其他相关表/实体的选择来获取的加入。

【问题讨论】:

    标签: hibernate jpa join orm relational-database


    【解决方案1】:

    我通常会创建一个数据库视图,例如 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 实现
    • 过滤、排序和分页可以根据任何其他属性在数据库级别应用。

    【讨论】:

    • 感谢您的想法。我会试试的。我实际上可以检查文件,但既然提到了这里,其他人也提到这里; @Formula 是否适用于应用程序级别,还是根据公式的形式应用于应用程序级别或数据库级别?
    • 如果我想坚持使用 Hibernate;您认为将视图作为实体放入我的应用程序并包含在我的连接案例中会解决我的问题吗?
    • 你有多种选择,只有你可以决定你想做什么。
    • 我已经通过这种方式使用另一个实体和 OneToOne 关系。但是对于那些不存在的行(意味着还没有登录),hibernate 无法为关系对象(LoginDate)设置任何内容,因此在尝试访问 jsf 上的 device.loginDate.timeStamp 的属性时。抛出异常 (ObjetNotFoundException) 并且页面无法呈现。我不知道我做错了什么,我不想用 try catch 操作 setter 方法。为什么休眠不为这种情况注入空对象?还是我又错过了什么?
    • 如果没有记录,那么你#ll需要在你的视图中添加一个空检查。
    【解决方案2】:

    我会通过反转映射并进行一些 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&lt;LoginDate&gt; 留给您的Device,但将fetch=FetchType.LAZY 添加到它以用于其他目的。

    【讨论】:

    • 谢谢,如果我理解正确,您是说用延迟加载创建双向(设备上为 1-n,LoginDate 上为 n-1)。但取决于我的表现情况;这可能达不到我的预期。因为在创建报告时,我将不得不一一等待来自数据库的延迟加载调用。我实际上是在尝试找到一种连接结构的方法,以便一个选择可以从 DB 中选择每行的所有这些。 @alan-hay 的回答可能会照亮我的道路。
    • @OlgunKaya 不完全是。我刚刚提到它可以是双向的,但你可以从每个设备的 LoginDate 中搜索最新的行。如果您需要获取所有具有最新 LoginDates 的设备,只需进行分组即可,如果使用 JPQL,这会使事情变得更加容易。并且 LoginDate 中的设备不是延迟加载的。
    • 这就是问题所在。由于该表正在由我在某些时候无法管理的其他进程填充,因此登录表站点上可能不存在我站点上的某些设备,因此不必显示登录日期。报告中的某些行可能有空登录。并且由于上面的 jpql 将基于登录返回,以使用我的设备表和登录表关联/计算最终列表。对吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    相关资源
    最近更新 更多