Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Rest ManytoMany POST

First, let me explain my usecase. It's pretty straight forward. There is a User entity and a Service entity. I have ManytoMany association between User and Service using UserService as the Joined entity (joined table) Initially,there will be some set of users and some set of services. Users can subscribe to any Service at any point of time. In that case, an entry will be added to UserService. But, I am getting null pointer exception when i tried to create a new UserService association. I could create User and Service individually.

My Entities are : User.java

package dao.models;

import java.io.Serializable;
import javax.persistence.*;

import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="`user`", schema="emm")
public class User implements Serializable {
    public User() {
    }

    @Column(name="id", nullable=false, unique=true) 
    @Id 
    @GeneratedValue(generator="EMM_USER_ID_GENERATOR")  
    @org.hibernate.annotations.GenericGenerator(name="EMM_USER_ID_GENERATOR", strategy="native")    
    private long id;


    @ManyToOne(targetEntity=dao.models.Tenant.class, fetch=FetchType.LAZY)  
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})    
    @JoinColumns({ @JoinColumn(name="tenant_id", referencedColumnName="id", nullable=false) })  
    @org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.NO_PROXY)  
    private dao.models.Tenant tenant;

    @OneToOne(targetEntity=dao.models.Person.class, fetch=FetchType.LAZY)   
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
    @JoinColumns({ @JoinColumn(name="Person_id", nullable=false) }) 
    @org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.NO_PROXY)  
    private dao.models.Person person;

    @Column(name="password", nullable=true, length=255) 
    private String password;

    @Column(name="email", nullable=false, length=255)   
    private String email;

    @Column(name="status", nullable=true, length=255)   
    private String status;

    @ManyToMany(mappedBy="user", targetEntity=dao.models.TenantGroup.class) 
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.TenantGroup> group = new java.util.ArrayList<dao.models.TenantGroup>();

    @OneToMany(mappedBy="user", targetEntity=dao.models.UserService.class)  
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.UserService> userService = new java.util.ArrayList<dao.models.UserService>();

    public void setId(long value) {
        this.id = value;
    }

    public long getId() {
        return id;
    }



    public void setPassword(String value) {
        this.password = value;
    }

    public String getPassword() {
        return password;
    }

    public void setEmail(String value) {
        this.email = value;
    }

    public String getEmail() {
        return email;
    }

    public void setStatus(String value) {
        this.status = value;
    }

    public String getStatus() {
        return status;
    }

    public void setTenant(dao.models.Tenant value) {
        this.tenant = value;
    }

    public dao.models.Tenant getTenant() {
        return tenant;
    }

    public void setPerson(dao.models.Person value) {
        this.person = value;
    }

    public dao.models.Person getPerson() {
        return person;
    }

    public void setGroup(java.util.List<dao.models.TenantGroup> value) {
        this.group = value;
    }

    public java.util.List<dao.models.TenantGroup> getGroup() {
        return group;
    }




    public java.util.List<dao.models.UserService> getUserService() {
        return userService;
    }

    public void setUserService(
            java.util.List<dao.models.UserService> userService) {
        this.userService = userService;
    }

    public String toString() {
        return String.valueOf(getId());
    }

}

Service Entity

package dao.models;

import java.io.Serializable;
import javax.persistence.*;

import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="service", schema="emm")
public class Service implements Serializable {
    public Service() {
    }

    @Column(name="service_id", nullable=false, unique=true) 
    @Id 
    @GeneratedValue(generator="EMM_SERVICE_SERVICE_ID_GENERATOR")   
    @org.hibernate.annotations.GenericGenerator(name="EMM_SERVICE_SERVICE_ID_GENERATOR", strategy="native") 
    private long id;

    @Column(name="service_name", nullable=false, length=255)    
    @org.hibernate.annotations.Index(name="service_service_name")   
    private String serviceName;

    @Column(name="description", nullable=true, length=255)  
    private String description;

    @Column(name="app_key", nullable=false, length=255) 
    private String appKey;

    @Column(name="app_token", nullable=false, length=255)   
    private String appToken;

    @Column(name="learnmoreurl",  length=255)   
    private String learnMoreURL;

    @Column(name="trialurl",  length=255)   
    private String trialURL;

    @ManyToMany(mappedBy="service", targetEntity=dao.models.Device.class)   
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.Device> device = new java.util.ArrayList<dao.models.Device>();

    @OneToMany(mappedBy="service", targetEntity=dao.models.ServiceParam.class)  
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE})   
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.ServiceParam> serviceParams = new java.util.ArrayList<dao.models.ServiceParam>();

    @OneToMany(mappedBy="service", targetEntity=dao.models.TenantService.class) 
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.MERGE,
                                        org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})    
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.TenantService> tenantService = new java.util.ArrayList<dao.models.TenantService>();

    @OneToMany(mappedBy="service", targetEntity=dao.models.UserService.class)   
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE})   
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.UserService> userService = new java.util.ArrayList<dao.models.UserService>();

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getServiceName() {
        return serviceName;
    }


    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getAppKey() {
        return appKey;
    }

    public void setAppKey(String appKey) {
        this.appKey = appKey;
    }

    public String getAppToken() {
        return appToken;
    }

    public void setAppToken(String appToken) {
        this.appToken = appToken;
    }

    public String getLearnMoreURL() {
        return learnMoreURL;
    }
    public void setLearnMoreURL(String learnMoreURL) {
        this.learnMoreURL = learnMoreURL;
    }

    public String getTrialURL() {
        return trialURL;
    }

    public void setTrialURL(String trialURL) {
        this.trialURL = trialURL;
    }

    public java.util.List<dao.models.Device> getDevice() {
        return device;
    }


    public void setDevice(java.util.List<dao.models.Device> device) {
        this.device = device;
    }

    public java.util.List<dao.models.ServiceParam> getServiceParams() {
        return serviceParams;
    }

    public void setServiceParams(
            java.util.List<dao.models.ServiceParam> serviceParams) {
        this.serviceParams = serviceParams;
    }

    public java.util.List<dao.models.TenantService> getTenantService() {
        return tenantService;
    }

    public void setTenantService(
            java.util.List<dao.models.TenantService> tenantService) {
        this.tenantService = tenantService;
    }

    public java.util.List<dao.models.UserService> getUserService() {
        return userService;
    }

    public void setUserService(
            java.util.List<dao.models.UserService> userService) {
        this.userService = userService;
    }
    public String toString() {
        return String.valueOf(getId());
    }

}

And finally the join entity

UserService.java

package dao.models;

import java.io.Serializable;
import javax.persistence.*;
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="user_service" ,schema="emm")
public class UserService implements Serializable {
    public UserService() {
    }

    @Column(name="id", nullable=false, unique=true) 
    @Id 
    @GeneratedValue(generator="EMM_USER_SERVICE_ID_GENERATOR")  
    @org.hibernate.annotations.GenericGenerator(name="EMM_USER_SERVICE_ID_GENERATOR", strategy="native")    
    private long id;

    @ManyToOne(targetEntity=dao.models.User.class, fetch=FetchType.LAZY)    
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})    
    @JoinColumns({ @JoinColumn(name="user_id", referencedColumnName="id", nullable=false) })    
    @org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.NO_PROXY)  
    private dao.models.User user;

    @ManyToOne(targetEntity=dao.models.Service.class, fetch=FetchType.LAZY) 
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})    
    @JoinColumns({ @JoinColumn(name="service_id", referencedColumnName="service_id", nullable=false) }) 
    @org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.NO_PROXY)  
    private dao.models.Service service;

    @Column(name="param_name", nullable=false)  
    private String paramName;

    @Column(name="param_value", nullable=true)  
    private String paramValue;

    @OneToMany(mappedBy="userService", targetEntity=dao.models.UserServiceToken.class)  
    @org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
    @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)  
    private java.util.List<dao.models.UserServiceToken> userServiceToken = new java.util.ArrayList<dao.models.UserServiceToken>();



    public long getId() {
        return id;
    }



    public void setId(long id) {
        this.id = id;
    }



    public dao.models.User getUser() {
        return user;
    }



    public void setUser(dao.models.User user) {
        this.user = user;
    }



    public dao.models.Service getService() {
        return service;
    }



    public void setService(dao.models.Service service) {
        this.service = service;
    }



    public String getParamName() {
        return paramName;
    }



    public void setParamName(String paramName) {
        this.paramName = paramName;
    }



    public String getParamValue() {
        return paramValue;
    }



    public void setParamValue(String paramValue) {
        this.paramValue = paramValue;
    }



    public java.util.List<dao.models.UserServiceToken> getUserServiceToken() {
        return userServiceToken;
    }



    public void setUserServiceToken(
            java.util.List<dao.models.UserServiceToken> userServiceToken) {
        this.userServiceToken = userServiceToken;
    }



    public String toString() {
        return String.valueOf(getId());
    }

}

Now my issue, GET requests are working properly, But, I get null pointer exception when I try to create a new UserService.

POST : http://localhost:8080/em/api/userServices/ I am trying to associate user 1 with service 2 Request :

{
    "paramName": "p1",
    "paramValue": "v1",
    "service": {
        "href": `"http://localhost:8080/em/api/userServices/1/service/2"`
    },
    "user": {
        "href": `"http://localhost:8080/em/api/userServices/1/user/1"`
    }
}

Error Messgae :

{
    "cause": {
        "cause": {
            "cause": null,
            "message": null
        },
        "message": "(was java.lang.NullPointerException) (through reference chain: dao.models.UserService[\"service\"])"
    },
    "message": "Could not read JSON: (was java.lang.NullPointerException) (through reference chain: dao.models.UserService[\"service\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: dao.models.UserService[\"service\"])"
}

GET http://localhost:8080/em/api/userServices yields me the following output :

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/em/api/userServices{?page,size,sort}",
      "templated" : true
    }
  },
  "_embedded" : {
    "userServices" : [ {
      "paramName" : "p1",
      "paramValue" : "v1",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/em/api/userServices/1"
        },
        "userServiceToken" : {
          "href" : "http://localhost:8080/em/api/userServices/1/userServiceToken"
        },
        "user" : {
          "href" : "http://localhost:8080/em/api/userServices/1/user"
        },
        "service" : {
          "href" : "http://localhost:8080/em/api/userServices/1/service"
        }
      }
    }, {
      "paramName" : "pone",
      "paramValue" : "vone",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/em/api/userServices/2"
        },
        "userServiceToken" : {
          "href" : "http://localhost:8080/em/api/userServices/2/userServiceToken"
        },
        "user" : {
          "href" : "http://localhost:8080/em/api/userServices/2/user"
        },
        "service" : {
          "href" : "http://localhost:8080/em/api/userServices/2/service"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

Has anyone successfully implemented ManyToMany association using Spring-data-rest. If so, kindly help me in this regard

like image 459
user1917034 Avatar asked Mar 13 '14 13:03

user1917034


1 Answers

I figured out the issue and got it working.

Previously, my request body was:

{
    "paramName": "p1",
    "paramValue": "v1",
    "service": {
        "href": "http://localhost:8080/em/api/userServices/1/service/2"
    },
    "user": {
        "href": "http://localhost:8080/em/api/userServices/1/user/1"
    }
}

I figured out that it should be the following:

{
    "paramName": "p1",
    "paramValue": "v1",
    "service":  "http://localhost:8080/em/api/services/2",
    "user":  "http://localhost:8080/em/api/users/1"
 }

I feel there is still an issue with spring-data-rest. Kindly clarify, if anyone feels otherwise. Even with the fixed request, i was getting null constraint for ServiceId. I figured out in db, the primary key column for service was service_id. Even though, I have the entity mapping properly (My Id property in Service Entity maps properly to service_id in db), it was not working, I had to change the column name to id to get this working.

Spring-Data-Rest should depending upon the Entity mappings for Id right ? If so, then Still there is a bug.

Thanks, Vivek

like image 175
user1917034 Avatar answered Sep 29 '22 12:09

user1917034