Ci troviamo nel mondo Spring dove possiamo far creare e far gestire le singole entità attraverso Spring Data.
Durante la fase di analisi è stato ipotizzato correttamente che abbiamo 3 Oggetti Java che possono estendere una Classe Astratta di riferimento. Se volessimo utilizzare questa rappresentazione avremmo una situazione in cui la classe astratta rappresenterebbe anche delle Entity (le classi che la estendono). Purtroppo questa situazione è tanto semplice nella realizzazione quanto complessa nell’utilizzo, ma di seguito ripoto un esempio che potrebbe illuminare un caso d’uso.
Il problema delle Survey
Supponiamo di voler realizzare un sistema che gestisca delle Survey (sia domande che risposte da parte degli utenti) come quell che ci propone Google Drive quando ci permette di realizzare delle Form.
Dobbiamo definire il nostro elemento base (rappresentazione delle singole domande) e successivamente andremo a definire più nello specifico i singoli tipi di domanda. Di seguito uno schema che potrebbe rappresentare il sistema nel modo migliore:
SimpleQuestion (Abstract Class) | +----------------+----------------+ | | | | | | Paragraph MultipleChoice LinearScale
Cosa rappresentano i singoli elementi sopra riportati?
- SimpleQuestion: rappresentazione della domanda, ovvero quella parte di codiche che ha come property la domanda (il testo della domanda)
- Paragraph: Rappresentazione della domanda a risposta aperta
- MultipleChoise: Rappresentazione della domanda a risposta multipla (sia in checkbox che in radio button)
- LinearScale: Rappresentazione della domanda che ha come risposta un valore (tipicamente una valutazione)
Ecco come verrà scritto il codice:
@Entity @Inheritance @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @Type(value = Paragraph.class), @Type(value = MultipleChoice.class), @Type(value = LinearScale.class), }) public abstract class SimpleQuestion implements Serializable { @javax.persistence.Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull private String question; public SimpleQuestion() { } public SimpleQuestion(Long id, String question) { this.id = id; this.question = question; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getQuestion() { return question; } public void setQuestion(String question) { this.question = question; } } |
Rappresentazione delle domande a risposta aperta
@Entity public class Paragraph extends SimpleQuestion { public Paragraph(){} @JsonCreator public Paragraph(@JsonProperty("id")Long id, @JsonProperty("question")String question) { // JSON Rappresentation super(id, question); } } |
Rappresentazione delle domande a risposta multipla
@Entity public class MultipleChoice extends SimpleQuestion { @NotNull private String mcType; @OneToMany(cascade = CascadeType.ALL) private Set options; public MultipleChoice(){} @JsonCreator public MultipleChoice(@JsonProperty("id")Long id, @JsonProperty("question")String question, @JsonProperty("mcType")String mcType, @JsonProperty("options")Set options) { // JSON Rappresentation super(id, question); this.mcType = mcType; //tipo di risposta, se radio o check this.options = options; //elenco delle risposte } public String getMcType() { return mcType; } public void setMcType(String mcType) { this.mcType = mcType; } public Set getOptions() { return options; } public void setOptions(Set options) { this.options = options; } } |
@Entity public class Option { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull private String response; // singola risposta della MultipleChoise public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getResponse() { return response; } public void setResponse(String response) { this.response = response; } } |
Rappresentazione delle domande per una valutazione
@Entity public class LinearScale extends SimpleQuestion { @NotNull private Integer maxValue; //massimo valore che può avere la scala per la valutazione public LinearScale(){} @JsonCreator public LinearScale(@JsonProperty("id")Long id, @JsonProperty("question")String question, @JsonProperty("maxValue")Integer maxValue) { // JSON Rappresentation super(id, question); this.maxValue = maxValue; } public Integer getMaxValue() { return maxValue; } public void setMaxValue(Integer maxValue) { this.maxValue = maxValue; } } |
Da notare il valore type nella SimpleQuestion, questa property serve a definire la tipologia di domanda quando viene eseguita una POST per creare una survey. Infatti il servizio rest da realizzare con la metodologia Spring dovrà essere chiamato come segue
curl -i -X POST -H "Content-Type": "application/json" http://localhost:8080/survey-spring-rest-module/api/survey/save |
{ "simpleQuestions": [ { "type" : "Paragraph", "question": "metti il tuo nome" }, { "type" : "MultipleChoice", "question": "Quale colore preferisci?", "mcType": "radio", "options": [ { "response": "Rosso" }, { "response": "Giallo" }, { "response": "Blue" } ] }, { "type" : "LinearScale", "maxValue": 10, "question": "Come valuti questa survey?" }, { "type" : "MultipleChoice", "question": "Quali parole ti identificano?", "mcType": "checkbox", "options": [ { "response": "Ingegnere" }, { "response": "Sviluppattore" }, { "response": "Spazzino" } ] } ], "title": "questa è una survey" } |
Conclusioni
In questo modo siamo riusciti a rappresentare una survey in manire dinamica, dove il type definisce il tipo di domanda e la parte frontend sarà in grado di visualizzare la pagina dedicata alle survey.
Edit per gestire i dtype
Potete notare che come informazione utilizzando le ricerche tramite Spring Data ritorni un valore chiamato type (sarebbe il valore nel Data Base che corrisponde all’attributo dtype).
Utilizando una custom query in Spring Data potrebbe non ritornare tale informazione a meno che non impostiate tale informazione come segue:
@Column(insertable = false, updatable = false) private String dtype; |