java - 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。
我想知道是否有可能或有其他方法吗?
解决方案
不管你是否使用PESSIMISTIC_WRITE
,如果你更新实体,无论如何都会获得一个锁。不同之处在于,在您在此处描述的情况下,急切地获取锁可以防止丢失的写入。
通常,这是通过为序列增量创建单独的事务来解决的。为了提高性能,您应该增加一个批处理因子,即 10,并将 10 个值保留在内存队列中以供服务。当队列为空时,您要求另外 10 个值等。
Hibernate 在幕后使用 with 实现了这org.hibernate.id.enhanced.TableGenerator
一点org.hibernate.id.enhanced.PooledOptimizer
。因此,如果您预先知道所需的序列,我建议您为此目的使用这些工具。如果你愿意,你也可以自己做类似的事情。
推荐阅读
- javascript - 为什么我的 jquery 计数器功能不起作用?
- db2 - 是否可以在存储过程中向用户授予角色?
- javascript - 如何根据Javascript和Jest单元测试中的可变条件将两个功能和两个单元测试合二为一
- applescript - 如何使用 AppleScript 登录远程 Mac?
- rust - missing generics for trait `actix_service::Service` --> src/middleware.rs:57:8
- arrays - 在 Swift 中创建 C 数组并将其指针传递给 C 函数(在 Swift 中)
- flutter - Flutter A RenderFlex 底部溢出 46 像素
- javascript - 如何在javascript中为对象动态添加属性?
- reactjs - 使用next js动态导入有条件导入模块,SSR不起作用
- bash - 如何从我的域列表中删除命令分隔符?