【问题标题】:Specify field is transient for MongoDB but not for RestController指定字段对于 MongoDB 是瞬态的,但对于 RestController 不是
【发布时间】:2015-09-07 08:37:45
【问题描述】:

我正在使用 spring-boot 来提供与 MongoDB 保持一致的 REST 接口。我正在使用“标准”依赖项为其提供动力,包括spring-boot-starter-data-mongodbspring-boot-starter-web

但是,在我的一些课程中,我有一些字段需要注释 @Transient,这样 MongoDB 就不会保留这些信息。但是,我确实希望在我的休息服务中发送此信息。不幸的是,MongoDB 和其余控制器似乎都共享该注释。因此,当我的前端收到 JSON 对象时,这些字段不会被实例化(但仍被声明)。删除注释允许字段通过 JSON 对象。

如何分别为 MongoDB 和 REST 配置瞬态?

这是我的课

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

照原样,hpdefenses 分别显示为 0null。如果我删除 @Transient 标签,它就会通过。

【问题讨论】:

标签: java spring mongodb rest spring-boot


【解决方案1】:

只要您使用org.springframework.data.annotation.Transient,它就会按预期工作。 Jackson 对 spring-data 一无所知,它忽略了它的注释。

有效的示例代码:

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

正在执行 POST http://localhost:8080/person:

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

正在执行 GET http://localhost:8080/person:

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

【讨论】:

  • 我有类似的情况,它似乎对我不起作用...当我指定瞬态字段时,JSON 对象中没有出现
  • 你用的是什么版本的mongo和spring-boot?
  • 最新 Spring Boot 启动项目提供的默认依赖项。我使用start.spring.io 创建项目。
  • 但是你还有 spring-boot-starter-parent 的版本,不是吗?
  • spring-boot-parent 1.2.4.RELEASE
【解决方案2】:

您的问题似乎是 mongo 和 jackson 的行为都符合预期。 Mongo 不会持久化数据,而 jackson 会忽略该属性,因为它被标记为瞬态。我设法通过“欺骗”杰克逊忽略瞬态字段,然后用@JsonProperty 注释getter 方法来实现这一点。这是我的示例 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;
    }
}

这更多的是一种解决方法,而不是一个适当的解决方案,所以我不确定这是否会给您带来任何副作用。

【讨论】:

  • 我认为这正好相反。我想要我的 JSON 对象中的字段。我不希望 MongoDB 持久化它。还是JsonIgnoreProperties的意思是会忽略@Transient这个注解?
  • 已经完全修改了我的答案......似乎没有合适的方法来做到这一点..
  • 嗯,这对我不起作用。你用的是什么版本的 mongo 和 spring-boot?
【解决方案3】:

我通过使用 @JsonSerialize 解决了。如果您也希望对它进行脱轨,您也可以选择 @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;
}

【讨论】:

    【解决方案4】:

    卡洛斯-布里比斯卡斯, 你用的是什么版本。可能是版本问题。因为这个瞬态注释仅用于不持久化到 mongo db。请尝试更改版本。可能类似于 Maciej 一(1.2.4 版本)

    在其中一个版本中,spring 数据项目的 json 解析存在问题。 http://www.widecodes.com/CyVjgkqPXX/fields-with-jsonproperty-are-ignored-on-deserialization-in-spring-boot-spring-data-rest.html

    【讨论】:

      【解决方案5】:

      由于您没有将您的MongoRepositories 暴露为带有Spring Data REST 的restful 端点,因此将您的Resources/endpoint 响应与您的域模型解耦更有意义,这样您的域模型就可以在不影响其余客户/消费者的情况下发展.对于资源,您可以考虑利用 Spring HATEOAS 提供的资源。

      【讨论】:

      • 我正在通过 REST 公开我的 Mongo Repos...这只是我在 mongo 中保存的内容和我在前端使用的内容的域模型。
      • 我明白,但是,你没有为此使用 Spring Data Rest,通过将域模型与资源/Rest 模型分开,你不会遇到此类问题
      【解决方案6】:

      我通过实现自定义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();
      }
      

      此代码使Jacksonorg.springframework.data.annotation.Transient 注释不可见,但它适用于mongodb

      【讨论】:

        【解决方案7】:

        您可以在字段中使用注解 org.bson.codecs.pojo.annotations.BsonIgnore 而不是 @transient,这样 MongoDB 将不会持久化。

        @BsonIgnore
        private BuildingDefense defenses;
        

        它也适用于 getter。

        【讨论】:

          猜你喜欢
          • 2017-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-04
          • 2012-10-25
          • 2010-12-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多