如何在 Hibernate 中为每个公司创建一个 customerNumber 生成器

Posted

技术标签:

【中文标题】如何在 Hibernate 中为每个公司创建一个 customerNumber 生成器【英文标题】:How to create a customerNumber generator for each company in Hibernate 【发布时间】:2015-05-25 02:24:03 【问题描述】:

我有一张客户表,每个客户都属于一家公司,公司希望他们的客户编号从 0 开始并随着他们添加客户而增加,当公司 B 添加客户时,公司 A 的客户编号不应该受到影响。 CustomerId 内部可以是任何数字,customerNumber 必须在 companyId 上下文中无间隙递增(无间隙我的意思是 0、1、2、3、4,如果删除 2,它就消失了,下一个插入应该是5 而不是 2)

Example: 
    companyId customerNumber  customerId 
    0         0               0
    0         1               1
    0         2               2
    0         3               3
    0         4               4
    0         5               5
    1         0               6
    1         1               7
    1         2               8

我想知道是否有比打开交易、找到最大 customerNumber、使用 max+1 作为 customerNumber 插入条目并关闭交易更好的方法

是否可以使用某种注释来指定生成 customerNumber 的标准?下一个 customerNumber 应该是该公司内可用的最高号码。 (我有大约 20 个其他实体,它们基于日期和 comapnyId 具有类似的人类可读增量 id 要求,我想让 customerNumber 类型字段的生成尽可能简单,我不想记住每次我坚持一个新实体时都这样做)

类似:

@SequenceGenerator(uniqueOver="companyId,customerNumber",strategy=GenerationType.AUTO)
private Long customerNumber;

该解决方案应该符合 ACID,因为我正在处理股票和财务数据。

更新:我已将 id 重命名为 customerId 并将 customerId 重命名为 customerNumber 以防止混淆。

更新: 当我的意思是无间隙时,我的意思是 customerNumbers 应该是 0,1,2,3,4,5,6,7,8,9 - 如果我删除数字 4 它将永远消失,下一个插入应该是 10,除非我创建了一家新公司,那么他们的第一个客户应该从 0 开始。

更新: 我正在使用带有休眠功能的 spring,所以 @PrePersist 注释对我不起作用。如果建议将@PrePersist 作为解决方案,那么它需要在spring 下工作,因此需要对Simon 的问题进行回答:Enable @PrePersist and @PreUpdate in Spring

我不确定的建议答案:

@Entity
@Table(name = "Customer")
public class Customer 

    @Table(name = "CustomerNumber")
    @Entity(name = "sequenceIdentifier")
    public static class CustomerNumberSequenceIdentifier 

        @Id
        @GenericGenerator(name = "sequence", strategy = "sequence", parameters = 
                @org.hibernate.annotations.Parameter(name = "sequenceName", value = "sequence"),
                @org.hibernate.annotations.Parameter(name = "allocationSize", value = "1"),
        )
        @GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)
        private Long id;

    


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long customerId;

    @ManyToOne
    private Company company;

    @GeneratedValue(generator = "sequenceIdentifier", strategy=GenerationType.TABLE)
    private Long customerNumber


【问题讨论】:

【参考方案1】:

虽然我不能谈论事物的休眠方面(对不起),但只要它使用 myisam 引擎,您可以使用 mysql 中的表来完成此操作,具有以下表结构:

create table ai_test (
    company_id integer,
    customer_number integer auto_increment,
    primary key (company_id, customer_number)
) engine=MyISAM;

这是一个显示实际效果的小提琴:http://sqlfiddle.com/#!9/45e78/1

抱歉,我无法为您将其翻译为休眠 - 从未使用过它,但这可能是一个起点。

编辑 由于需要 inno,您可以使用此表和触发器:

create table ai_test (
    company_id integer,
    customer_number integer,
    primary key (company_id, customer_number)
) engine=InnoDB;


create trigger inno_composite_ai_ok before insert on ai_test
for each row 
begin
    set new.customer_number =     
        (select ifnull((select max(customer_number)+1 
                         from ai_test 
                           where 
                        company_id=new.company_id),1));
end//

这里有一个示例小提琴: http://sqlfiddle.com/#!9/fffd0/14

编辑 一项更新以包括单列 PK 的要求

create table ai_test (
    company_id integer,
    customer_no integer,
    customer_id integer primary key auto_increment

) engine=InnoDB;

create trigger inno_composite_ai_ok before insert on ai_test
for each row 
begin
    set new.customer_no =     
        (select ifnull((select max(customer_no)+1 
                         from ai_test 
                           where 
                        company_id=new.company_id),1));
end//

fiddle here

请注意,您可以(并且可能应该)在 company_d/customer_no 中设置唯一约束

【讨论】:

【参考方案2】:

如果你使用休眠; 示例查询

Query query = session.createQuery("insert into customer (company_id, customer_id)" +"(select :company_id, IFNULL(max(customer_id),1)+1 from customer where company_id = :company_id)");

    //set the company_id here

    int result = query.executeUpdate();

注意:IFNULL 是 MYSQL 特定的语法

我建议将公司 ID auto_increment 字段保留在单独的公司主表中。

【讨论】:

【参考方案3】:

您正在寻找序列号生成,而无需编写 ddl 来为每个表生成新的序列号。使用单独的序列号表执行此操作,并为更新事务创建选择以锁定两个表以进行插入。 Hibernate 使用增强表来做到这一点。

这是一个很好的例子: http://vladmihalcea.com/hibernate-identity-sequence-and-table-sequence-generator/

向下滚动到 TABLE(SEQUENCE) 部分,这是一个序列号解决方案表。

在示例中,只需将客户名称指定为序列名称即可获得正确的序列。

开销是为每个新公司生成一个序列行,并为每个新客户锁定表。

【讨论】:

您的 TableSequenceIdentifier 类,我假设在我的情况下是 Customer 类?如何使用表格序列示例获取每个公司的客户编号? 表序列标识符类是一个单独的表,它只为您的客户存储序列值。它将保存该公司的 companyid 和下一个序列值。 Hibernate 提供了增强的表来执行选择更新/更新策略来包装您的插入事务。这为您提供了锁定安全序列更新。您的客户表插入现在将依赖于表序列标识符类。 我不太关注...我已经更新了我的问题,customerId 是一个自动递增字段,每个公司可以有多个客户,每个 customerNumber 应该每个公司递增。我的 CustomerNumberSequenceIdentifier 类是否适合我正在尝试做的事情,以及如何将它带入 CustomerNumber,以便我可以按照上面的示例获得每个公司递增的客户编号?我找到的例子都只是展示了序列号表是如何生成序列的。随意编辑我的问题中的休眠模型并填补空白。

以上是关于如何在 Hibernate 中为每个公司创建一个 customerNumber 生成器的主要内容,如果未能解决你的问题,请参考以下文章

在 Laravel 4.2 中为每个公司创建目录(命名为 Today Date)并编写日志文件(命名为 CompanyName)

如何在 Hibernate 中为某些实体禁用模式验证?

为啥 hibernate 在不同的 JVM 实例中为相同的查询创建不同的列别名?

如何在 MySQL 中为每个类别创建一个 SQL 窗口函数列?

如何在hibernate中为count(*)编写查询

Hibernate 不会在 h2 中为使用 @Entity 注释的类创建专用表