[英]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-mongodb
和spring-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.按原样,
hp
和defenses
显示为0
和null
。 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
}
Saving person: Person{age=40, id='null', name='John Doe'}
Saving person: Person{age=40, id='null', name='John Doe'}
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
:
[{"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 REST将
MongoRepositories
为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.