前言
在使用spring data mongodb的mongoTemplate 更新数据时遇到如下错误
org.bson.codecs.configuration.CodecConfigurationException: Can’t find a codec for class java.lang.Class.
详细的错误信息
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.at org.bson.internal.CodecCache.lambda$getOrThrow$1(CodecCache.java:52)at java.base/java.util.Optional.orElseThrow(Optional.java:403)at org.bson.internal.CodecCache.getOrThrow(CodecCache.java:51)at org.bson.internal.OverridableUuidRepresentationCodecRegistry.get(OverridableUuidRepresentationCodecRegistry.java:72)at org.bson.internal.ChildCodecRegistry.get(ChildCodecRegistry.java:52)at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:206)at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:165)at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:44)at org.bson.internal.LazyCodec.encode(LazyCodec.java:38)at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91)at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:207)at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:165)at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:44)at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.encode(SplittablePayload.java:221)at com.mongodb.internal.connection.SplittablePayload$WriteRequestEncoder.encode(SplittablePayload.java:187)at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)at com.mongodb.internal.connection.BsonWriterHelper.writeDocument(BsonWriterHelper.java:77)at com.mongodb.internal.connection.BsonWriterHelper.writePayload(BsonWriterHelper.java:59)at com.mongodb.internal.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:168)at com.mongodb.internal.connection.RequestMessage.encode(RequestMessage.java:138)at com.mongodb.internal.connection.CommandMessage.encode(CommandMessage.java:62)at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:326)at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:116)at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:647)at com.mongodb.internal.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:71)at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:244)at com.mongodb.internal.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:227)at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:127)at com.mongodb.internal.connection.DefaultServer$OperationCountTrackingConnection.command(DefaultServer.java:357)at com.mongodb.internal.operation.MixedBulkWriteOperation.executeCommand(MixedBulkWriteOperation.java:477)at com.mongodb.internal.operation.MixedBulkWriteOperation.executeBulkWriteBatch(MixedBulkWriteOperation.java:339)at com.mongodb.internal.operation.MixedBulkWriteOperation.lambda$execute$2(MixedBulkWriteOperation.java:260)at com.mongodb.internal.operation.OperationHelper.lambda$withSourceAndConnection$2(OperationHelper.java:564)at com.mongodb.internal.operation.OperationHelper.withSuppliedResource(OperationHelper.java:589)at com.mongodb.internal.operation.OperationHelper.lambda$withSourceAndConnection$3(OperationHelper.java:563)at com.mongodb.internal.operation.OperationHelper.withSuppliedResource(OperationHelper.java:589)at com.mongodb.internal.operation.OperationHelper.withSourceAndConnection(OperationHelper.java:562)at com.mongodb.internal.operation.MixedBulkWriteOperation.lambda$execute$3(MixedBulkWriteOperation.java:232)at com.mongodb.internal.async.function.RetryingSyncSupplier.get(RetryingSyncSupplier.java:65)at com.mongodb.internal.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:268)at com.mongodb.internal.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:84)at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:212)at com.mongodb.client.internal.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:1010)at com.mongodb.client.internal.MongoCollectionImpl.executeUpdate(MongoCollectionImpl.java:994)at com.mongodb.client.internal.MongoCollectionImpl.updateOne(MongoCollectionImpl.java:579)at org.springframework.data.mongodb.core.MongoTemplate.lambda$doUpdate$20(MongoTemplate.java:1695)at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:560)at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1668)at org.springframework.data.mongodb.core.MongoTemplate.updateFirst(MongoTemplate.java:1599)at com.wn.cloud.recommend.gateway.repository.mongo.impl.MongoRepositoryImpl.updateFirst(MongoRepositoryImpl.java:243)at com.wn.cloud.recommend.gateway.repository.mongo.impl.MongoRepositoryImpl$$FastClassBySpringCGLIB$$e32636f.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)at com.wn.cloud.recommend.gateway.repository.mongo.impl.MongoRepositoryImpl$$EnhancerBySpringCGLIB$$ac6a30e3.updateFirst(<generated>)at com.wn.cloud.recommend.gateway.impl.MongoGatewayImpl.updateFirst(MongoGatewayImpl.java:270)at com.wn.cloud.recommend.service.test.ProcessTest.test(ProcessTest.java:57)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.base/java.lang.reflect.Method.invoke(Method.java:568)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
问题现场
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class ProcessTest {@Autowiredprivate MongoGateway mongoGateway;@Testpublic void test() {String collectionName = mongoGateway.getCollectionName(User.class);System.out.println(collectionName);User user = new User();user.setId(1L);user.setUser_id(1111L);user.setAge(10);User user1 = mongoGateway.save(user, "user");System.out.println("新增user: " + JSON.toJSONString(user1));Query query = new Query();Criteria criteria = new Criteria(TableInfoEnum.USER.getUniqueFieldName());criteria.is(user.getUser_id());query.addCriteria(criteria);user.setAge(18);Update update = new Update();setUpdate(update, user);mongoGateway.updateFirst(query, update, User.class, "user");User one = mongoGateway.findOne(query, User.class, "user");System.out.println("one: " + JSON.toJSONString(one));}public void setUpdate(Update update, Object object) {if (null == object) {return;}Class<?> clazz = object.getClass();BeanInfo beanInfo;try {beanInfo = Introspector.getBeanInfo(clazz);PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {String name = propertyDescriptor.getName();Method readMethod = propertyDescriptor.getReadMethod();readMethod.setAccessible(true);Object invoke = readMethod.invoke(object, null);if (null != invoke) {update.set(name, invoke);// 后来新增System.out.println("name: " + name + " invoke: " + invoke);}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {log.error("setUpdate属性失败,错误日志: {[]}", e);}}
}
代码的功能也很简单
第一个插入数据 使用到了 save
第二个 更新数据 使用到了 updateFirst
在updateFirst 更新方法中 会调用 setUpdate 进行属性复制 会把 User对象的属性 以及属性值都设置给Update 然后 进行更新
分析
刚开始以为缺少了什么 CodeC的加解密类 后来又增加了 日志打印设置的属性以及属性值 System.out.println("name: " + name + " invoke: " + invoke); 后来发现属性中居然有个class的 属性名 后来一想也是正常的 因为java的类默认有个Object父类然后在这个父类中 有个class属性以及 获取class的方法
public class Object {private static native void registerNatives();static {registerNatives();}public final native Class<?> getClass();
}
然后在 mongoTemplate 的配置并未设置class的属性的编解码器 所以就报错了
解决
方式一: 设置解析class属性的 Codec 编解码器
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;public class ClassCodec implements Codec<Class<?>> {@Overridepublic void encode(BsonWriter writer, Class<?> value, EncoderContext encoderContext) {// 在此处实现将 Class 对象编码为 BSON 的逻辑// 例如,您可以将类名编码为字符串并写入 BsonWriterwriter.writeString(value.getName());}@Overridepublic Class<?> decode(BsonReader reader, DecoderContext decoderContext) {// 在此处实现将 BSON 解码为 Class 对象的逻辑// 例如,您可以从 BsonReader 读取字符串并使用 Class.forName() 加载相应的类String className = reader.readString();try {return Class.forName(className);} catch (ClassNotFoundException e) {throw new IllegalArgumentException("Failed to decode Class object.", e);}}@Overridepublic Class<?> getEncoderClass() {return Class.class;}
}
然后注册这个class编解码器 到mongodb template 即可 这是是将class的属性转化为className的字符串存储 然后读取也是读取这个字符串 如果需要 可以自行扩展实现
方式二: 排除掉这个属性的设置即可
public void setUpdate(Update update, Object object) {if (null == object) {return;}Class<?> clazz = object.getClass();BeanInfo beanInfo;try {beanInfo = Introspector.getBeanInfo(clazz);PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {String name = propertyDescriptor.getName();if (!"class".equalsIgnoreCase(name)) {Method readMethod = propertyDescriptor.getReadMethod();readMethod.setAccessible(true);Object invoke = readMethod.invoke(object, null);if (null != invoke) {update.set(name, invoke);System.out.println("name: " + name + " invoke: " + invoke);}}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {log.error("setUpdate属性失败,错误日志: {[]}", e);}
}
the end !!!
good day ! !!