mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-08-15 18:24:52 +00:00
perf[orm]: map key codec
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package com.zfoo.orm.codec;
|
||||
|
||||
import org.bson.*;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* map解析器
|
||||
*
|
||||
* @Author:lqh
|
||||
* @Date:2024/6/14 10:32
|
||||
*/
|
||||
public class IntMapCodec<V> implements Codec<Map<Integer, V>> {
|
||||
|
||||
private final Class<Map<Integer, V>> encoderClass;
|
||||
private final Codec<Integer> keyCodec;
|
||||
private final Codec<V> valueCodec;
|
||||
|
||||
IntMapCodec(final Class<Map<Integer, V>> encoderClass, final Codec<Integer> keyCodec, final Codec<V> valueCodec) {
|
||||
this.encoderClass = encoderClass;
|
||||
this.keyCodec = keyCodec;
|
||||
this.valueCodec = valueCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final BsonWriter writer, final Map<Integer, V> map, final EncoderContext encoderContext) {
|
||||
try (var dummyWriter = new BsonDocumentWriter(new BsonDocument())) {
|
||||
dummyWriter.writeStartDocument();
|
||||
writer.writeStartDocument();
|
||||
|
||||
for (final Map.Entry<Integer, V> entry : map.entrySet()) {
|
||||
writer.writeName(entry.getKey().toString());
|
||||
valueCodec.encode(writer, entry.getValue(), encoderContext);
|
||||
}
|
||||
dummyWriter.writeEndDocument();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, V> decode(final BsonReader reader, final DecoderContext context) {
|
||||
reader.readStartDocument();
|
||||
Map<Integer, V> map = new HashMap<>();
|
||||
while (!BsonType.END_OF_DOCUMENT.equals(reader.readBsonType())) {
|
||||
int key = java.lang.Integer.parseInt(reader.readName());
|
||||
V value = BsonType.NULL.equals(reader.getCurrentBsonType()) ? null : valueCodec.decode(reader, context);
|
||||
map.put(key, value);
|
||||
}
|
||||
reader.readEndDocument();
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Map<Integer, V>> getEncoderClass() {
|
||||
return encoderClass;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.zfoo.orm.codec;
|
||||
|
||||
import org.bson.*;
|
||||
import org.bson.codecs.Codec;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.bson.codecs.EncoderContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* map解析器
|
||||
*
|
||||
* @Author:lqh
|
||||
* @Date:2024/6/14 10:32
|
||||
*/
|
||||
public class LongMapCodec<V> implements Codec<Map<Long, V>> {
|
||||
|
||||
private final Class<Map<Long, V>> encoderClass;
|
||||
private final Codec<Long> keyCodec;
|
||||
private final Codec<V> valueCodec;
|
||||
|
||||
LongMapCodec(final Class<Map<Long, V>> encoderClass, final Codec<Long> keyCodec, final Codec<V> valueCodec) {
|
||||
this.encoderClass = encoderClass;
|
||||
this.keyCodec = keyCodec;
|
||||
this.valueCodec = valueCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final BsonWriter writer, final Map<Long, V> map, final EncoderContext encoderContext) {
|
||||
try (var dummyWriter = new BsonDocumentWriter(new BsonDocument())) {
|
||||
dummyWriter.writeStartDocument();
|
||||
writer.writeStartDocument();
|
||||
|
||||
for (final Map.Entry<Long, V> entry : map.entrySet()) {
|
||||
writer.writeName(entry.getKey().toString());
|
||||
valueCodec.encode(writer, entry.getValue(), encoderContext);
|
||||
}
|
||||
dummyWriter.writeEndDocument();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, V> decode(final BsonReader reader, final DecoderContext context) {
|
||||
reader.readStartDocument();
|
||||
Map<Long, V> map = new HashMap<>();
|
||||
while (!BsonType.END_OF_DOCUMENT.equals(reader.readBsonType())) {
|
||||
long key = Long.parseLong(reader.readName());
|
||||
V value = BsonType.NULL.equals(reader.getCurrentBsonType()) ? null : valueCodec.decode(reader, context);
|
||||
map.put(key, value);
|
||||
}
|
||||
reader.readEndDocument();
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Map<Long, V>> getEncoderClass() {
|
||||
return encoderClass;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import org.bson.codecs.pojo.TypeWithTypeParameters;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author:lqh
|
||||
* @Date:2024/6/14 10:23
|
||||
*/
|
||||
@@ -16,11 +15,18 @@ 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)));
|
||||
if (!Map.class.isAssignableFrom(type.getType())) {
|
||||
return null;
|
||||
}
|
||||
var typeParameters = type.getTypeParameters();
|
||||
if (type.getTypeParameters().size() != 2) {
|
||||
return null;
|
||||
}
|
||||
if (typeParameters.get(0).getType() == Integer.class) {
|
||||
return new IntMapCodec(type.getType(), registry.get(type.getTypeParameters().get(0)), registry.get(type.getTypeParameters().get(1)));
|
||||
}
|
||||
if (typeParameters.get(0).getType() == Long.class) {
|
||||
return new LongMapCodec(type.getType(), registry.get(type.getTypeParameters().get(0)), registry.get(type.getTypeParameters().get(1)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ public class MapTest {
|
||||
|
||||
OrmContext.getAccessor().insert(entity);
|
||||
var myEntity = OrmContext.getAccessor().load(1, MapEntity.class);
|
||||
Assert.assertEquals(entity.getBagMap(), myEntity.getBagMap());
|
||||
Assert.assertEquals(entity.getBaseMap(), myEntity.getBaseMap());
|
||||
Assert.assertEquals(entity.getIntStringMap(), myEntity.getIntStringMap());
|
||||
Assert.assertEquals(entity.getIntBagMap(), myEntity.getIntBagMap());
|
||||
Assert.assertEquals(entity, myEntity);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user