2017-02-03 215 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有一個構造函數,這些構造函數使用正確類型的參數。