Scala中Spring Data MongoDB保存含lazy val的类如何去除bitmap$0字段?
这个问题的根源在于Scala编译器对lazy val的实现机制:当你定义一个lazy val时,Scala会自动生成类似bitmap$0的辅助字段,用来跟踪这个lazy val是否已经完成初始化。而Spring Data MongoDB的@Transient注解仅作用于你显式定义的fullName字段,无法覆盖编译器自动生成的bitmap$0,所以它会被序列化到Mongo文档中。
下面给你几个可行的解决方案,按推荐程度排序:
方案1:使用Scala原生的@transient注解
这是最简单直接的方法,Scala的@transient注解会告诉编译器:不仅标记fullName本身为瞬态,还要把它对应的初始化跟踪字段(比如bitmap$0)也标记为瞬态,这样Spring Data MongoDB在序列化时就会忽略这些字段。
修改后的代码如下:
import scala.transient import org.springframework.data.mongodb.core.mapping.Document @Document(collection = "users") case class User(firstName: String, lastName: String) { @transient lazy val fullName: String = s"$firstName $lastName" }
注意:Scala的@transient属于scala包,不要和Spring的org.springframework.data.annotation.Transient混淆(你也可以同时添加Spring的注解,但核心是Scala原生的@transient)。
方案2:自定义MongoConverter过滤bitmap字段
如果你有多个类都存在这个问题,不想逐个修改case class,可以自定义一个MappingMongoConverter,在写入Mongo文档时自动移除所有以bitmap$开头的字段。
第一步:实现自定义Converter
import org.springframework.data.mongodb.core.convert.MappingMongoConverter import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity import org.bson.Document class CustomMappingMongoConverter( mappingContext: org.springframework.data.mongodb.core.mapping.MongoMappingContext, conversionService: org.springframework.core.convert.ConversionService ) extends MappingMongoConverter(mappingContext, conversionService) { override def writeInternal(obj: Any, doc: Document, entity: MongoPersistentEntity[_]): Unit = { super.writeInternal(obj, doc, entity) // 移除所有自动生成的bitmap辅助字段 doc.keySet().removeIf(_.startsWith("bitmap$")) } }
第二步:配置Spring使用这个Converter
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.mongodb.core.convert.MongoConverter @Configuration class MongoConfig { @Bean def customMongoConverter( mappingContext: org.springframework.data.mongodb.core.mapping.MongoMappingContext, conversionService: org.springframework.core.convert.ConversionService ): MongoConverter = { new CustomMappingMongoConverter(mappingContext, conversionService) } }
方案3:将lazy val改为def(备选)
如果你的fullName不需要缓存计算结果(每次调用重新计算也不影响性能),可以直接把lazy val改成def。因为def是方法而非字段,MongoDB不会序列化方法,自然也就不会生成bitmap字段。
修改后的代码:
case class User(firstName: String, lastName: String) { def fullName: String = s"$firstName $lastName" }
这个方案的缺点是每次调用fullName都会重新拼接字符串,如果你需要缓存结果,还是方案1更合适。
内容的提问来源于stack exchange,提问作者t_tia

