Spring mongo 只写字段

Kir*_*nov 5 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();
}
Run Code Online (Sandbox Code Playgroud)

我想getSomething()作为字符串字段写入文档。但我不想读回来

我尝试使用@AccessType注释:

@AccessType(AccessType.Type.PROPERTY)
abstract String getSomething();
Run Code Online (Sandbox Code Playgroud)

但是当我从数据库中读取此文档时, spring 抛出UnsupportedOperationException: No accessor to set property. 它正在尝试为该字段找到一个 setter,但我不想为它定义一个 setter - 该方法可能会返回一个计算值,并且应该无法更改它。虽然空的 setter 可能会有所帮助,但它看起来更像是一种解决方法,我会尽量避免它。

所以我想知道从数据库读取时是否有办法跳过这个特定的属性?与@Transient注释相反的东西。或类似于@JsonIgnore杰克逊图书馆。

Kir*_*nov 2

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

虽然创建一个空的 setter 可以解决问题,但我觉得它打破了最小惊喜原则:如果你有一个 setter,你期望它设置一个值。

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface WriteOnly {
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您需要扩展AbstractMongoEventListener

@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));
    }

}
Run Code Online (Sandbox Code Playgroud)