前言
在使用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 {
@Autowired
private MongoGateway mongoGateway;
@Test
public 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<?>> {
@Override
public void encode(BsonWriter writer, Class<?> value, EncoderContext encoderContext) {
// 在此处实现将 Class 对象编码为 BSON 的逻辑
// 例如,您可以将类名编码为字符串并写入 BsonWriter
writer.writeString(value.getName());
}
@Override
public 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);
}
}
@Override
public 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 ! !!