2017-02-03 155 views
2

我正在使用Hibernate与SQL Server 2016/Azure SQL Server进行接口,并且迄今为止一直在与它共享美好时光。在我的数据库中,我实现了系统版本化的时态表。我想通过注释将两个更多变量(最好是懒惰地)映射到我的Hibernate实体,这些实体代表适当表的时间历史记录中的原始ValidFrom和UpdatedBy字段。Hibernate sql注释

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

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) 
) 

最后,我的休眠实体/ POJO看起来像这样(再次,redacting无关的变量):

@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,但在查找这方面的信息时遇到了困难。如果你能帮我解决这个谜题,让我继续使用标准查询检索数据并通过需求注释填充这些字段,我将不胜感激。

+0

如果我明白你想要什么,你应该做一个@ManyToOne并映射另一个表,而不是试图用一个本地子查询使它成为一个列值。 – JustinKSU

+0

所以,你说有一个帐户originalAccount与映射到AccountHistory?我不确定这样做是否有效,因为历史表实际上应该由代理通过主表间接查询。历史表没有PK,不能写入,并且所有基于时间的查询对话只能在它的父表Account中使用。如果我用sql查询映射了一个新实体,我可能可以通过这种方式做一个懒加载,尽管 – Proto

+0

哦,我现在看到了。我认为你最好的选择是Native Query https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querysql.html – JustinKSU

回答

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有一个构造函数,这些构造函数使用正确类型的参数。