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

Java 指定字段对于MongoDB是暂时的,但对于RestController不是,java,spring,mongodb,rest,spring-boot,Java,Spring,Mongodb,Rest,Spring Boot,我使用SpringBoot提供了一个与MongoDB持久化的REST接口。我正在使用“标准”依赖项为它供电,包括springbootstarterdatamongodb和springbootstarterweb 但是,在我的一些类中,我对字段进行了注释@Transient,这样MongoDB就不会保留这些信息。但是,我确实希望在rest服务中发送这些信息。不幸的是,MongoDB和rest控制器似乎都共享该注释。因此,当我的前端接收到JSON对象时,这些字段没有实例化(但仍然声明)。删除注释允许

我使用SpringBoot提供了一个与MongoDB持久化的REST接口。我正在使用“标准”依赖项为它供电,包括
springbootstarterdatamongodb
springbootstarterweb

但是,在我的一些类中,我对字段进行了注释
@Transient
,这样MongoDB就不会保留这些信息。但是,我确实希望在rest服务中发送这些信息。不幸的是,MongoDB和rest控制器似乎都共享该注释。因此,当我的前端接收到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);
    }
}
package com.clashalytics.domain.building;
导入com.clashalytics.domain.building.constants.BuildingConstants;
导入com.clashalytics.domain.building.constants.BuildingType;
导入com.google.common.base.Objects;
导入org.springframework.data.annotation.Id;
导入org.springframework.data.annotation.Transient;
导入java.util.*;
公共班级大楼{
@身份证
私有int-id;
私有BuildingType BuildingType;
私有整数级;
私人位置;
//待办事项http://stackoverflow.com/questions/30970717/specify-field-is-transient-for-mongodb-but-not-for-restcontroller
@短暂的
私人int hp;
@短暂的
私人建筑防御工事;
私有静态映射建筑=新HashMap();
公共建筑(){}
公共建筑(建筑类型建筑类型,内部级别){
this.buildingType=buildingType;
这个水平=水平;
if(BuildingConstants.hpMap.containsKey(buildingType))
this.hp=BuildingConstants.hpMap.get(buildingType.get)(级别-1);
this.defenses=BuildingDefense.get(buildingType,level);
}
公共静态建筑get(建筑类型市政厅,内部层){
建筑新候选人=新建筑(市政厅,楼层);
if(建筑物、集装箱(新候选者)){
返回建筑物。获取(新候选);
}
建筑物。放置(新候选,新候选);
返回新的候选人;
}
公共int getId(){
返回id;
}
公共字符串getName(){
返回buildingType.getName();
}
公共BuildingType getBuildingType(){
返回buildingType;
}
公共int getHp(){
返回hp;
}
public int getLevel(){
回报水平;
}
公共位置getLocation(){
返回位置;
}
公共无效设置位置(位置){
这个位置=位置;
}
公共建筑防御系统{
返回防御;
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
建筑物=(建筑物)o;
返回Objects.equal(id,building.id)&&
Objects.equal(hp,building.hp)&&
对象。相等(标高,建筑物。标高)&&
Objects.equal(buildingType,building.buildingType)&&
对象。相等(防御,建筑。防御)&&
对象。相等(位置,建筑。位置);
}
@凌驾
公共int hashCode(){
返回Objects.hashCode(id、buildingType、hp、级别、防御、位置);
}
}

按原样,
hp
防御
分别显示为
0
null
。如果我删除
@Transient
标记,它就会出现。

您的问题似乎是mongo和jackson的行为都与预期相符。Mongo不会持久化数据,jackson会忽略该属性,因为它被标记为瞬态。我通过“欺骗”jackson忽略瞬态字段,然后用
@JsonProperty
注释getter方法,成功地实现了这一点。这是我的样本豆

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

这与其说是一个合适的解决方案,不如说是一个变通办法,所以我不确定这是否会给您带来任何副作用。

只要您使用
org.springframework.data.annotation.Transient
它就应该可以正常工作。Jackson对spring数据一无所知,它忽略了它的注释

示例代码,有效:

interface PersonRepository extends CrudRepository<Person, String> {}
  • 日志输出
    Saving person:person{age=40,id='null',name='John Doe'}
  • person
    集合中的条目:
    {“_id”:ObjectId(“55886dae5ca42c52f22a9af3”),“_类”:“demo.Person”,“name”:“John Doe”}
    -年龄不会持久化
正在执行GET
http://localhost:8080/person

{
    "name":"John Doe",
    "age": 40
}
  • 结果:
    [{“id”:“55886dae5ca42c52f22a9af3”,“姓名”:“John Doe”,“年龄”:18}]
carlos Bobbiescas, 你用的是什么版本。可能是版本问题。因为此瞬态注释仅用于不持久化到mongo db。请尝试更改版本。可能类似于Maciej one(1.2.4版)

其中一个版本中的spring数据项目的json解析存在问题。

由于您没有将您的
MongoRepositories
公开为restful端点,因此将
资源/端点
响应与域模型分离更有意义,这样您的域模型就可以在不影响rest客户机/消费者的情况下发展。对于资源,你可以考虑利用什么来提供。

< P>我用<强> @ JSONSerialIt/Stult>解决了。如果您还希望反序列化,还可以选择@jsondeselliate

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

我通过实现定制的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();
}
这段代码使
org.springframework.data.annotation.Transient
annotation对
Jackson
不可见,但对
mongodb有效
@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;
}
@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();
}