Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.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 Spring mongo只写字段_Java_Spring_Mongodb_Spring Data_Spring Data Mongodb - Fatal编程技术网

Java Spring mongo只写字段

Java Spring mongo只写字段,java,spring,mongodb,spring-data,spring-data-mongodb,Java,Spring,Mongodb,Spring Data,Spring Data Mongodb,我有一个描述mongo文档的抽象类。这个类可能有不同的实现需要重写抽象方法。以下是一个简化的示例: @Document @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) public abstract class Entity { @Id private ObjectId id; abstract String getSomething(); } 我希望getSomething()作为字符串字段写入文档。但是我不想把它读回去 我已

我有一个描述mongo文档的抽象类。这个类可能有不同的实现需要重写抽象方法。以下是一个简化的示例:

@Document
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public abstract class Entity {

    @Id
    private ObjectId id;

    abstract String getSomething();
}
我希望
getSomething()
作为字符串字段写入文档。但是我不想把它读回去

我已尝试使用
@AccessType
注释:

@AccessType(AccessType.Type.PROPERTY)
abstract String getSomething();
但是,当我从db中读取此文档时,spring抛出
UnsupportedOperationException:No accessor来设置属性。它试图为这个字段找到一个setter,但我不想为它定义一个setter——该方法可能返回一个计算值,并且应该没有能力更改它。虽然一个空的setter可能会有所帮助,但它看起来更像是一个解决方法,我会尽量避免它


所以我想知道在从db读取时是否有一种方法可以跳过这个特定属性?与
@Transient
注释相反的内容。或类似于Jackson库中的
@JsonIgnore

您可以尝试添加
@Transient
属性,以便在映射字段时忽略它。 请参阅问题

另一方面,如果只是在读取时忽略该属性,则可以指定一个空的
setter
方法。这将避免无存取器方法的异常


或者创建一个自定义注释,并将所有具有空setter方法的属性放在一个单独的类中,并使用新注释对该类进行注释。然后,可以在需要时扩展该类。您可以查看project,了解他们是如何为getter和setter方法创建注释的

您案例中的问题是带有“get”一词的方法的名称

通过使用“get”一词,Spring认为有一个名为“something”的属性,并以此检查是否有各自的getter和setter方法


将您的方法从“getSomething”重命名为,例如,“calculatedInformation”。

令人惊讶的是,没有简单的解决方案使字段只写

尽管创建一个空的setter可以解决这个问题,但我觉得它打破了一个规则:如果你有一个setter,你希望它设置一个值

因此,我决定创建自己的
@WriteOnly
注释,并使用它忽略我不想从数据库中读取的字段:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface WriteOnly {
}
要使用它,您需要扩展:

@服务
公共类WriteOnlyListener扩展了AbstractMongoEventListener{
@凌驾
加载后的公共无效(加载后事件){
Document doc=event.getDocument();
如果(doc==null)返回;
for(字段:getWriteOnly(event.getType(),Class::getDeclaredFields)){
doc.remove(field.getName());
}
for(方法:getWriteOnly(event.getType(),Class::getDeclaredMethods)){
doc.remove(getFieldName(method.getName());
}
}
私有列表getWriteOnly(类类型,
函数c=type;c!=null;c=c.getSuperclass(){
list.addAll(Arrays.stream(提取器.apply(c))
.filter(o->o.isAnnotationPresent(WriteOnly.class))
.collect(Collectors.toList());
}
退货清单;
}
私有静态字符串getFieldName(字符串方法名){
return Introspector.decapitalize(methodName.substring)(methodName.startsWith(“is”)?2:
methodName.startsWith(“get”)?3:0);
}
}

创建一个空的setter可能会有所帮助,但它看起来更像是一种解决方法,会有点混乱。如果可能的话,我希望避免这种情况:它正在尝试为此字段查找setter,但我不想为它定义setter。如果我将该方法重命名为have no
get
,spring将不会保留其结果。它看起来像是
@AccessType(AccessType.Type.PROPERTY)
只对getter和setter有效。您说过“该方法可能返回一个计算值,应该无法更改它”。所以,您只想将这些计算出的信息保存一次,然后读取这些信息?我想将这个方法的返回值写入数据库,但不将其读回。我可以重命名它,但在本例中,spring似乎只是从写入和读取两个方面忽略了它。也许
@AccessType
不是正确的方法
@Service
public class WriteOnlyListener extends AbstractMongoEventListener<Entity> {

    @Override
    public void onAfterLoad(AfterLoadEvent<Entity> event) {
        Document doc = event.getDocument();
        if (doc == null) return;
        for (Field field: getWriteOnly(event.getType(), Class::getDeclaredFields)) {
            doc.remove(field.getName());
        }
        for (Method method: getWriteOnly(event.getType(), Class::getDeclaredMethods)) {
            doc.remove(getFieldName(method.getName()));
        }
    }

    private <T extends AccessibleObject> List<T> getWriteOnly(Class<?> type, 
                                                              Function<Class<?>, T[]> extractor) {
        List<T> list = new ArrayList<>();
        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
            list.addAll(Arrays.stream(extractor.apply(c))
                    .filter(o -> o.isAnnotationPresent(WriteOnly.class))
                    .collect(Collectors.toList()));
        }
        return list;
    }

    private static String getFieldName(String methodName) {
        return Introspector.decapitalize(methodName.substring(methodName.startsWith("is") ? 2 : 
                    methodName.startsWith("get") ? 3 : 0));
    }

}