Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/unity3d/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用反射将Mongo DBObject转换为实体POJO_Java_Entity Framework_Mongodb_Orm - Fatal编程技术网

Java 使用反射将Mongo DBObject转换为实体POJO

Java 使用反射将Mongo DBObject转换为实体POJO,java,entity-framework,mongodb,orm,Java,Entity Framework,Mongodb,Orm,首先我想说是的-我知道MongoDB有类似Morphia和Spring数据的ORM。我不是想重新创造这个世界,只是为了学习。所以我的AbstractRepository背后的基本思想是封装所有存储库之间共享的逻辑。子类(特定实体的存储库)将实体类传递给 使用反射将实体bean(POJO)转换为DBObject非常简单。将DBObject转换为实体bean时会出现问题。原因?我需要将DBObject中的任何字段类型转换为实体bean属性类型。这就是我被困的地方。我无法在AbstractReposi

首先我想说是的-我知道MongoDB有类似Morphia和Spring数据的ORM。我不是想重新创造这个世界,只是为了学习。所以我的AbstractRepository背后的基本思想是封装所有存储库之间共享的逻辑。子类(特定实体的存储库)将实体类传递给

使用反射将实体bean(POJO)转换为DBObject非常简单。将DBObject转换为实体bean时会出现问题。原因?我需要将DBObject中的任何字段类型转换为实体bean属性类型。这就是我被困的地方。我无法在AbstractRepository方法中获取实体bean类
T getEntityFromDBObject(DBObject对象)

我可以将实体类传递给这个方法,但这会破坏多态性的目的。另一种方法是声明
privateType
属性,然后使用字段读取类型。定义附加属性只是为了让我可以阅读,这听起来不太正确

所以问题是-如何使用尽可能少的参数使用反射将DBObject映射到POJO。这又是一个想法:

public  abstract class AbstractRepository<T> {
   T getEntityFromDBObject(DBObject object) {
      ....
   }
}
公共抽象类抽象存储库{
T getEntityFromDBObject(DBObject对象){
....
}
}
具体的存储库如下所示:

public class EntityRepository extends AbstractRepository<T> {
}
公共类EntityRepository扩展了AbstractRepository{
}
谢谢


注意:忽略复杂的关系和引用。假设它不需要支持对另一个DBObjects或pojo的引用。

您需要构建一个类型为t的实例,并用“DBObject”中的数据填充它:

public abstract class AbstractRepository<T> {

    protected final Class<T> entityClass;

    protected AbstractRepository() {
        // Don't remember if this reflection stuff throws any exception
        // If it does, try-catch and throw RuntimeException 
        // (or assign null to entityClass)
        // Anyways, it's impossible that such exception occurs here
        Type t = this.getClass().getGenericSuperclass();
        this.entityClass = ((Class<T>)((ParameterizedType)t).getActualTypeArguments()[0]);
    }

    T getEntityFromDBObject(DBObject object) {
        // Use reflection to create an entity instance
        // Let's suppose all entities have a public no-args constructor (they should!)
        T entity = (T) this.entityClass.getConstructor().newInstance();

        // Now fill entity with DBObject's data
        // This is the place to fill common fields only, i.e. an ID
        // So maybe T could extend some abstract BaseEntity that provides setters for these common fields
        // Again, all this reflection stuff needs to be done within a try-catch block because of checked exceptions
        // Wrap the original exception in a RuntimeException and throw this one instead
        // (or maybe your own specific runtime exception for this case)

        // Now let specialized repositories fill specific fields
        this.fillSpecificFields(entity, object);

        return entity;
    }

    protected abstract void fillSpecificFields(T entity, DBObject object);

}
公共抽象类抽象存储库{
受保护的最终类entityClass;
受保护的抽象存储库(){
//不记得这个反射东西是否引发任何异常
//如果是,请尝试捕获并抛出RuntimeException
//(或将null赋值给entityClass)
//无论如何,在这里不可能发生这样的异常
类型t=this.getClass().getGenericSuperclass();
this.entityClass=((类)((参数化类型)t).getActualTypeArguments()[0]);
}
T getEntityFromDBObject(DBObject对象){
//使用反射创建实体实例
//假设所有实体都有一个公共的无参数构造函数(它们应该!)
T entity=(T)this.entityClass.getConstructor().newInstance();
//现在用DBObject的数据填充实体
//这是只填写公共字段的地方,即ID
//所以也许T可以扩展一些抽象的BaseEntity,为这些公共字段提供setter
//同样,由于选中的异常,所有这些反射都需要在try-catch块中完成
//将原始异常包装到RuntimeException中,并抛出此异常
//(或者您自己的特定运行时异常)
//现在让专门的存储库填充特定字段
此.fillSpecificFields(实体、对象);
返回实体;
}
受保护的抽象字段(T实体、DBObject对象);
}
如果不想在每个实体的存储库中实现方法
.fillSpecificFields()
,则需要使用反射来设置每个字段(包括常见字段,如ID,因此不要手动设置)

如果是这种情况,那么您已经将entity类作为受保护的属性,因此每个实体的存储库都可以使用它。您需要迭代它的所有字段,包括在超类中声明的字段(我相信您必须使用方法
.getFields()
,而不是
.getDeclaredFields()
),并通过反射设置值


顺便说一句,我真的不知道这个
DBObject
实例中有什么数据,也不知道是什么格式,所以请告诉我从中提取字段值的结果是否非常重要。

首先,我想为两个月后回复您的评论道歉。我确实自己设法解决了这个问题,下面是我如何实现它(并进行了测试),所以也许有人会利用它:

public abstract class AbstractRepository<T> {

@Inject
private MongoConnectionProvider provider;

// Keeps current repository collection name
protected String collectionName;

@PostConstruct
public abstract void initialize();

public String getCollectionName() {
    return this.collectionName;
}

protected void setCollectionName(String collectionName) {
    this.collectionName = collectionName;
}

protected DBCollection getConnection() {
    DB conn = this.provider.getConnection();
    DBCollection collection = conn.getCollection(this.collectionName);
    return collection;
}

private void putFieldToDbObject(T source, DBObject target, Field field) {
    // TODO: Think more elegant solution for this
    try {
        field.setAccessible(true);
        // Should we cast String to ObjectId
        if (field.getName() == "id" && field.get(source) != null
                || field.isAnnotationPresent(DBRef.class)) {
            String key = field.getName().equals("id") ? "_id" : field.getName();
            target.put(key, new ObjectId(field.get(source).toString()));
        } else {
            if(!field.getName().equals("id")) {
                target.put(field.getName(), field.get(source));
            }
        }
    } catch (IllegalArgumentException | IllegalAccessException exception) {
        // TODO Auto-generated catch block
        exception.printStackTrace();
    } finally {
        field.setAccessible(false);
    }

}

@SuppressWarnings("rawtypes")
protected DBObject getDbObject(T entity) {
    DBObject result = new BasicDBObject();
    // Get entity class
    Class entityClass = entity.getClass();
    Field[] fields = entityClass.getDeclaredFields();
    // Update DBobject with entity data
    for (Field field : fields) {
        this.putFieldToDbObject(entity, result, field);
    }
    return result;
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public T getEntityFromDBObject(DBObject object) throws MappingException {
        Type superclass = this.getClass().getGenericSuperclass();
        Type entityClass = ((ParameterizedType) superclass).getActualTypeArguments()[0];
        T entity;
    try {
        entity = ((Class<T>) entityClass).newInstance();
        // Map fields to entity properties
        Set<String> keySet = object.keySet();
        for(String key : keySet) {
            String fieldName = key.equals("_id") ? "id" : key;
            Field field = ((Class<T>) entityClass).getDeclaredField(fieldName);
            field.setAccessible(true);
            if(object.get(key).getClass().getSimpleName().equals("ObjectId")) {
                field.set(entity, object.get(key).toString());
            } else {
                // Get field type
                Type fieldType = field.getType();
                Object fieldValue = object.get(key);
                Class objectType = object.get(key).getClass();
                if(!fieldType.equals(objectType)) {
                    // Let's try to convert source type to destination type
                    try {
                        fieldValue = (((Class) fieldType).getConstructor(objectType)).newInstance(object.get(key));
                    } catch (NoSuchMethodException exception) {
                        // Let's try to use String as "man-in-the-middle"
                        String objectValue = object.get(key).toString();
                        // Get constructor for destination type that take String as parameter
                        Constructor constructor = ((Class) fieldType).getConstructor(String.class);
                        fieldValue = constructor.newInstance(objectValue);
                    }
                }
                field.set(entity, fieldValue);
            }

            field.setAccessible(false);
        }
    } catch (InstantiationException | IllegalAccessException | NoSuchFieldException | SecurityException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException e) {
        throw new MappingException(e.getMessage(), MappingExceptionCode.UNKNOWN_ERROR);
    }

    return entity;
}

public List<T> getAll() {
    DBCollection conn = this.getConnection();
    DBCursor cursor = conn.find();
    List<T> result = new LinkedList<T>();
    while (cursor.hasNext()) {
        DBObject obj = cursor.next();
        try {
            result.add(this.getEntityFromDBObject(obj));
        } catch (MappingException e) {

        }
    }
    return result;
}

public T getOneById(String id) {
    DBObject idRef = new BasicDBObject().append("_id", new ObjectId(id));
    DBCollection conn = this.getConnection();
    DBObject resultObj = conn.findOne(idRef);
    T result = null;
    try {
        result = this.getEntityFromDBObject(resultObj);
    } catch (MappingException e) {

    }

    return result;

}

public void save(T entity) {
    DBObject object = this.getDbObject(entity);
    DBCollection collection = this.getConnection();
    collection.save(object);
}

}
公共抽象类抽象存储库{
@注入
私有MongoConnectionProvider;
//保留当前存储库集合名称
受保护的字符串集合名称;
@施工后
公共抽象空初始化();
公共字符串getCollectionName(){
返回this.collectionName;
}
受保护的void setCollectionName(字符串collectionName){
this.collectionName=collectionName;
}
受保护的DBCollection getConnection(){
DB conn=this.provider.getConnection();
DBCollection collection=conn.getCollection(this.collectionName);
回收;
}
私有void putFieldToDbObject(T源、DBObject目标、字段){
//TODO:为这个问题考虑更优雅的解决方案
试一试{
字段。setAccessible(true);
//我们应该将字符串转换为ObjectId吗
if(field.getName()=“id”&&field.get(source)!=null
||字段isAnnotationPresent(DBRef.class)){
字符串key=field.getName().equals(“id”)?“id”:field.getName();
target.put(key,newobjectid(field.get(source.toString());
}否则{
如果(!field.getName().equals(“id”)){
target.put(field.getName(),field.get(source));
}
}
}捕获(IllegalArgumentException | IllegalAccessException异常){
//TODO自动生成的捕捉块
异常。printStackTrace();
}最后{
字段.setAccessible(false);
}
}
@抑制警告(“原始类型”)
受保护的DBObject getDbObject(T实体){
DBObject结果=新的BasicDBObject();
//获取实体类
类entityClass=entity.getClass();
Field[]fields=entityClass.getDeclaredFields();
//使用实体数据更新DBobject
用于(字段:字段){
这个,放