指定字段对于 MongoDB 是暂时的,但对于 RestController 不是
Specify field is transient for MongoDB but not for RestController
我正在使用 spring-boot 来提供一个与 MongoDB 保持一致的 REST 接口。我正在使用 'standard' 依赖项为其提供动力,包括 spring-boot-starter-data-mongodb
和 spring-boot-starter-web
.
但是,在我的某些 classes 中,我对某些字段进行了注释 @Transient
,以便 MongoDB 不会保留该信息。但是,我确实希望在我的休息服务中发送这些信息。不幸的是,MongoDB 和其余控制器似乎共享该注释。因此,当我的前端接收到 JSON 对象时,这些字段不会被实例化(但仍会声明)。删除注释允许字段在 JSON 对象中出现。
如何分别配置 MongoDB 和 REST 的瞬态?
这是我的 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
@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);
}
}
照原样,hp
和 defenses
分别显示为 0
和 null
。如果我删除 @Transient
标签,它就会通过。
您的问题似乎是 mongo 和 jackson 的行为都符合预期。 Mongo 不会保留数据,jackson 会忽略 属性,因为它被标记为瞬态。我设法让 'tricking' 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;
}
}
这与其说是一个合适的解决方案,不如说是一种变通方法,所以我不确定这是否会给您带来任何副作用。
只要您使用 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}]
carlos-bribiescas,
你用的是什么版本。可能是版本问题。因为此临时注释仅用于不持久保存到 mongo 数据库。请尝试更改 version.Probably 类似于 Maciej one(1.2.4 版本)
其中一个版本中 json 对 spring 数据项目的解析存在问题。
http://www.widecodes.com/CyVjgkqPXX/fields-with-jsonproperty-are-ignored-on-deserialization-in-spring-boot-spring-data-rest.html
因为您没有将 MongoRepositories
公开为 restful 端点 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. For the Resource you could consider leveraging what Spring HATEOAS 必须提供。
我用@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;
}
我通过实现自定义 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();
}
此代码使 Jackson
的 org.springframework.data.annotation.Transient
注释不可见,但它适用于 mongodb
。
您可以在字段中使用注释 org.bson.codecs.pojo.annotations.BsonIgnore 而不是 @transient,这样 MongoDB 将不会持续存在。
@BsonIgnore
private BuildingDefense defenses;
它也适用于吸气剂。
我正在使用 spring-boot 来提供一个与 MongoDB 保持一致的 REST 接口。我正在使用 'standard' 依赖项为其提供动力,包括 spring-boot-starter-data-mongodb
和 spring-boot-starter-web
.
但是,在我的某些 classes 中,我对某些字段进行了注释 @Transient
,以便 MongoDB 不会保留该信息。但是,我确实希望在我的休息服务中发送这些信息。不幸的是,MongoDB 和其余控制器似乎共享该注释。因此,当我的前端接收到 JSON 对象时,这些字段不会被实例化(但仍会声明)。删除注释允许字段在 JSON 对象中出现。
如何分别配置 MongoDB 和 REST 的瞬态?
这是我的 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
@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);
}
}
照原样,hp
和 defenses
分别显示为 0
和 null
。如果我删除 @Transient
标签,它就会通过。
您的问题似乎是 mongo 和 jackson 的行为都符合预期。 Mongo 不会保留数据,jackson 会忽略 属性,因为它被标记为瞬态。我设法让 'tricking' 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;
}
}
这与其说是一个合适的解决方案,不如说是一种变通方法,所以我不确定这是否会给您带来任何副作用。
只要您使用 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}]
carlos-bribiescas, 你用的是什么版本。可能是版本问题。因为此临时注释仅用于不持久保存到 mongo 数据库。请尝试更改 version.Probably 类似于 Maciej one(1.2.4 版本)
其中一个版本中 json 对 spring 数据项目的解析存在问题。 http://www.widecodes.com/CyVjgkqPXX/fields-with-jsonproperty-are-ignored-on-deserialization-in-spring-boot-spring-data-rest.html
因为您没有将 MongoRepositories
公开为 restful 端点 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. For the Resource you could consider leveraging what Spring HATEOAS 必须提供。
我用@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;
}
我通过实现自定义 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();
}
此代码使 Jackson
的 org.springframework.data.annotation.Transient
注释不可见,但它适用于 mongodb
。
您可以在字段中使用注释 org.bson.codecs.pojo.annotations.BsonIgnore 而不是 @transient,这样 MongoDB 将不会持续存在。
@BsonIgnore
private BuildingDefense defenses;
它也适用于吸气剂。