首页 > 解决方案 > Hibernate 中的多租户序列生成器

问题描述

我有一个以 orgId 和 clientId 作为复合键的 Client 实体。当我必须插入一个新的客户端对象时,我必须为每个 orgId 依次生成 clientId id,为此,我通过在单独的表中维护每个 orgId 的最后一个 clientId 并选择、添加 1 和更新它。

@Entity
@Table(name = "ftb_client")
public class Client implements Serializable {

    @Id
    @JoinColumn(name = "ORG_ID")
    protected String orgId;

    @Id
    @Column(name = "CLIENT_ID")
    protected int clientId;

    @Column(name = "CLIENT_NAME_ENG")
    private String clientNameEng;
//....
}

@Entity
@Table
public class MySeq implements Serializable {
    @Id
    protected String orgId;

    private int lastClientId;
//....
}

public Long getNewClientId(String orgId) {
    MySeq mySeq = getSession()
            .createQuery("from MySeq where orgId = :orgId", MySeq.class)
            .setParameter("orgId", orgId)
            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
            .uniqueResult();

    mySeq.setLastClientId(mySeq.getLastClientId() + 1);

    return mySeq.getLastClientId();
}

但是,如果有数千个并发请求,这会导致重复的 id 生成。因此,为了使其线程安全,我必须使用悲观锁定,以便多个请求不会生成相同的 clientId。但是现在的问题是,直到事务结束并且并发请求保持等待很长时间,锁才会被释放。

因此,如果我可以为每个 orgId 使用单独的序列,而不是使用锁,那么我也可以使 id 生成并发。我想通过执行诸如client_sequence_ [orgId]之类的操作来确定运行时中的序列名称来手动执行序列生成器,然后执行它来生成id。

而且我还想让它独立于数据库,或者至少对于 Oracle、MySQL 和 Postgres。

我想知道是否有可能或有其他方法吗?

标签: javamultithreadinghibernatehql

解决方案


不管你是否使用PESSIMISTIC_WRITE,如果你更新实体,无论如何都会获得一个锁。不同之处在于,在您在此处描述的情况下,急切地获取锁可以防止丢失的写入。

通常,这是通过为序列增量创建单独的事务来解决的。为了提高性能,您应该增加一个批处理因子,即 10,并将 10 个值保留在内存队列中以供服务。当队列为空时,您要求另外 10 个值等。

Hibernate 在幕后使用 with 实现了这org.hibernate.id.enhanced.TableGenerator一点org.hibernate.id.enhanced.PooledOptimizer。因此,如果您预先知道所需的序列,我建议您为此目的使用这些工具。如果你愿意,你也可以自己做类似的事情。


推荐阅读