Hibernate SQL 注解

3

我目前正在使用Hibernate与SQL Server 2016 / Azure SQL Server进行交互,并且到目前为止一直非常顺利。在我的数据库中,我已经实现了系统版本化时间表。我想通过注释仅将两个变量(最好是延迟加载)映射到我的Hibernate实体,以表示适当表的时间历史记录中的原始ValidFrom和UpdatedBy字段。

例如,我有一个帐户类和表。账户 [减去不相关的列、约束等] 表格如下:

CREATE TABLE [dbo].[Account] (
[Id]          INT                                         IDENTITY (1, 1) NOT NULL,
[UpdatedBy]   INT                                         NOT NULL,
[ValidFrom]   DATETIME2 (7) GENERATED ALWAYS AS ROW START DEFAULT (sysutcdatetime()) NOT NULL,
[ValidTo]     DATETIME2 (7) GENERATED ALWAYS AS ROW END   DEFAULT (CONVERT([datetime2],'9999-12-31 23:59:59.9999999')) NOT NULL,
CONSTRAINT [FK_Account.UpdatedById_Account.Id] FOREIGN KEY ([UpdatedBy]) REFERENCES [dbo].[Account] ([Id]),
PRIMARY KEY CLUSTERED ([Id] ASC),
PERIOD FOR SYSTEM_TIME ([ValidFrom], [ValidTo])
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE=[dbo].[AccountHistory], DATA_CONSISTENCY_CHECK=ON));

我想要获取数据的SQL语句如下(我想选择每个注释中的UpdatedBy或ValidFrom,但现在它们在一起以便简洁):

SELECT UpdatedBy, ValidFrom FROM dbo.Account
FOR SYSTEM_TIME ALL
WHERE ValidFrom IN
(
SELECT MIN(ValidFrom) OVER (Partition BY Id) AS ValidFrom
FROM dbo.Account
FOR SYSTEM_TIME ALL
WHERE ID = $(passedInIdOfThisEntity)
)

最后,我的Hibernate实体/POJO大致如下(再次省略不相关的变量):
@Entity
@Table(name = "Account")
public class Account implements Serializable {

    @Id
    @Column(name = "Id", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "UpdatedBy")
    private Account updatedBy;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "ValidFrom", nullable = false, length = 27, insertable = false, updatable = false)
    private Date validFrom;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "ValidTo", nullable = false, length = 27, insertable = false, updatable = false)
    private Date validTo;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "updatedBy")
    private Set<Account> accountsUpdated;

    // This is a stub of what I'm hoping you can help me add
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "ValidFrom", fetch = FetchType.LAZY, insertable = false, updatable = false, somesqlselect = SQL_STATEMENT_FROM_ABOVE)
    private Date createdOn;

    @Column(name = "UpdatedBy", fetch = FetchType.LAZY, insertable = false, updatable = false, somesqlselect = SQL_STATEMENT_FROM_ABOVE)
    private Account createdBy

    // ... getters and setters below
}

我一直在广泛使用Hibernate,但是在这方面找到信息很困难,尽管我已经找到并使用了实现本地查询以检索实体的示例,而不是使用条件查询。如果您能帮助我解决这个谜题,让我可以继续使用条件查询来检索数据,并通过注释按需填充这些字段,我将非常感激。


如果我理解你的需求,你应该使用@ManyToOne来映射另一个表,而不是试图将其作为单个列值与本地子查询一起使用。 - JustinKSU
所以,你是说要有一个名为originalAccount的账户,并与AccountHistory进行映射?我不确定这对我正在做的事情是否有效,因为历史表实际上应该通过主表间接地通过代理进行查询。历史表没有主键,不能写入,并且所有基于时间的查询对话只适用于它的父表Account。如果我使用SQL查询映射一个新实体,也许可以通过延迟加载的方式实现。 - Proto
哦,我明白了。我认为你最好使用原生查询 https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querysql.html - JustinKSU
在这份文档中,我没有看到如何通过注解将类变量Account createdBy或Date createdOn映射到本地查询。我正在处理一个通用的抽象父类,它将检查这些变量是否为空,并在必要时执行本地查询,但这似乎是一种绕行且不够简洁的方法。 - Proto
我不知道JPA/Hibernate是否允许通过动态SQL查询生成一个列。如果你在谷歌上找不到相关信息,这里也没有人回答的话,可能就是这个问题了。有时候,在这种情况下,我会使用Spring的JdbcTemplate,并使用rowmapper来构造对象。 - JustinKSU
1个回答

0

你所描述的临时表构造并不是我所知道的 JPA 或甚至 Hibernate 原生支持的内容。这些可能是 ANSI SQL 标准的新功能,尚未得到适当支持。

也就是说,并不意味着你不能使用像 Hibernate 这样的框架来完成任务。如评论中所示,你可以指定一个命名查询并执行该查询以获取所需的属性。

从 JPA 2.1 的角度来看,你需要使用 @SqlResultSetMapping@ConstructorResult

@SqlResultSetMapping(
  name = "Account.getWithTemporalAttributes",
  classes = {
    @ConstructorResult(
      targetClass = com.company.domain.AccountTemporalDetails.class,
      columns = {
        @ColumnResult(name = "col1"),
        @ColumnResult(name = "col2")
      })
  })

要使用这个,你需要按照以下步骤进行操作:
Query query = entityManager.createNativeQuery( 
                 "SELECT a.col1 as col1, a.col2 as col2 FROM Account a",
                 "Account.getWithTemporalAttributes");
List<AccountTemporalDetails> results = query.getResultList();

这将使您能够使用本地SQL查询,并将其映射到一个POJO,您可以轻松地在应用程序中使用它,而无需编写样板代码。 @ConstructorResult 注解旨在模拟JPQL的 SELECT NEW 语法。因此,您只需要确保 AccountTemporalDetails 具有一个以正确类型接受这些参数的构造函数即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接