ArslanAnjum
ArslanAnjum

Reputation: 1724

Creating Resource with references using spring data rest

I am using spring data rest, I have following entities exposed via spring data rest

DonationRequest

@Data
@Entity
@Table(name="donation_request",schema="public")
public class DonationRequest {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="donation_request_id")
    Integer donationRequestId;

    @Column(name="expiry_datetime")
    Date expiryDatetime;

    @Column(name="blood_group")
    String bloodGroup;

    @Column(name="no_of_bottles")
    String noOfBottles;

    @OneToOne
    @JoinColumn(name="hospital_id")
    Hospital hospital;

    @OneToOne
    @JoinColumn(name="user_data_id")
    UserData requester;

    @Column(name="active")
    Boolean active;

}

Hospital

@Data
@Entity
@Table(name="hospital",schema="public")
public class Hospital {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="hospital_id")
    Integer hospitalId;

    @Column(name="name")
    String name;

    @Column(name="address")
    String address;

    @Column(name="loc",columnDefinition = "geometry")
    Point loc;

}

Now I have an android client which has the same class definitions as stated above. Hospitals are cached at startup on android client. Now I want to create a donationRequest entity on server. I can do that easily by posting json of donationRequest object to /api/donationRequests. this json contains hospital object also. But the newly created donationRequest and hospital are not linked together.

Following type of json in postman does not create link:

{
    "bloodGroup":"AB+",
    "hospital":{
        "hospitalId":1
    }
}

I know that following json does create link:

{
    "bloodGroup":"AB+",
    "hospital":"/api/hospitals/1"
}

My question is how can I create link using first type of json as that is the natural way to serialize dontaionRequest object from android client? Also I want hospitals to be exposed via /api/hospitals, so removing that rest resource is not an option.

Upvotes: 0

Views: 1063

Answers (2)

ArslanAnjum
ArslanAnjum

Reputation: 1724

It can be achieved by using a custom HttpMessageConverter and defining a custom content-type which can be anything other than standard (I used application/mjson):

MHttpMessageConverter.java

public class MHttpMessageConverter implements HttpMessageConverter<Object>{
    @Override
    public boolean canRead(Class<?> aClass, MediaType mediaType) {
        if (mediaType.getType().equalsIgnoreCase("application")
                && mediaType.getSubtype().equalsIgnoreCase("mjson"))
            return true;
        else
            return false;
    }

    @Override
    public boolean canWrite(Class<?> aClass, MediaType mediaType) {
        return false;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return new ArrayList<>(Arrays.asList(MediaType.APPLICATION_JSON));
    }

    @Override
    public Object read(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(httpInputMessage.getBody(),aClass);
        return obj;
    }

    @Override
    public void write(Object o, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {

    }
}

CustomRestConfiguration.java

@Configuration
public class CustomRestConfiguration extends RepositoryRestConfigurerAdapter {

    @Override
    public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        messageConverters.add(new MHttpMessageConverter());
    }
}

Upvotes: 2

Cepr0
Cepr0

Reputation: 30289

Spring Data REST is using HATEOAS. To refer to associated resources we have to use links to them:

Create a hospital first

POST /api/hospitals
{
    //...
}

response

{
    //...
    "_links": [
        "hostpital": "http://localhost/api/hospitals/1",
        //...
    ]
}

Then get 'hospital' (or 'self') link and add it to the 'donationRequests' payload

POST /api/donationRequests
{
    "bloodGroup":"AB+",
    "hospital": "http://localhost/api/hospitals/1"
}

Another approach - create first 'donationRequests' without hospital

POST /api/donationRequests
{
    //...
}

response

{
    //...
    "_links": [
        "hostpital": "http://localhost/api/donationRequests/1/hospital"
        //...
    ]
}

then PUT hospital to donationRequests/1/hospital using text link to hospital in your payload (pay attention to Content-Type: text/uri-list)

PUT http://localhost/api/donationRequests/1/hospital (Content-Type: text/uri-list)
http://localhost/api/hospitals/1

Info: Repository resources - The association resource

UPDATE

If it's necessary to deal without links to resources we have to make a custom rest controller.

Upvotes: 0

Related Questions