Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException on deserializing computed (non-persistent) property [DATAREST-574] #948

Open
spring-projects-issues opened this issue Jun 11, 2015 · 6 comments
Assignees
Labels
type: bug A general bug

Comments

@spring-projects-issues
Copy link

Ben Madore opened DATAREST-574 and commented

I have a project like:

@Entity
public class Store {
  @Id
  public Long id;

  @OneToMany 
  @JsonIgnore
  public Set<Hours> hours;
  
   @OneToMany
   @JsonIgnore
   public Set<HourOverrides> overrides;

  public TodaysHours getTodaysHoursComputedValue() {
      TodayHours todaysHours = //computation based on hours and overrides
      return todaysHours;
  }

  public setTodaysHoursComputedValue() {
  //Do nothing, just needed so that jackson has a corresponding setter to call, but value is discarded
  }

I have a separate manual controller that handles ```
/stores/{id}/hours


In DomainObjectReader there is the call to: 		
```java
final MappedProperties properties = getJacksonProperties(entity, mapper);
``` which returns all the properties jackson knows about. Then for each of those it calls:
```java
PersistentProperty<?> persistentProperty = entity.getPersistentProperty(property.getInternalName());

which in the case of this, returns null as this is a COMPUTED value and doesn't map to a persisted entity.

The problem is that even though there IS no persistent property it adds the null to a couple of java propertyToFieldName.put(persistentProperty, property.getName()); fieldNameToProperty.put(property.getName(), persistentProperty);
which are later dereferenced and leads to an NPE.

Caused by: java.lang.NullPointerException
	at org.springframework.data.mapping.model.BeanWrapper.getProperty(BeanWrapper.java:96) ~[spring-data-commons-1.11.0.M1.jar:?]
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.doMerge(DomainObjectReader.java:185) ~[spring-data-rest-webmvc-2.4.0.M1.jar:?]
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.merge(DomainObjectReader.java:136) ~[spring-data-rest-webmvc-2.4.0.M1.jar:?]
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.readPut(DomainObjectReader.java:130) ~[spring-data-rest-webmvc-2.4.0.M1.jar:?]
	at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyPut(JsonPatchHandler.java:134) ~[spring-data-rest-webmvc-2.4.0.M1.jar:?]

I believe the fix would be on DomainObjectReader.java:237 to add a null check before adding the nulls to those maps.

It seems like you should be able to have computed values in your response and to simply ignore them on the deserialization if the client happens to pass them. I know it's a bit of a hack in jackson to have a getter with an empty setter for computed properties - but this is getting full support in jackson 2.6 (FasterXML/jackson-databind#95) - and i believe it's a valid use case.

Please let me know if you disagree, or if you know of any work arounds.


Affects: 2.4 M1 (Gosling)

Issue Links:

  • DATAREST-575 Insufficient check for the presence of a PersistentProperty in MappedProperties
    ("duplicates")

2 votes, 5 watchers

@spring-projects-issues
Copy link
Author

Herman Meerlo commented

Ok this bug is blocking me as well. I'm trying to do a PUT on a resource which has a property for which I have made my own serializer and deserializer. The JSON output of this property is an object but the handling of the doMerge code assumes that every object in the uploaded resource must be a persisted entity, which is not the case off course

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

Would you guys mind trying the GA release (2.4.0.RELEASE) and see whether this still occurs? We tweaked a few things in the DomainObjectReader and might just have squashed this one without realizing :). I am not entirely getting this:

…code assumes that every object in the uploaded resource must be a persisted entity.

Every entity consists of two types of properties: (store) simple ones (i.e. properties of a type that can be handled by the store without conversion) and what we call a persistent entity, i.e. complex types that need to be mapped onto store simple types in a potentially recursive manner.

What would a property of a class, that's represented as a JSON object but not being a persistent entity look like? Any chance you provide a tiny sample that shows the issue?

@spring-projects-issues
Copy link
Author

Herman Meerlo commented

For me it is currently not possible to try the 2.4.0.RELEASE version because I'm using the spring-boot-starter as a dependency. But I'll try to explain where my code fails. I'm using a Postgis database where my entity has the following field:

@Column(columnDefinition = "Geometry")
@Type(type = "org.hibernate.spatial.GeometryType")
private Point location;

This is a native type supported by the database. But I serialize and deserialize this using a custom mapper which maps it to a JSON object:

{"lat":52.3,"lon":6.1}

So it is a complex type when it is received but after deserialization it is a simple property. I hope I have explained my case well enough...

@spring-projects-issues
Copy link
Author

Manuel Sousa commented

I've run into this situation, even though it was to provide support for a legacy client where i need to provide an "embedded" object which is not an entity but a response from another service outside spring data.

There's probably a better way to do this but i couldn't find out how and as such stumbled upon this issue.

I like the above suggestion of adding a null check before adding nulls to the map.

I'm currently using spring boot 1.3RC1 with spring-data-jpa 1.9

@spring-projects-issues
Copy link
Author

Gazal Gaffoor commented

We faced the same issue with spring-data-test-2.4.2.RELEASE and spring-data-jpa-1.9.2.RELEASE.

We were trying to save an entity with OneToOne mapping to another entity.

@Entity
@Table("person")
public class Person {

  @Id
  @GeneratedValue
  private Long id;

  @OneToOne
  @JoinColumn(name = "id")
  private PersonInfo info;

}

@Entity
@Table("person_info")
public class PersonInfo {

  @Id
  private Long id;

  @OneToOne
  @JoinColumn(name = "id")
  private Person person;
}

We wanted to save personInfo along with person. Only person had a repository defined. Even if we set @JsonIgnore on personInfo.person, we would get json serialization error. (related issue: https://jira.spring.io/browse/DATAREST-87)

When we add a repository for PersonInfo, we won't be able to send personInfo along with person cause only a link is expected for the property. So we tried adding a temporary transient variable from which data is copied with a method on @PrePersist and @PreUpdate. Create worked, but update failed with this issue, NullPointerException in BeanWrapper.java:96

Finally resorted to using @SecondaryTable to combine the data from both tables into the same entity.
Is there any workaround for this issue. Shouldn't a null check be simple enough to fix this?

There are requirements where we might want to save an entity and a lot of other related entities in one go, within the same transaction. So, what is the recommended approach to save such cascaded entities using spring-data. (also for @OneToMany related entities) ?

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

This suspiciously sounds like what we've fixed for DATAREST-575. Would you mind giving the latest snapshots a spin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants