1

I'm new at entity frameworks, so pardon me if this seems silly.

Consider the following json object:

{
    "id": "8850a2d2-230e-8ecb-cbb8-06068df136aa",
    "fiscalCode": "QHNMPN27E68F765S",
    "role": {
        "id": "MED",
        "name": "Medico di base"
    },
    "enabled": true,
    "name": "Alessio",
    "surname": "De Padova",
    "contacts": [],
    "specs": [
        {
            "name": "Fisiatria",
            "id": "FISIA"
        },
         {
            "name": "Cardiologia",
            "id": "CARD"
        }
    ],
    "account": {
        "id": "5541",
        "email": "alessandro.spaghetti@gmail.com"
    },
    "submit": null
}

Let's assume we are sending it as a request body in order to update it in the database. It is a user which has subentities as shown here below:

@Id
@Column(name = "user_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;

@Column(name = "name")
@NotNull(message = "Name cannot be null")
@Size(min = 1, message = "Name is not long enough")
private String name;

@Column(name = "fiscal_code")
@NotNull(message = "Fiscal code cannot be null")
@Size(min=16, max=16, message = "Fiscal code has to be 16 chars long")
private String fiscalCode;

@Column(name = "enabled")
private Boolean enabled;

@Column(name = "surname")
@Size(min = 1, message = "Surname is not long enough")
@NotNull(message = "Surname cannot be null")
private String surname;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "account_id")
private AccountEntity account;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "role_id")
private RoleEntity role;

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id")
private Set<ContactEntity> contacts;

@OneToMany(cascade = CascadeType.ALL)
@JoinTable(
        name = "users_specs",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "spec_id")
)
private Set<SpecEntity> specs;

Now, here comes the problem. Whereas I want the userentity to be able to create a new account (or contact) or update an existing one (as it is happening right now), I don't want the same to happen with specs. A user can only add or remove specs, it has not the right to create new ones or change specs' properties.

What is the right way to configure this kind of behavior?

The following code shows what I would like to do using mainly the configuration:

  userEntity.setSpecs(
            userEntity
                    .getSpecs()
                    .stream()
                    .map(spec -> specSvc.get(spec.getId()))
                    .collect(Collectors.toSet())
    );

What I am doing right there is "correcting" each spec entity that is sent by the client. By correcting, I mean preventing name (or any other kind of property) change so that the user can only add or remove specs. But cannot update them or create them. If the server receives a spec not containing an id, it will throw an error

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Do you have different classes for you json and JPA entities? In almost every framework, it's desired to use different classes for these two concerns so each can evolve independently of each other as your problem shows. – Augusto Jan 07 '22 at 19:58
  • No, for the time being the controller receives the user entity object directly. Are you suggesting the controller receives a different kind of object that gets later converted to entity? – The problem with other minds Jan 08 '22 at 14:06
  • Hi Alessio! Sebastian answer below is on spot. I would recommend you to read about [Model-View-Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) to understand the separation and why it's desirable. In the case of APIs you can take the View as the JSON representation (or object). I would also recommend you to read the book [Implementing Domain-Driven Design](https://www.goodreads.com/book/show/15756865) as it goes quite deep (and with plenty of examples) into how to design apps – Augusto Jan 08 '22 at 18:12
  • Thanks for the book suggestion. I will definetely put it in my whishlist. I'm not new at MVC btw (all my rest apis are realized through that framework) and I completely understand why separation is necessary. The problem is to translate this to an entity-framework based application, if you know what I mean. But thanks to your, and Sebastian's, answer I'm making steps forward. Thanks really – The problem with other minds Jan 09 '22 at 12:41

1 Answers1

3

In general, don't let users post your Entity directly. This will lead to all kinds of different problems in the future anyway. In your example alone, it would be uncommon if a user client can set the database id. And if you ever let a user update their own account, you probably don't want them to be able to set a role.

I would create some DTO objects and either manually map or use some mapper to map these to the JPA entities.

There are some different approaches in How to properly convert domain entities to DTOs while considering scalability & testability (although this is about the opposite direction as your case), I am linking to my own answer there but there are various alternatives.

Sebastiaan van den Broek
  • 5,818
  • 7
  • 40
  • 73
  • I completely get your point. This is just an "excersise" and my main issue was how to manage parent-child relationships. I will study DTOs. Probably, they could be a good way to validate the request body as well (since I would like the controller, and not the repository, to block bad requests). – The problem with other minds Jan 09 '22 at 12:48