Android使用hapi-fhir更新Patient触发ResourceVersionConflictException
问题根因
ResourceVersionConflictException对应HTTP 409 Conflict,是FHIR服务端乐观锁校验拦截请求导致的:你提交更新请求时携带的资源版本号,和服务端当前存储的该资源最新版本号不匹配,请求会被直接拒绝。
结合Android端hapi-fhir的使用场景和你贴的代码,90%以上的情况是你提交的Patient对象丢失了meta字段中的versionId版本标识,常见触发场景:
- 读取到Patient对象后,使用Gson、FastJSON等通用JSON序列化工具将对象持久化到本地(Room、SharedPreferences等),后续反序列化后再提交更新。HAPI资源模型的
meta.versionId、资源ID等字段和内置解析器绑定,通用序列化工具默认不会序列化这类内部字段,反序列化后版本号直接为null。 - 修改资源属性时意外覆盖了
patient.meta字段,导致版本号丢失。 - 少数场景是读取资源后,有其他客户端/线程提前修改了服务端的该Patient资源,你持有的版本已经过期。
排查方法
在调用update接口前加一行日志,确认版本号是否存在:
println("待更新资源版本号:${patient.meta?.versionId ?: "版本号为空"}")
如果输出为「版本号为空」,即可确认是版本标识丢失导致的冲突。
修复方案
方案1:正确携带版本号(推荐,符合FHIR规范)
这是最稳妥的实现方式,不会误覆盖其他端提交的修改:
- 如果你有持久化资源的需求,不要用通用JSON工具序列化HAPI资源对象,改用HAPI内置的解析器做序列化/反序列化:
val fhirParser = client.fhirContext.newJsonParser() // 持久化时转JSON字符串 val patientStr = fhirParser.encodeResourceToString(patient) // 读取本地存储的JSON时转回资源对象 val localPatient = fhirParser.parseResource(Patient::class.java, patientStr) - 如果不方便调整序列化逻辑,可以在读取到资源后单独存储版本号,更新前手动赋值回资源对象,同时在更新请求中显式指定版本匹配:
// 读取到资源后立刻存下版本号 val version = patient.meta.versionId.toLong() // --- 修改名字逻辑 --- val newName = ..... val humanName = if (patient.name == null){ patient.addName() }else{ if (patient.name.isEmpty()){ patient.addName() } else patient.name[0] } if (humanName.given.isEmpty()){ humanName.addGiven(newName) }else{ // 注意不要直接改value字段,用setValue方法赋值 humanName.given[0].setValue(newName) } // 更新前手动补全版本号 patient.meta.versionId = version.toString() val resp = client.update() .resource(patient) .withMatchVersion(version) // 显式携带If-Match请求头 .encodedJson() .execute()
方案2:跳过版本校验(仅用于调试,不推荐生产环境用)
如果不需要乐观锁保护,可以直接强制覆盖服务端最新版本,注意该操作可能覆盖其他用户提交的修改:
val resp = client.update() .resource(patient) .withMatchVersion(null) // 传null跳过版本校验 .encodedJson() .execute()
额外注意
你修改Given名称的代码存在一个隐患:直接操作humanName.given[0].value字段赋值,不会触发HAPI模型的内部变更标记,可能导致序列化时值丢失,应该调用setValue()方法完成赋值。
内容的提问来源于stack exchange,提问作者Abu Yousuf
相关产品推荐
相关产品推荐

