mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-09-04 10:17:17 +00:00
feat[orm]: Fix Maps MUST have string keys
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package com.zfoo.orm.codec;
|
||||
|
||||
import org.bson.*;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
import org.bson.codecs.configuration.CodecConfigurationException;
|
||||
import org.bson.json.JsonReader;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* map解析器
|
||||
* @Author:lqh
|
||||
* @Date:2024/6/14 10:32
|
||||
*/
|
||||
public class MapCodec<K, T> implements Codec<Map<K, T>> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(MapCodec.class.getName());
|
||||
|
||||
private final Class<Map<K, T>> encoderClass;
|
||||
private final Codec<K> keyCodec;
|
||||
private final Codec<T> valueCodec;
|
||||
|
||||
MapCodec(final Class<Map<K, T>> encoderClass, final Codec<K> keyCodec, final Codec<T> valueCodec,
|
||||
Map<Class<?>, Class<? extends PropertyEditor>> map) {
|
||||
this.encoderClass = encoderClass;
|
||||
this.keyCodec = keyCodec;
|
||||
this.valueCodec = valueCodec;
|
||||
|
||||
map.forEach(PropertyEditorManager::registerEditor);
|
||||
}
|
||||
|
||||
MapCodec(final Class<Map<K, T>> encoderClass, final Codec<K> keyCodec, final Codec<T> valueCodec) {
|
||||
this.encoderClass = encoderClass;
|
||||
this.keyCodec = keyCodec;
|
||||
this.valueCodec = valueCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final BsonWriter writer, final Map<K, T> map, final EncoderContext encoderContext) {
|
||||
try (var dummyWriter = new BsonDocumentWriter(new BsonDocument())) {
|
||||
dummyWriter.writeStartDocument();
|
||||
writer.writeStartDocument();
|
||||
|
||||
for (final Map.Entry<K, T> entry : map.entrySet()) {
|
||||
PropertyEditor editor = PropertyEditorManager.findEditor(keyCodec.getEncoderClass());
|
||||
if (editor != null) {
|
||||
LOGGER.fine("Found PropertyEditor for class: " + keyCodec.getEncoderClass().getName());
|
||||
|
||||
editor.setValue(entry.getKey());
|
||||
writer.writeName(editor.getAsText());
|
||||
} else {
|
||||
String dummyId = UUID.randomUUID().toString();
|
||||
dummyWriter.writeName(dummyId);
|
||||
keyCodec.encode(dummyWriter, entry.getKey(), encoderContext);
|
||||
writer.writeName(dummyWriter.getDocument().asDocument().get(dummyId).asString().getValue());
|
||||
}
|
||||
|
||||
valueCodec.encode(writer, entry.getValue(), encoderContext);
|
||||
}
|
||||
dummyWriter.writeEndDocument();
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to encode map: " + map, e);
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<K, T> decode(final BsonReader reader, final DecoderContext context) {
|
||||
reader.readStartDocument();
|
||||
Map<K, T> map = getInstance();
|
||||
while (!BsonType.END_OF_DOCUMENT.equals(reader.readBsonType())) {
|
||||
K key;
|
||||
PropertyEditor editor = PropertyEditorManager.findEditor(keyCodec.getEncoderClass());
|
||||
if (editor != null) {
|
||||
LOGGER.fine("Found PropertyEditor for class: " + keyCodec.getEncoderClass().getName());
|
||||
editor.setAsText(reader.readName());
|
||||
key = (K) editor.getValue();
|
||||
} else {
|
||||
var dummyReader = new JsonReader(String.format("\"key\": \"%s\"", reader.readName()));
|
||||
key = keyCodec.decode(dummyReader, context);
|
||||
}
|
||||
|
||||
map.put(key,
|
||||
(!BsonType.NULL.equals(reader.getCurrentBsonType()))
|
||||
? valueCodec.decode(reader, context)
|
||||
: null);
|
||||
}
|
||||
reader.readEndDocument();
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Map<K, T>> getEncoderClass() {
|
||||
return encoderClass;
|
||||
}
|
||||
|
||||
private Map<K, T> getInstance() {
|
||||
if (encoderClass.isInterface()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
try {
|
||||
return encoderClass.getDeclaredConstructor().newInstance();
|
||||
} catch (final Exception e) {
|
||||
throw new CodecConfigurationException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zfoo.orm.codec;
|
||||
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.pojo.PropertyCodecProvider;
|
||||
import org.bson.codecs.pojo.PropertyCodecRegistry;
|
||||
import org.bson.codecs.pojo.TypeWithTypeParameters;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author:lqh
|
||||
* @Date:2024/6/14 10:23
|
||||
*/
|
||||
public class MapCodecProvider implements PropertyCodecProvider {
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public <T> Codec<T> get(final TypeWithTypeParameters<T> type, final PropertyCodecRegistry registry) {
|
||||
if (Map.class.isAssignableFrom(type.getType()) && type.getTypeParameters().size() == 2) {
|
||||
return new MapCodec(
|
||||
type.getType(),
|
||||
registry.get(type.getTypeParameters().get(0)),
|
||||
registry.get(type.getTypeParameters().get(1)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.zfoo.orm.OrmContext;
|
||||
import com.zfoo.orm.anno.*;
|
||||
import com.zfoo.orm.cache.EntityCache;
|
||||
import com.zfoo.orm.cache.IEntityCache;
|
||||
import com.zfoo.orm.codec.MapCodecProvider;
|
||||
import com.zfoo.orm.config.OrmConfig;
|
||||
import com.zfoo.orm.model.EntityDef;
|
||||
import com.zfoo.orm.model.IEntity;
|
||||
@@ -97,7 +98,8 @@ public class OrmManager implements IOrmManager {
|
||||
|
||||
CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
|
||||
MongoClientSettings.getDefaultCodecRegistry(),
|
||||
CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build()));
|
||||
CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true)
|
||||
.register(new MapCodecProvider()).build()));
|
||||
|
||||
var mongoBuilder = MongoClientSettings
|
||||
.builder()
|
||||
@@ -541,9 +543,9 @@ public class OrmManager implements IOrmManager {
|
||||
var keyType = types[0];
|
||||
var valueType = types[1];
|
||||
|
||||
if (!String.class.isAssignableFrom((Class<?>) keyType)) {
|
||||
throw new RunException("[class:{}] type declaration is incorrect, and the key type of the Map must be the String type", clazz.getCanonicalName());
|
||||
}
|
||||
// if (!String.class.isAssignableFrom((Class<?>) keyType)) {
|
||||
// throw new RunException("[class:{}] type declaration is incorrect, and the key type of the Map must be the String type", clazz.getCanonicalName());
|
||||
// }
|
||||
|
||||
checkSubEntity(clazz, valueType);
|
||||
} else if (ObjectId.class.isAssignableFrom(fieldType)) {
|
||||
|
||||
@@ -60,6 +60,11 @@ public class MapTest {
|
||||
map.put("a", Map.of("b", "b"));
|
||||
entity.setBaseMap(map);
|
||||
|
||||
var longStringHashMap = new HashMap<Long, String>();
|
||||
longStringHashMap.put(100L,"hello map1");
|
||||
longStringHashMap.put(101L,"hello map2");
|
||||
entity.setLongStringMap(longStringHashMap);
|
||||
|
||||
OrmContext.getAccessor().insert(entity);
|
||||
|
||||
var myEntity = OrmContext.getAccessor().load(1, MapEntity.class);
|
||||
|
||||
@@ -31,6 +31,8 @@ public class MapEntity implements IEntity<Long> {
|
||||
|
||||
private Map<String, Map<String, String>> baseMap = new HashMap<>();
|
||||
|
||||
private Map<Long, String> longStringMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Long id() {
|
||||
return id;
|
||||
@@ -60,6 +62,14 @@ public class MapEntity implements IEntity<Long> {
|
||||
this.baseMap = baseMap;
|
||||
}
|
||||
|
||||
public Map<Long, String> getLongStringMap() {
|
||||
return longStringMap;
|
||||
}
|
||||
|
||||
public void setLongStringMap(Map<Long, String> longStringMap) {
|
||||
this.longStringMap = longStringMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
Reference in New Issue
Block a user