【问题标题】:Select most recent dependant record选择最近的依赖记录
【发布时间】:2013-09-09 17:29:19
【问题描述】:

我想按如下方式为产品实施定价:

  • Product 对象包含一个描述其当前价格的 Pricing 对象。
  • Product 表以一对多关系链接到 Pricing 表
+------------+ +------------+ |产品 | |价格 | +------------+ 1-n +------------+ |产品编号 | ------------ |产品编号 | | ... | | price_id | +------------+ |价格价值 | | price_creation_time | | ... | +----------+

到目前为止听起来很简单,但我遇到问题的部分是

  • 产品只有一个有效价格,即最近的一个。
  • 不应获取最近一次之前的任何定价记录。
  • 当用户编辑产品定价时,当前记录不应更新,而应插入具有当前时间戳的新记录。
  • 保留以前的定价记录是出于历史原因(例如,在购买产品时看到客户希望获得退款)。

本质上,在 Product 对象中,关系应该看起来像是一对一的关系。但是,我不知道如何使用 Hibernate 实现这样的事情

Last Record of one to many relation Hibernate Criteria 建议称为 Criteria 的东西可能会有所帮助,但我几乎没有 Hibernate 经验,并且仍在掌握类注释。

是否可以使用 Hibernate 实现一对多关系,就像这样的一对一关系?我该怎么做呢?

更新:关于这个想法的更多信息如下:

收据将包含每件购买物品的记录集合。

+--------------+ +------------------+ |收据 | |收据行 | +----------+ +--------------+ +-----------------+ +----- |产品 | |收据_id | ------------ |收据_id | | +----------+ |收据时间 | |产品编号 | -----+ +--------+ | ... | | price_id | ------------ |价格 | +--------------+ |产品数量 | +-------+ | ... | +------------------+

产品的当前价格(显然只有一个当前价格)由价格表中具有最新时间戳的价格记录确定。

SELECT *
FROM prices
WHERE product_id = (Product ID goes here)
ORDER BY price_creation_time;
LIMIT 1;

当您更新定价时,我想插入一条新的,而不是更新当前链接到产品的定价记录。

INSERT INTO price (
    product_id, 
    price_value, 
    price_creation_time
) VALUES (
    (Product ID goes here),
    (New price goes here),
    NOW()
);

当客户购买某件商品时,每件购买的商品都会在收据记录中添加一行。每行包含产品的 ID,cut 还包括生成收据时适用的定价记录的 ID。这样我就可以知道客户在购买产品时为产品支付的费用。

此数据还可用于尚未定义的会计和其他数据挖掘活动。

【问题讨论】:

  • 您可以创建一个仅显示最新价格的可更新视图,并在 Hibernate 中使用该视图(不确定 Hibernate 是否可行 - 我不使用它)
  • 改变数据模型以更好地反映真实关系和用例不是一种选择?
  • @OndrejBozek:数据模型有什么问题?这是对版本化价格进行建模的常见模式(尽管我通常使用 valid_from/valid_to 方法而不是单个时间戳)
  • 我并不是说这是错的。对我来说,似乎(仅从这个有限的描述)重要且普遍接受的价格是最后的价格。所以我可能会从Product(例如一对一)模拟到最后一个Price 的显式连接。然后以某种方式将它与它的历史联系起来。但正如我所说,我对问题领域的了解不如你。
  • 是什么阻止您限制访问历史价格?他们不会只是备份。

标签: java hibernate postgresql


【解决方案1】:
  • 产品只有一个有效价格,即最近的一个。
  • 不应获取最近一次之前的任何定价记录。

您可以使用Hibernate filters 来实现此目的。这将允许您在不初始化整个集合的情况下检索集合的某些元素。在这种情况下,您只想从Product 类中的price collection 中获取一个Price 元素(最近的元素)。

将此过滤器定义添加到您的Product 映射文件(首先使用<hibernate-mapping/> 元素中的<filter-def/> 元素定义过滤器,然后将过滤器附加到price 集合):

 <hibernate-mapping package="x.y.z">
 <class name="Product" table="product_table_name">
 ....
 <set name="priceList" inverse="true" cascade="save-update" lazy="true">
 ...
 <filter name="priceFilter"
            condition="price_id = (select max(p.price_id) from Price p where 
            p.product_id = product_id)" />
 </set>
 </class>
 <filter-def name="priceFilter"/>
 </hibernate-mapping>

此过滤器将使用 max(price_id) 加载 price,这是 product 最近添加的价格。现在您需要为给定会话启用此过滤器,可以按如下方式完成:

session.enableFilter("priceFilter");

为了确保为您提供与最近的Price 关联的Product,请在检索产品数据之前启用会话上的过滤器:

session.enableFilter("priceFilter");
Criteria criteria = session.createCriteria(Product.class);
criteria.setFetchMode("priceList", FetchMode.JOIN);
criteria.add(Restrictions.eq("id", productId));
Product product = (Product) criteria.uniqueResult();

使用此过滤器,关系将显示为一对一关系。关联的价目表将仅包含一个Price(最近的一个)。如果您没有使用session.enableFilter("stateFilter") 启用过滤器,Hibernate 将加载所有关联的价格。

  • 当用户编辑产品定价时,当前记录不应更新,应插入具有当前时间戳的新记录 而是。
  • 保留以前的定价记录是出于历史原因(例如看到客户希望在 购买时间)。

假设您有一个prices 中的set 与特定的product 关联,这可以通过Product 类中的便捷updatePrice 方法如下完成:

private Set<Price> priceList = new HashSet<Price>();
...
public void updatePrice(Price price) {
price.setId(null);// Hibernate will execute an INSERT statement

price.setProduct(this);
priceList.add(price);
}

想法是在将更新后的price 添加到productpriceList 之前,将PriceId 属性设置为null

如果您在价目表中添加新价格,则此新价格将根据“可达性持久性”保持不变。当原始实例被持久化或内存状态与数据存储同步之前,从持久化实例可访问的所有对象都将变为持久化。

【讨论】:

    【解决方案2】:

    实际上,最好的解决方案是使用 PRODUCT -> PRODUCT_PRICE 的显式 FK。

    首选:

    <one-to-one name='currentPrice' class='ProductPrice' column='FK_CURR_PRICE' 
        fetch='join' />
    

    您可以在 a 中映射 a 以子选最近价格的 ID,但它在并发方面性能不佳或健壮。

    间接的,不是首选的:

    <one-to-one name='currentPrice' class='ProductPrice'>
        <formula>
            (select MAX(ID) from PRODUCT_PRICE where PRODUCT_PRICE.FK_PRODUCT=this.ID)
        </formula>
    </one-to-one>
    

    【讨论】:

      【解决方案3】:

      我认为您应该研究渐变维度:

      http://en.wikipedia.org/wiki/Slowly_changing_dimension

      您可能还会发现这个话题很有趣,因为它与您正在做的事情高度相关,而且您几乎肯定会遇到与我自己问的情况类似的情况:

      Temporal database design, with a twist (live vs draft rows)

      如果我要尝试总结一下我当时的发现(我也在设计购物软件,尽管我的问题将其介绍为与博客相关):

      1. 有效查询当前行与历史行的最佳方法(恕我直言)是将后者存储在单独的审计表中(即 price 与 price_revs)。这样可以避免看起来血腥和低效的查询,并在面向客户的用例中保持快速和精简。

        审计表应适当更新,理想情况下但不一定使用触发器。 (如果这对你很重要,我最终选择了 tsrange 类型,而不是两个单独的 start_date/created_at 和 end_date/deleted_at 字段。)

      2. 小心过度使用外键约束,尤其是在引入级联删除时。当您删除产品时,您可能仍希望保留它及其定价历史记录。如果是这样,您需要删除状态或标志或您选择的任何内容。从某种意义上说,您的主要参考点应该是修订 ID,而不是 product_id 或 price_id。

      3. 如果您希望实时行与同一产品的最新非实时行共存(例如,在建议的草稿定价由某个随机经理验证的同时进行实时定价),我发现更好的解决方案是完全创建一个单独的价格(具有草稿状态)。将其完全视为一个新的“当前”草稿分支,à la git,当您不再需要时,它会被合并(即从“当前”行中删除)到活动分支中它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 2018-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多