How to pass @EmbeddedId in POST json in spring-boot data rest

ivange94 :

I have built a REST API using Spring Boot Data REST. I'm using an embeddedId and have also implemented a BackendIdConverter.

Below is my Embeddable class

@Embeddable
public class EmployeeIdentity implements Serializable {
    @NotNull
    @Size(max = 20)
    private String employeeId;

    @NotNull
    @Size(max = 20)
    private String companyId;

    public EmployeeIdentity() {}

    public EmployeeIdentity(String employeeId, String companyId) {
        this.employeeId = employeeId;
        this.companyId = companyId;
    }

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public String getCompanyId() {
        return companyId;
    }

    public void setCompanyId(String companyId) {
        this.companyId = companyId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EmployeeIdentity that = (EmployeeIdentity) o;

        if (!employeeId.equals(that.employeeId)) return false;
        return companyId.equals(that.companyId);
    }

    @Override
    public int hashCode() {
        int result = employeeId.hashCode();
        result = 31 * result + companyId.hashCode();
        return result;
    }
}

Here's my Employee model

@Entity
@Table(name = "employees")
public class Employee {

    @EmbeddedId
    private EmployeeIdentity id;

    @NotNull
    @Size(max = 60)
    private String name;

    @NaturalId
    @NotNull
    @Email
    @Size(max = 60)
    private String email;

    @Size(max = 15)
    @Column(name = "phone_number", unique = true)
    private String phoneNumber;

    public Employee() {}

    public Employee(EmployeeIdentity id, String name, String email, String phoneNumber) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.phoneNumber = phoneNumber;
    }

    public EmployeeIdentity getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

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

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
}

And to have resource links generated properly using my embedded id instead of a qualified class name

@Component
public class EmployeeIdentityIdConverter implements BackendIdConverter {

    @Override
    public Serializable fromRequestId(String id, Class<?> aClass) {
        String[] parts = id.split("_");
        return new EmployeeIdentity(parts[0], parts[1]);
    }

    @Override
    public String toRequestId(Serializable source, Class<?> aClass) {
        EmployeeIdentity id = (EmployeeIdentity) source;
        return String.format("%s_%s", id.getEmployeeId(), id.getCompanyId());
    }

    @Override
    public boolean supports(Class<?> type) {
        return Employee.class.equals(type);
    }
}

And here's my repository code

@RepositoryRestResource(collectionResourceRel = "employees", path = "employees")
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, EmployeeIdentity> {
}

This works fine with GET requests but I need to be able to POST. The first thing I noticed that when I do a POST with the json

{
  "id": {
       "employeeId": "E-267", 
       "companyId": "D-432"
  },
  "name": "Spider Man", 
  "email": "[email protected]", 
  "phoneNumber": "+91-476253455"
}

This doesn't work. EmployeeIdentityIdConverter#fromRequestId throws a null pointer exception because the string parameter is null. So I added a null check and return default EmployeeIdentity when id is null. As described by this answer https://stackoverflow.com/a/41061029/4801462

Modified EmployeeIdentityIdConverter#fromRequestId

@Override
public Serializable fromRequestId(String id, Class<?> aClass) {
    if (id == null) {
        return new EmployeeIdentity();
    }
    String[] parts = id.split("_");
    return new EmployeeIdentity(parts[0], parts[1]);
}

But this raised another problem. My implementations for hashCode and equals now through null pointer exceptions since the default constructor was used and the employeeId and companyId are null.

In an attempt to fix this, I gave default values to employeeId and companyId

**Modified Employee#Employee() constructor*

public Employee() {
    this.employeeId = "";
    this.companyId = "";
}

NOTE

I am not even sure of what I was doing above. I was just trying to fix the small problems as they occurred.

By the way if you guessed this didn't work then you're right. While I didn't get an error and the request was successful, I didn't get the behavior I expected. A new entry was created with empty employeeId and companyId.

How do make POST to REST API whose model uses @EmbeddedId with spring boot data rest?

Selindek :

Here is an other solution. (Still not perfect though.)

Expose the id for your Employee class:

@Configuration
  protected class MyRepositoryRestConfigurer implements RepositoryRestConfigurer {

   @Override
   public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
     config.exposeIdsFor(ThemeMessage.class);
   }
}

Add the following line to your converter (during POST requests the id will be null):

@Override
public Serializable fromRequestId(String id, Class<?> aClass) {
    if(id==null) {
      return null;
    }
    String[] parts = id.split("_");
    return new EmployeeIdentity(parts[0], parts[1]);
}

The following POST request then will work:

{
  "id": {
       "employeeId": "E-267", 
       "companyId": "D-432"
  },
  "name": "Spider Man", 
  "email": "[email protected]", 
  "phoneNumber": "+91-476253455"
}

However, the id field will be exposed in all of the responses. But maybe it's not a real problem, because when you use a composite id it usually means that the id is not only an abstract identifier, but its parts have meaningful content which should appear in the entity body.

Actually, I'm thinking of adding these lines to my own code too .... :)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=98229&siteId=1