0

I want to create a spring boot rest controller with this specification :

Customers of an electricity and gas supply company can choose to receive their monthly bills either by email or by regular mail, neither or both.

My goal is to create java hibernate entities to manage these customers and their choices of sending bills.

A utility customer is identified by their email and can have multiple choice change events that change the customer choice status.

Each choice made by a customer generates a choice change event.

A choice change event relates to a customer. A customer can have multiple choice events.

Here are my java entities.

@Entity
@Table(name = "customers")
public class Customer {

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

    @Email(message="this field must respect the email format !")
    private String email;
    
    @ManyToOne
    private Choices choices;

}


@Entity
@Table(name = "choices")
public class Choices {

    @Id
    private String id;

    @Column(name = "email")
    private boolean isThisChoice;
    
    @OneToOne
    private Customer customer;

}

The resulting customer with id 24587 (GET request):
{
  "id": "24587",
  "email": "tartampion",
  "choices": [
    {
      "id": "regular mail",
      "isThisChoice": false
    },
    {
      "id": "email",
      "isThisChoice": true
    }
  ]
}

Must I have an entity of management of event of choice of the customer

ln3106
  • 113
  • 10
  • Your model doesn't make sense: you have a singular "choices' attribute in mapped as a ManyToOne - so many customers use a single 'email' choices instance, but how then can that single email choices instance reference a single customer? Try looking at how you might store the data in tables - that might help you map out entities in a way that might better fit your application use cases. – Chris Jan 09 '23 at 21:57

2 Answers2

0

Did you mean a model more like:

@Entity
@Table(name = "customers")
public class Customer {

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

    @Email(message="this field must respect the email format !")
    private String email;
    
    @ElementCollection
    @CollectionTable(name="Choices")
    @MapKeyColumn(name="CHOICE") //an "EMAIL" or "MAIL" string. You can use an enum instead if you want, but I wouldn't for upgrade reasons.
    @Column(name="enabled")
    private Map<String, Boolean> choices;

}

This will give you a Map of choices, resulting in JSON more like:

{
  "id": "24587",
  "email": "tartampion",
  "choices": {
      "MAIL": false,
      "EMAIL": true
    }
}

It should be much more expandable if you get other options and combinations in the future.

Similarly, you can use the same table structure with "Choices" as an entity:

@Entity
@Table(name = "customers")
public class Customer {

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

    @Email(message="this field must respect the email format !")
    private String email;

    @OneToMany(mappedBy = "customer")
    @MapKey(name="type") //don't need this if you want just a list
    private Map<String, Choice> choices;
}

@Entity
@Table(name = "choices")
public class Choice {
    @Id
    @OneToOne
    private Customer customer;

    @Id
    @Enumerated(EnumType.STRING)
    private ChoiceType type;

    @Column(name="enabled")
    private boolean enabled;
}

@IdClass(EmployeeId.class)
public class ChoiceId implements Serializable {
  private Integer customer;
  private ChoiceType type;
}

public enum ChoiceType {
    MAIL, 
    EMAIL;             
}
Chris
  • 20,138
  • 2
  • 29
  • 43
  • hi @Chris, choices must be an entity java which two different possible id : regular mail or email. a choice concerns one customer and the customer can update his choice any time. I must have two entities customer and choice. A customer can have many choices and a choice concerns one customer – ln3106 Jan 10 '23 at 10:41
  • Don't see how that doesn't match what you want- a choice is just an email:active key:value pair, and you can change a choice anytime through the customer entity. If you want to use entities, It'll look exactly the same in the database though, since the 'CHOICES' table will have the customer_id, choice and enabled flag, with many in there per customer. Limiting it two only two choices is something you'd do in the app - just make sure the map/collection only has the Email or Mail keys. I'll update with an example. – Chris Jan 10 '23 at 15:05
  • Hi @Chris, if i want to have a controller with post method to add a customer with his list of choices(two choices), how can i do it? I don't understand(ChoiceId class with @IdClass(EmployeeId.class) annotation). Can you complete please the example with a minimal crud operation (only add user with choices) by using jpa and service – ln3106 Jan 10 '23 at 19:36
  • I'm not sure what you don't understand - do you want what the JSON would look like? Just perform a GET request on the model with a customer with two choices and post that back. JSON though is not JPA - you'll want to differentiate how you serialize your model data to/from JSON differently than maybe what you use for JPA - starting with how you serialize circular references like the customer-choices mappings or you'll get a stack overflow. Choices aren't overly complex and I do not believe need to be their own entity, so I think the first option I had in there is the better one, but up to you. – Chris Jan 11 '23 at 17:08
0

Here you have to use Many to Many Mapping. Because one customer can have many choices and one choice can be opted by many customers.

package com.umesh.entity;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="customer")
public class Customer {

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

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

    @ManyToMany(fetch=FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
    @JoinTable(
            name="customer_choice",
            joinColumns=@JoinColumn(name="customer_id"),
            inverseJoinColumns=@JoinColumn(name="choice_id")
    )
    @LazyCollection(LazyCollectionOption.FALSE)
    private List<Choice> choices;

    public void Customer(){}

    public int getId() {
        return id;
    }

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

    public String getEmail() {
        return email;
    }

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

    public List<Choice> getChoices() {
        return choices;
    }

    public void setChoices(List<Choice> choices) {
        this.choices = choices;
    }

    public void addChoices(Choice choice){
        if(choices == null){
            choices = new ArrayList<>();
            choices.add(choice);
        }
        choices.add(choice);
    }
}

package com.umesh.entity;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name="choice")
public class Choice {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private int id;

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

    @ManyToMany(fetch=FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
    @JoinTable(
            name="customer_choice",
            joinColumns=@JoinColumn(name="choice_id"),
            inverseJoinColumns=@JoinColumn(name="customer_id")
    )
    @LazyCollection(LazyCollectionOption.FALSE)
    private List<Customer> customers;

    public void Choics(){}

    public int getId() {
        return id;
    }

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

    public String getChoice() {
        return choice;
    }

    public void setChoice(String choice) {
        this.choice = choice;
    }

    public List<Customer> getCustomers() {
        return customers;
    }

    public void setCustomers(List<Customer> customers) {
        this.customers = customers;
    }

    public void addCustomers(Customer customer){
        if(customers == null){
            customers = new ArrayList<>();
            customers.add(customer);
        }
        customers.add(customer);
    }
}
  • hi @Umesh Maurya I was thinking of a one to many relationship between customer and choice and a one to one relationship between choice and customer. How to have the get of a particular client with the list of his choices with the result of an endpoint like this { "id": "24587", "email": "tartampion", "choices": [ { "id": "regular mail", "isThisChoice": false }, { "id": "email", "isThisChoice": true } ] } – ln3106 Jan 10 '23 at 11:59
  • I think what you can do is, you have to make a GET method in the controller, that will get the details of a particular customer in object of a Customer class from the database, for that you will have to write an appropriate query. And if you use this many to many mapping, than it will create a join table named "customer_choice" . This table will relate the data of both tables. And also what you can do is, since you have only two choices, you can manually put them in the choices table. And from the id of choices table you can relate the customer table. – Umesh Maurya Jan 10 '23 at 12:12
  • Hi @Umesh Maurya Could you make rather a small example of get in the controller to see that. knowing that the post of the customer, the object json must send the identifier of the choice (postal mail or mail otherwise I return an error code to the browser) – ln3106 Jan 10 '23 at 12:50