Skip to content

ChangeListener 数据变更监听

本文是 Zeze ChangeListener 的完整 API 参考——覆盖监听接口、Changes.Record 变更结构、增量日志、注册移除、与事务的关系和数据同步模式,供写代码时随查随用。Table 接口见 Table 存储接口,事务触发时机见 事务系统

ChangeListener 是 Table 级的数据变更回调机制。事务提交后,表中记录发生插入、修改、删除时,已注册的监听器会收到通知,携带键和详细变更日志。最常用于同步数据给客户端

@FunctionalInterface
public interface ChangeListener {
void OnChanged(Object key, Changes.Record r);
}
参数说明
key主键
rChanges.Record,含变更细节

通过 r.getState() 获取变更类型:

状态说明附加方法
Put1插入或整个替换getValue() 取新值
Edit2增量修改,日志描述哪些字段变化getVariableLog(id)
Remove0删除
ChangeListener listener = (key, r) -> {
switch (r.getState()) {
case Changes.Record.Put:
Object newValue = r.getValue(); // 整个新值
break;
case Changes.Record.Edit:
// 增量日志,见下节
break;
case Changes.Record.Remove:
// 记录被删除
break;
}
};

Edit 状态下 Changes.Record 携带增量日志,通过 getVariableLog(variableId) 取得:

日志类型适用容器说明
LogBeanBeanBean 修改,Variables 映射 variableId -> Log
LogList1ListList 增量
LogList2ListList 增量(含项内 Bean 变更)
LogMap1MapMap 替换(Replaced)/ 删除(Removed
LogMap2MapMap 替换 / 删除(项内 Changed
LogSet1SetSet 新增(Added)/ 删除(Removed
简单类型日志基本类型字段含新值(Value
case Changes.Record.Edit:
// 取某字段的增量日志
LogBean log = (LogBean) r.getVariableLog(VARIABLE_ID_ITEMS);
// 根据 log 类型处理...
break;

方法说明
table.getChangeListenerMap().addListener(listener)注册监听器
table.getChangeListenerMap().removeListener(listener)移除监听器
table.getChangeListenerMap().hasListener()是否有监听器
特性说明
读写分离写加锁,读访问不可变快照
快照一致性保证事务收集日志阶段和通知阶段用同一份快照
ChangeListener listener = (key, r) -> { ... };
table.getChangeListenerMap().addListener(listener);
// 不再需要时移除
table.getChangeListenerMap().removeListener(listener);

维度说明
触发时机事务 finalCommit 阶段,先于 whileCommit 回调触发(均在提交成功后,但 listener 先于 whileCommit 执行)
事务内回调不应再对同表写操作
非事务上下文直接修改不触发监听器

监听器严格绑定事务提交——只有经过事务提交的修改才会触发通知。


ChangeListener bagListener = (key, r) -> {
long roleId = (Long) key;
switch (r.getState()) {
case Changes.Record.Put:
// 全量:插入或整体替换
Bag bag = (Bag) r.getValue();
online.sendWhileCommit(roleId, new SBagSync(bag.toData()));
break;
case Changes.Record.Edit:
// 增量:可用 MergeChangedToReplaced 简化客户端处理
// 或分别取 getReplaced / getRemoved
break;
case Changes.Record.Remove:
// 背包记录被删除
online.sendWhileCommit(roleId, new SBagRemoved());
break;
}
};
module.getTableBag().getChangeListenerMap().addListener(bagListener);
状态客户端动作
Putclear + putAll(整体替换)
EditputAll(变更项) + removeAll(删除项)
Removenull

特点说明
结构清晰整个 Bean 推送
推荐✅ 推荐使用
问题说明
原子性Get 与增量需保证原子
丢消息增量消息可能丢失
对策维护版本号递增,客户端发现不连续时重新 Get
特点说明
离线记录记录玩家离线期间的变更
重连同步重连时只同步差异部分

维度说明
各自注册每台 Server 各自注册监听器
触发实例只有实际执行修改的实例触发回调
适用同步个人数据(玩家自己的数据)
不适用同步共享数据(多实例共改的数据)

场景是否推荐
逻辑接近事件模型可以用监听器实现逻辑
一般业务逻辑❌ 不建议用监听器实现,直接在存储过程里写
同步个人数据给客户端✅ 合适
更新共享数据❌ 不合适
方式推荐度说明
Get 模式(客户端主动拉)⭐⭐⭐ 大大推荐客户端按需拉取
Push 完整数据⭐⭐⭐ 大大推荐全量推送
Push 不完备增量⚠️ 限制使用
Push 完备增量❌ 暂不推荐
Auth → Login → Map.EnterWorld → UI 显示 → 其他