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

时间:2015-03-21 20:09:42

标签: mysql hibernate jpa orm hibernate-mapping

我有一个客户表,每个客户都属于一家公司,公司希望他们的客户编号从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

我想知道是否有更好的方法来做这件事,而不是打开一个交易,找到max 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被建议作为解决方案,那么它需要在春天工作,因此需要回答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

}

3 个答案:

答案 0 :(得分:2)

虽然我无法与hibernate方面交谈(对不起),但只要使用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

之间添加唯一约束

答案 1 :(得分:1)

如果你使用hibernate; 示例查询

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

//set the company_id here int result = query.executeUpdate();

注意:IFNULL是一种MYSQL特定语法

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

答案 2 :(得分:1)

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

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

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

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

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