0

I use Hibernate to create relations as shown below. As far as I remember, there was no need to set FK fields as required, but I am not sure if it is the case for Hibernate or JPA.

@Entity
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Exclude
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;
}

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String firstName;

    @Column(nullable = false, length = 20)
    private String lastName;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private Set<Comment> comments = new HashSet<>();

    public void addComment(Comment comment) {
        comments.add(comment);
        comment.setUser(this);
    }

    public void removeComment(Comment comment) {
        comments.remove(comment);
        comment.setUser(null);
    }
}

1- So, should I make userId field required?

2- For this kind of scenarios, I retrieve the entity using its id and then add this entity to the related entity before persist. For example, when I create a Comment, I check if the user by the given id exists and then add the returned user to the Comment entity before save. Is it a bad approach? Because we can also set comment.userId field before persist without retrieving from database. Which one is better?

Jack
  • 1
  • 21
  • 118
  • 236
  • 1
    It depends on the cardinality of the relation: If it is 0..1 or 0..* then a FK is not required, but if it is 1..1 or 1..* then the FK is required. – cyberbrain Feb 27 '23 at 17:34
  • Wonderful explanation in a short clause... Many thanks. – Jack Feb 27 '23 at 18:15
  • @cyberbrain You may probably have good ideas on https://stackoverflow.com/questions/75553186/why-class-diagram-methods-are-defined-in-the-same-class/75571681?noredirect=1#comment133350134_75571681 I would be happy if you share your opinions. regards – Jack Feb 27 '23 at 18:15
  • Nothing in JPA is 'mandatory' so I don't know what you mean; make it required if and only if you require it. Same as not-null on a basic. Better is subjective and not ideal for stack overflow questions - it entirely depends on application usage which you have not and likely cannot detail here. As for a basic mapping: Apps I've seen that don't know what is best can do both - a basic mapping and the reference mapping, allowing them to have the best of both worlds. As long as they understand the drawbacks, they can avoid the pitfalls of both (ie use lazy fetching and respect it). – Chris Feb 27 '23 at 19:54
  • As for why you might perform a CityId look up or some other id reference lookup and require the reference - it depends on if you trust and need to validate that ID value. If it is user provided, you may want to make sure they aren't inadvertently putting in a city ID of Detroit when they mean Chicago - or that they even gave a valid ID value that refers to a city instead of say a city name. If you trust the data, it is unnecessary overhead that affects performance. So it all depends on your app and what you actually need. Easiest is to start with a model that matches the data and tweak later. – Chris Feb 27 '23 at 19:59
  • @Chris Yes, you absolutely right and especially when the user select these data from a dropdown list probably no need to check. On the other hand, as far as I know, I should have add extra field to the entity to save these fields easily. For example, in order to make something e.g. employee.setCityId(), I need to add cityId field besides city to my Employee entity, right? If there is a better way, could you add an example to your answer pls? – Jack Feb 27 '23 at 20:45

1 Answers1

1
  1. It depends on your business rules. If a comment can exist without any user , then you can configure @ManyToOne as optional (i.e. default value). Otherwise you can consider to configure it as mandatory by @ManyToOne(fetch = FetchType.LAZY, optional=false) . Hibernate will help to validate a comment instance must have an user even though you do not define the related foreign key constraint in DB.

  2. It is perfectly valid to do it especially you have some non-trivial business logic which need to check on the user before adding a comment.

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • In that case, If the entity has 2-3 Foreign Key for example, an Employee entity has cityId, departmentId and professionId, then when we create an Employee, it is good practice to check city, department and profession by using their id values and findById() methods. ıs that true? I am always confused if this approach is not good or good :( – Jack Feb 27 '23 at 19:22
  • It depends always. The aim is that you need to have some way that you can report to the users if they provide invalid ID for city or department or profession. If you are 100% sure the user will always provide an valid ID and does not need to have any checking on its city/department/profession 's state, it is perfectly okay to directly save the employee without selecting city/department/profession first. – Ken Chan Feb 27 '23 at 19:45
  • Yes, you absolutely right and especially when the user select these data from a dropdown list probably no need to check. On the other hand, as far as I know, I should have add extra field to the entity to save these fields easily. For example, in order to make something e.g. `employee.setCityId()`, I need to add `cityId` field besides `city` to my Employee entity, right? If there is a better way, could you add an example to your answer pls? – Jack Feb 27 '23 at 20:44
  • By the way, voted up ;) – Jack Feb 27 '23 at 20:44
  • even the user can select the correct city from the dropdown list , the advanced user can always bypass your ui and call your API with an invalid cityId. And no need to add an extra cityId field in employee , just use `EntityManager.getReference()` to get the city instance and set it to the employee if you do not want to select city for saving employee. see my answer at [here](https://stackoverflow.com/a/54727589/339637) – Ken Chan Feb 27 '23 at 20:57
  • Sounds good. In this scene, is it the best way to set FK values of an entity without retrieving the entity of the FK value? If so, is there an equivalent for Spring Data JPA or Hibernate without using Entity manager? – Jack Feb 27 '23 at 21:03
  • yes if you do not want to have an additional select the entity for setting the relationship. The equivalent method in spring-data repository is `getReferenceById()` – Ken Chan Feb 27 '23 at 21:06
  • Could you give an example usage for it? By the way, do you mean Spring data JPA with spring-data repository? – Jack Feb 27 '23 at 21:14
  • already give you the example at [this](https://stackoverflow.com/a/54727589/339637).Just replace `entityManager.getReference()` with `repo. getReferenceById(xxx)` – Ken Chan Feb 27 '23 at 21:17
  • Thanks, I injected `EntityManager` to my service, but `entityManager.getReferenceById(cityId)` gives error as "Cannot resolve method 'getReferenceById' in 'EntityManager'". Any idea? Also tried with `EntityManager.getReferenceById(cityId)` but not works. – Jack Feb 27 '23 at 21:21
  • Should I use `cityRepository.getReferenceById(cityId)` ? – Jack Feb 27 '23 at 21:25
  • yes. use `cityRepository.getReferenceById(cityId)`. If not okay , please create another new question for it and let me know – Ken Chan Feb 27 '23 at 21:26
  • 1
    Thanks a lot amigo. It would probably ok. I think I will use this approach for retrieving and setting? >>> `Category category = categoryRepository.getReferenceById(request.getCategoryId()); recipe.setCategory(category);` – Jack Feb 27 '23 at 21:30
  • 1
    Is my last comment true? Just would like to confirm it, any reply please? – Jack Feb 28 '23 at 15:49
  • yes.it should work. amigo – Ken Chan Feb 28 '23 at 15:54
  • muchas gracias amigo :) regards... – Jack Feb 28 '23 at 16:08