昨天晚上碰到了一个问题,弄了老半天后来在别人的帮助下发现是mongodb存储HashMap类型字段时的问题,直接看代码吧:
//这是实体类中的那个HashMap类型的字段 private HashMap<String, String> properties; public HashMap<String, String> getProperties() { return properties; } public void setProperties(HashMap<String, String> properties) { this.properties = properties; }
这是monog保存实体时报的错误:
可以看出是数据库操作时出现的异常,再查看报错那地方的源码如下:
/** * If the given class format is not already present in the given map and * a format for this class name does not already exist, creates an * uninitialized format, adds it to the map, and also collects related * formats in the map. */ public Format createFormat(Class type, Map<String, Format> newFormats) { ...... /* * Although metadata is only needed for a complex type, call * getClassMetadata for all types to support checks for illegal * metadata on other types. */ //此处获取的metadata为NULL,基本上就可以得出应该是由于类型不匹配所导致的问题了 ClassMetadata metadata = model.getClassMetadata(className); /* Create format of the appropriate type. */ String proxyClassName = null; if (proxyClassMap != null) { proxyClassName = proxyClassMap.get(className); } if (proxyClassName != null) { format = new ProxiedFormat(this, type, proxyClassName); } else if (type.isArray()) { format = type.getComponentType().isPrimitive() ? (new PrimitiveArrayFormat(this, type)) : (new ObjectArrayFormat(this, type)); } else if (type.isEnum()) { format = new EnumFormat(this, type); } else if (type.getEnclosingClass() != null && type.getEnclosingClass().isEnum()) { /* * If the type is an anonymous class of an enum class, the format * which represents the enum class will be created. [#18357] */ format = new EnumFormat(this, type.getEnclosingClass()); } else if (type == Object.class || type.isInterface()) { format = new NonPersistentFormat(this, type); } else { if (metadata == null) { //此处为第799行报错的地方,所以应该是metadata的问题,再往上追踪metadata的出处 throw new IllegalArgumentException ("Class could not be loaded or is not persistent: " + className); } if (metadata.getCompositeKeyFields() != null && (metadata.getPrimaryKey() != null || metadata.getSecondaryKeys() != null)) { throw new IllegalArgumentException ("A composite key class may not have primary or" + " secondary key fields: " + type.getName()); } /* * Check for inner class before default constructor, to give a * specific error message for each. */ if (type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers())) { throw new IllegalArgumentException ("Inner classes not allowed: " + type.getName()); } try { type.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new IllegalArgumentException ("No default constructor: " + type.getName(), e); } if (metadata.getCompositeKeyFields() != null) { format = new CompositeKeyFormat (this, type, metadata, metadata.getCompositeKeyFields()); } else { EntityMetadata entityMetadata = model.getEntityMetadata(className); format = new ComplexFormat(this, type, metadata, entityMetadata); } } /* Collect new format along with any related new formats. */ newFormats.put(className, format); format.collectRelatedFormats(this, newFormats); return format; }
由于报错显示了BasicDbobject, 那我们再来看一下BasicDbobject的源码:
/* * Copyright (c) 2008-2014 MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // BasicDBObject.java package com.mongodb; import com.mongodb.util.JSON; import org.bson.BasicBSONObject; import java.util.Map; /** * a basic implementation of bson object that is mongo specific. * A <code>DBObject</code> can be created as follows, using this class: * <blockquote><pre> * DBObject obj = new BasicDBObject(); * obj.put( "foo", "bar" ); * </pre></blockquote> */ public class BasicDBObject extends BasicBSONObject implements DBObject { private static final long serialVersionUID = -4415279469780082174L; /** * Creates an empty object. */ public BasicDBObject(){ } /** * creates an empty object * @param size an estimate of number of fields that will be inserted */ public BasicDBObject(int size){ super(size); } /** * creates an object with the given key/value * @param key key under which to store * @param value value to stor */ public BasicDBObject(String key, Object value){ super(key, value); } /** * Creates an object from a map. * @param m map to convert */ //由于实体中只有一个hashmap字段比较特殊,所以怀疑此处的Map可能有问题 public BasicDBObject(Map m) { super(m); } public boolean isPartialObject(){ return _isPartialObject; } public void markAsPartialObject(){ _isPartialObject = true; } /** * Returns a JSON serialization of this object * @return JSON serialization */ @Override public String toString(){ return JSON.serialize( this ); } @Override public BasicDBObject append( String key , Object val ){ put( key , val ); return this; } public Object copy() { // copy field values into new object BasicDBObject newobj = new BasicDBObject(this.toMap()); // need to clone the sub obj for (String field : keySet()) { Object val = get(field); if (val instanceof BasicDBObject) { newobj.put(field, ((BasicDBObject)val).copy()); } else if (val instanceof BasicDBList) { newobj.put(field, ((BasicDBList)val).copy()); } } return newobj; } private boolean _isPartialObject; }
//再打开BasicDBObject的父类,发现BasicBSONObject继承了java.util.LinkedHashMap //所以猜测可能是Bdb里面的vo不识别非JDK自身的HashMap实现 public class BasicBSONObject extends LinkedHashMap<String,Object> implements BSONObject { }
解决方案:
//修改set方法 public void setProperties(HashMap<String, String> properties) { HashMap<String, String> map = new HashMap<String, String>(); map.putAll(properties); this.properties = map; }
关于HashMap的更多信息请参考这位作者的文章:
http://itindex.net/detail/46645-hashmap-%E8%A7%A3%E6%9E%90