2

I have the following tables Customers, Roles, CustomerRoles. Customers and Roles have a Long PK and CustomerRoles obviously does the many to many mappings. Consider the Roles table to be a fixed table (but not hard coded) defined by the system. I.e. a table driven "enum". I.e. "Admin", "User", etc.

On the Java side, I have a Customer entity and a RoleEntity and I'm using Hibernate / JPA to map.

It's all working how it is now, but I wind up with Json that looks like this:

{
    "customerId": 100000,
    "firstName": "Bob",
    "lastName": "Jenkins",
    "roles": [
      {
        "name": "Admin"
      },
      {
        "name": "Super User"
      }
    ]
  },

What I really want is for it to look like:

"roles": [ "Admin", "Super User" ]

and internally have it FK'ed by the M2M table using the ids. Note the roleid field is set to json ignore, but it still leaves it as a object array rather then a string array.

Obviously, the strings need to be enforced against whats in the Roles table.

Any suggestions?

Customer entity:

@ApiModelProperty(notes="Id of the customer.", required=true, value="100000")
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@JsonProperty(access=Access.READ_ONLY)
private Long customerId = 0L;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="First name of the customer.", required=true, value="John")
private String firstName;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="Last name of the customer.", required=true, value="Smith")
private String lastName;
@ManyToMany(cascade={ CascadeType.PERSIST })
@JoinTable(name="CustomerRoles",
           joinColumns={ @JoinColumn(name="CustomerId") },
           inverseJoinColumns={ @JoinColumn(name="RoleId") }
)
private List<Role> roles = new ArrayList<>();

public Long getCustomerId() {
    return this.customerId;
}

public String getFirstName() {
    return this.firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return this.lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public List<Role> getRoles() {
    //return new ArrayList<Role>(this.roles);
    return this.roles;
}

Role entity:

@ApiModelProperty(notes="Id of the role.", required=true, value="1")
@Id
//@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long roleId;
@NotNull
@Size(min=2, max=64)
@ApiModelProperty(notes="Name of the role.", required=true, value="Admin")
private String name;

@JsonIgnore
public Long getRoleId() {
    return this.roleId;
}

public String getName() {
    return this.name;
}

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

1 Answer 1

3

Add a new method in your Customer entity

    @JsonProperty("roles")
    public List<String> getRolesAsStrList() {
        return this.roles
            .stream()
            .map(Role::getName)
            .collect(toList());
    }

And then add @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) on roles property

    //other annotations
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private List<Role> roles = new ArrayList<>();

And for POST to automatically bind from String to Role just add two constructor in Role.java

    public Role() {
    }

    public Role(String name) {
        this.name = name;
    }

This will automatically handle payload like this and create two role entity with name "Admin" and "Super User".

{
    "customerId": 100000,
    "firstName": "Bob",
    "lastName": "Jenkins",
    "roles": [ "Admin", "Super User"]
}

However, if you need the id field of role you need to manually handle it before persisting or you can keep name of the role as foreign key in CustomerRoles table assuming name of roles will be unique.

If you need more advanced serialization or deserialization you can write a custom JsonSerializer and JsonDeserializer for that. See JsonComponent if you are using spring boot to serve your api.

Sign up to request clarification or add additional context in comments.

4 Comments

Ya... I was kind of trying to do that for the get side of things. How would it work for the set side? I.e. if somebody posts a string array[] in for a POST request? Would I need to take the array and translate it internally to the List<Role> I assume? I was hoping there was a (as an old boss used to say) magic "bSerializeArrayAsStrings=true" property lol.
Extended my answer to handle post requests.
Hmm... interesting. Hibernate really is magic lol. I wrote a setRolesAsStringList(), but I'll try this out today. Thanks!
@SledgeHammer Thanks, but hibernate actually doesn't do it. It is done by Jakson while deserializing JSON string to object. You can also use custom JsonDeserializer for more advanced magic. If you are using spring boot look at JsonComponent

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.