简体   繁体   English

指定字段对于 MongoDB 是暂时的,但对于 RestController 不是

[英]Specify field is transient for MongoDB but not for RestController

I'm using spring-boot to provide a REST interface persisted with MongoDB.我正在使用 spring-boot 提供一个与 MongoDB 一起持久化的 REST 接口。 I'm using the 'standard' dependencies to power it, including spring-boot-starter-data-mongodb and spring-boot-starter-web .我正在使用“标准”依赖项为其提供支持,包括spring-boot-starter-data-mongodbspring-boot-starter-web

However, in some of my classes I have fields that I annotate @Transient so that MongoDB does not persist that information.但是,在我的某些课程中,我对@Transient注释,以便 MongoDB 不会保留该信息。 However, this information I DO want sent out in my rest services.但是,我确实希望在我的休息服务中发送这些信息。 Unfortunately, both MongoDB and the rest controller seem to share that annotation.不幸的是,MongoDB 和其余控制器似乎都共享该注释。 So when my front-end receives the JSON object, those fields are not instantiated (but still declared).因此,当我的前端接收到 JSON 对象时,这些字段不会被实例化(但仍会被声明)。 Removing the annotation allows the fields to come through in the JSON object.删除注释允许字段在 JSON 对象中通过。

How I do configure what is transient for MongoDB and REST separately?我如何分别为 MongoDB 和 REST 配置什么是瞬态的?

Here is my class这是我的课

package com.clashalytics.domain.building;

import com.clashalytics.domain.building.constants.BuildingConstants;
import com.clashalytics.domain.building.constants.BuildingType;
import com.google.common.base.Objects;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;

import java.util.*;

public class Building {

    @Id
    private int id;

    private BuildingType buildingType;
    private int level;
    private Location location;
    // TODO http://stackoverflow.com/questions/30970717/specify-field-is-transient-for-mongodb-but-not-for-restcontroller
    @Transient
    private int hp;
    @Transient
    private BuildingDefense defenses;

    private static Map<Building,Building> buildings = new HashMap<>();

    public Building(){}
    public Building(BuildingType buildingType, int level){
        this.buildingType = buildingType;
        this.level = level;
        if(BuildingConstants.hpMap.containsKey(buildingType))
            this.hp = BuildingConstants.hpMap.get(buildingType).get(level - 1);

        this.defenses = BuildingDefense.get(buildingType, level);
    }

    public static Building get(BuildingType townHall, int level) {
        Building newCandidate = new Building(townHall,level);
        if (buildings.containsKey(newCandidate)){
            return buildings.get(newCandidate);
        }
        buildings.put(newCandidate,newCandidate);
        return newCandidate;
    }

    public int getId() {
        return id;
    }

    public String getName(){
        return buildingType.getName();
    }

    public BuildingType getBuildingType() {
        return buildingType;
    }

    public int getHp() {
        return hp;
    }

    public int getLevel() {
        return level;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    public BuildingDefense getDefenses() {
        return defenses;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Building building = (Building) o;
        return Objects.equal(id, building.id) &&
                Objects.equal(hp, building.hp) &&
                Objects.equal(level, building.level) &&
                Objects.equal(buildingType, building.buildingType) &&
                Objects.equal(defenses, building.defenses) &&
                Objects.equal(location, building.location);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id, buildingType, hp, level, defenses, location);
    }
}

As is, hp and defenses show up as 0 and null respectively.按原样, hpdefenses显示为0null If I remove the @Transient tag it comes through.如果我删除@Transient标签,它就会通过。

As long as you use org.springframework.data.annotation.Transient it should work as expected. 只要您使用org.springframework.data.annotation.Transient它应该按预期工作。 Jackson knows nothing about spring-data and it ignores it's annotations. 杰克逊对弹簧数据一无所知,它忽略了它的注释。

Sample code, that works: 示例代码,有效:

interface PersonRepository extends CrudRepository<Person, String> {}
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
class Person {
    @Id
    private String id;
    private String name;
    @Transient
    private Integer age;

    // setters & getters & toString()
}
@RestController
@RequestMapping("/person")
class PersonController {
    private static final Logger LOG = LoggerFactory.getLogger(PersonController.class);
    private final PersonRepository personRepository;

    @Autowired
    PersonController(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @RequestMapping(method = RequestMethod.POST)
    public void post(@RequestBody Person person) {
        // logging to show that json deserialization works
        LOG.info("Saving person: {}", person);
        personRepository.save(person);
    }

    @RequestMapping(method = RequestMethod.GET)
    public Iterable<Person> list() {
        Iterable<Person> list = personRepository.findAll();
        // setting age to show that json serialization works
        list.forEach(foobar -> foobar.setAge(18));

        return list;
    }
}

Executing POST http://localhost:8080/person : 执行POST http://localhost:8080/person

{
    "name":"John Doe",
    "age": 40
}
  • Log output Saving person: Person{age=40, id='null', name='John Doe'} 日志输出Saving person: Person{age=40, id='null', name='John Doe'}
  • Entry in person collection: { "_id" : ObjectId("55886dae5ca42c52f22a9af3"), "_class" : "demo.Person", "name" : "John Doe" } - age is not persisted 进入person收集: { "_id" : ObjectId("55886dae5ca42c52f22a9af3"), "_class" : "demo.Person", "name" : "John Doe" } - 年龄不持久

Executing GET http://localhost:8080/person : 执行GET http://localhost:8080/person

  • Result: [{"id":"55886dae5ca42c52f22a9af3","name":"John Doe","age":18}] 结果: [{"id":"55886dae5ca42c52f22a9af3","name":"John Doe","age":18}]

The problem for you seems to be that both mongo and jackson are behaving as expected. 你的问题似乎是mongo和jackson都表现得像预期的那样。 Mongo does not persist the data and jackson ignores the property since it is marked as transient. Mongo不会保留数据,而jackson会忽略该属性,因为它被标记为瞬态。 I managed to get this working by 'tricking' jackson to ignore the transient field and then annotating the getter method with @JsonProperty . 我设法通过'欺骗'杰克逊来忽略瞬态场,然后使用@JsonProperty注释getter方法。 Here is my sample bean. 这是我的示例bean。

    @Entity
    public class User {

    @Id
    private Integer id;
    @Column
    private String username;

    @JsonIgnore
    @Transient
    private String password;

    @JsonProperty("password")
    public String getPassword() {
        return // your logic here;
    }
}

This is more of a work around than a proper solution so I am not sure if this will introduce any side effects for you. 这不仅仅是一个合适的解决方案,所以我不确定这是否会为您带来任何副作用。

carlos-bribiescas, what version are you using for it. carlos-bribiescas,你用的版本是什么? It could be version issue. 它可能是版本问题。 Because this transient annotation is meant only for not persisting to the mongo db. 因为这个瞬态注释只是为了不坚持到mongo db。 Please try to change the version.Probably similar to Maciej one (1.2.4 release) 请尝试更改版本。可能类似于Maciej one(1.2.4发布)

There was issue with json parsing for spring data project in one of the version. 在其中一个版本中存在json解析spring数据项目的问题。 http://www.widecodes.com/CyVjgkqPXX/fields-with-jsonproperty-are-ignored-on-deserialization-in-spring-boot-spring-data-rest.html http://www.widecodes.com/CyVjgkqPXX/fields-with-jsonproperty-are-ignored-on-deserialization-in-spring-boot-spring-data-rest.html

Since you are not exposing your MongoRepositories as restful endpoint with Spring Data REST it makes more sense to have your Resources/endpoint responses decoupled from your domain model, that way your domain model could evolve without impacting your rest clients/consumers. 由于您没有使用Spring Data RESTMongoRepositories为restful端点,因此将Resources/endpoint响应与域模型分离会更有意义,这样您的域模型就可以在不影响其他客户端/使用者的情况下发展。 For the Resource you could consider leveraging what Spring HATEOAS has to offer. 对于资源,您可以考虑利用Spring HATEOAS提供的功能。

I solved by using @JsonSerialize . 我用@JsonSerialize解决了。 Optionally you can also opt for @JsonDeserialize if you want this to be deserailized as well. 如果您希望将其除去,也可以选择@JsonDeserialize

@Entity
public class Article {

@Column(name = "title")
private String title;

@Transient
@JsonSerialize
@JsonDeserialize
private Boolean testing;
}

// No annotations needed here
public Boolean getTesting() {
    return testing;
}

public void setTesting(Boolean testing) {
    this.testing = testing;
}

I solved this question by implementing custom JacksonAnnotationIntrospector : 我通过实现自定义JacksonAnnotationIntrospector解决了这个问题:

@Bean
@Primary
ObjectMapper objectMapper() {
  Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
  AnnotationIntrospector annotationIntrospector = new JacksonAnnotationIntrospector() {
    @Override
    protected boolean _isIgnorable(Annotated a) {
      boolean ignorable = super._isIgnorable(a);
      if (ignorable) {
        Transient aTransient = a.getAnnotation(Transient.class);
        JsonIgnore jsonIgnore = a.getAnnotation(JsonIgnore.class);

        return aTransient == null || jsonIgnore != null && jsonIgnore.value();
      }
      return false;
    }
  };
  builder.annotationIntrospector(annotationIntrospector);
  return builder.build();
}

This code makes invisible org.springframework.data.annotation.Transient annotation for Jackson but it works for mongodb . 这段代码为Jackson制作了不可见的org.springframework.data.annotation.Transient注释,但它适用于mongodb

You can use the annotation org.bson.codecs.pojo.annotations.BsonIgnore instead of @transient at the fields, that MongoDB shall not persist.您可以在字段中使用注释 org.bson.codecs.pojo.annotations.BsonIgnore 而不是 @transient,MongoDB 不会持久化。

@BsonIgnore
private BuildingDefense defenses;

It also works on getters.它也适用于吸气剂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM