diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 98f48a2..fed79a7 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,7 +1,7 @@ - + sqlite.xerial true org.sqlite.JDBC diff --git a/db/nursingHome.db b/db/nursingHome.db index b89b146..93edc75 100644 Binary files a/db/nursingHome.db and b/db/nursingHome.db differ diff --git a/src/main/java/de/hitec/nhplus/fixtures/MedicationFixture.java b/src/main/java/de/hitec/nhplus/fixtures/MedicationFixture.java index 8dc9f96..cc5b57d 100644 --- a/src/main/java/de/hitec/nhplus/fixtures/MedicationFixture.java +++ b/src/main/java/de/hitec/nhplus/fixtures/MedicationFixture.java @@ -1,17 +1,17 @@ package de.hitec.nhplus.fixtures; -import de.hitec.nhplus.Main; -import de.hitec.nhplus.datastorage.DaoFactory; -import de.hitec.nhplus.medication.Ingredient; -import de.hitec.nhplus.medication.Medication; -import de.hitec.nhplus.medication.database.MedicationDao; - import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.SQLException; import java.util.*; +import de.hitec.nhplus.Main; +import de.hitec.nhplus.datastorage.DaoFactory; +import de.hitec.nhplus.medication.Ingredient; +import de.hitec.nhplus.medication.Medication; +import de.hitec.nhplus.medication.database.MedicationDao; + /** * {@link Fixture} for {@link Medication}. * @@ -20,11 +20,13 @@ import java.util.*; public class MedicationFixture implements Fixture { private static final String SCHEMA = "/de/hitec/nhplus/medication/database/Medication.sql"; private static final String INGREDIENT_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Ingredient.sql"; + private static final String ALTERNATIVE_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Alternative.sql"; @Override public void dropTable(Connection connection) throws SQLException { connection.createStatement().execute("DROP TABLE IF EXISTS medication"); - connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient"); + connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient"); + connection.createStatement().execute("DROP TABLE IF EXISTS medication_alternative"); } @Override @@ -32,16 +34,25 @@ public class MedicationFixture implements Fixture { final InputStream schema = Main.class.getResourceAsStream(SCHEMA); final InputStream ingredientSchema = Main.class.getResourceAsStream(INGREDIENT_SCHEMA); + final InputStream alternativeSchema = Main.class.getResourceAsStream(ALTERNATIVE_SCHEMA); + assert schema != null; assert ingredientSchema != null; + assert alternativeSchema != null; + String SQL = new Scanner(schema, StandardCharsets.UTF_8) - .useDelimiter("\\A") - .next(); + .useDelimiter("\\A") + .next(); String ingredientSQL = ";" + new Scanner(ingredientSchema, StandardCharsets.UTF_8) - .useDelimiter("\\A") - .next(); + .useDelimiter("\\A") + .next(); + String alternativeSQL = ";" + new Scanner(alternativeSchema, StandardCharsets.UTF_8) + .useDelimiter("\\A") + .next(); + connection.createStatement().execute(SQL); connection.createStatement().execute(ingredientSQL); + connection.createStatement().execute(alternativeSQL); } @@ -67,86 +78,106 @@ public class MedicationFixture implements Fixture { Ingredient warfarinnatrium = new Ingredient("Warfarinnatrium"); medications.add(new Medication( - "Metformin", - "AstraZeneca", - List.of( - metforminhydrochlorid, - cellulose, - povidon, - magnesiumstearat - ), - "Übelkeit, Durchfall, Laktatazidose (selten)", - "Oral", - 100 + "Metformin", + "AstraZeneca", + List.of( + metforminhydrochlorid, + cellulose, + povidon, + magnesiumstearat + ), + "Übelkeit, Durchfall, Laktatazidose (selten)", + "Oral", + 100, + new ArrayList<>() )); medications.add(new Medication( - "Lisinopril", - "Teva Pharmaceuticals", - List.of( - lisinoprilDihydrat, - mannitol, - calciumphosphat, - magnesiumstearat - ), - "Schwindel, trockener Husten", - "Oral", - 150 + "Lisinopril", + "Teva Pharmaceuticals", + List.of( + lisinoprilDihydrat, + mannitol, + calciumphosphat, + magnesiumstearat + ), + "Schwindel, trockener Husten", + "Oral", + 150, + new ArrayList<>() )); medications.add(new Medication( - "Simvastatin", - "Mylan", - List.of( - simvastatin, - laktose, - cellulose, - magnesiumstearat - ), - "Muskelschmerzen, Leberprobleme(selten)", - "Oral", - 80 + "Simvastatin", + "Mylan", + List.of( + simvastatin, + laktose, + cellulose, + magnesiumstearat + ), + "Muskelschmerzen, Leberprobleme(selten)", + "Oral", + 80, + new ArrayList<>() )); medications.add(new Medication( - "Enoxaparin", - "Sanofi", - List.of( - enoxaparinNatrium, - benzylalkohol, - wasser - ), - "Blutungen, Reaktionen an der Injektionsstelle", - "Unterhautinjektion", - 120 + "Enoxaparin", + "Sanofi", + List.of( + enoxaparinNatrium, + benzylalkohol, + wasser + ), + "Blutungen, Reaktionen an der Injektionsstelle", + "Unterhautinjektion", + 120, + new ArrayList<>() )); medications.add(new Medication( - "Levothyroxin", - "Sandoz", - List.of( - levothyroxinnatrium, - laktose, - staerke, - akaziengummi - ), - "Herzrasen, Gewichtsverlust", - "Oral", - 90 + "Levothyroxin", + "Sandoz", + List.of( + levothyroxinnatrium, + laktose, + staerke, + akaziengummi + ), + "Herzrasen, Gewichtsverlust", + "Oral", + 90, + new ArrayList<>() )); medications.add(new Medication( - "Warfarin", - "Apotex Inc.", - List.of( - warfarinnatrium, - laktose, - staerke, - magnesiumstearat - ), - "Blutungen, Blutergüsse", - "Oral", - 110 + "Warfarin", + "Apotex Inc.", + List.of( + warfarinnatrium, + laktose, + staerke, + magnesiumstearat + ), + "Blutungen, Blutergüsse", + "Oral", + 110, + new ArrayList<>() )); MedicationDao dao = DaoFactory.getInstance().createMedicationDAO(); - Map medicationsByName = new HashMap<>(); for (Medication medication : medications) { dao.create(medication); + } + List createdMedications = dao.readAll(); + Map medicationsByName = new HashMap<>(); + for (Medication medication : createdMedications){ + switch (medication.getName()){ + case "Warfarin": + medication.setAlternativeMedication(List.of( + createdMedications.get(0), + createdMedications.get(3) + )); + break; + default: + break; + } + dao.update(medication); medicationsByName.put(medication.getName(), medication); } return medicationsByName; diff --git a/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java b/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java index 070c582..4af118a 100644 --- a/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java +++ b/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java @@ -48,6 +48,8 @@ public class AllMedicationController { private TableColumn columnAdministrationMethod; @FXML private TableColumn columnCurrentStock; + @FXML + public TableColumn columnAlternativeMedication; private final ObservableList medications = FXCollections.observableArrayList(); private MedicationDao dao; @@ -77,13 +79,29 @@ public class AllMedicationController { return new SimpleStringProperty( ingredients .stream() - .map(ingredient -> ingredient.getName()) + .map(Ingredient::getName) .collect(Collectors.joining("\n")) ); }); this.columnPossibleSideEffects.setCellValueFactory(new PropertyValueFactory<>("possibleSideEffects")); this.columnAdministrationMethod.setCellValueFactory(new PropertyValueFactory<>("administrationMethod")); this.columnCurrentStock.setCellValueFactory(new PropertyValueFactory<>("currentStock")); + this.columnAlternativeMedication.setCellValueFactory( + cellData -> { + Medication medication = cellData.getValue(); + List alternatives = medication.getAlternativeMedication(); + if (alternatives.isEmpty()) { + return new SimpleStringProperty(""); + } + + return new SimpleStringProperty( + alternatives + .stream() + .map(med -> med.getName() + ", " + med.getManufacturer()) + .collect(Collectors.joining("\n")) + ); + } + ); this.tableView.setItems(this.medications); } diff --git a/src/main/java/de/hitec/nhplus/medication/Medication.java b/src/main/java/de/hitec/nhplus/medication/Medication.java index 7fdce57..e7b965d 100644 --- a/src/main/java/de/hitec/nhplus/medication/Medication.java +++ b/src/main/java/de/hitec/nhplus/medication/Medication.java @@ -17,12 +17,13 @@ import java.util.stream.Collectors; */ public class Medication { private SimpleIntegerProperty id; - private final SimpleStringProperty name; - private final SimpleStringProperty manufacturer; - private final SimpleListProperty ingredients; - private final SimpleStringProperty possibleSideEffects; - private final SimpleStringProperty administrationMethod; - private final SimpleIntegerProperty currentStock; + private SimpleStringProperty name; + private SimpleStringProperty manufacturer; + private SimpleListProperty ingredients; + private SimpleStringProperty possibleSideEffects; + private SimpleStringProperty administrationMethod; + private SimpleIntegerProperty currentStock; + private SimpleListProperty alternativeMedication; /** * This constructor allows instantiating a {@link Medication} object, @@ -37,7 +38,8 @@ public class Medication { List ingredients, String possibleSideEffects, String administrationMethod, - int currentStock + int currentStock, + List alternativeMedication ) { this.name = new SimpleStringProperty(name); this.manufacturer = new SimpleStringProperty(manufacturer); @@ -45,6 +47,7 @@ public class Medication { this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects); this.administrationMethod = new SimpleStringProperty(administrationMethod); this.currentStock = new SimpleIntegerProperty(currentStock); + this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication)); } /** @@ -57,7 +60,8 @@ public class Medication { List ingredients, String possibleSideEffects, String administrationMethod, - int currentStock + int currentStock, + List alternativeMedication ) { this.id = new SimpleIntegerProperty(id); this.name = new SimpleStringProperty(name); @@ -66,6 +70,21 @@ public class Medication { this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects); this.administrationMethod = new SimpleStringProperty(administrationMethod); this.currentStock = new SimpleIntegerProperty(currentStock); + this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication)); + } + + public void replace(Medication medication){ + if(medication == null){ + return; + } + this.id = medication.idProperty(); + this.name = medication.nameProperty(); + this.manufacturer = medication.manufacturerProperty(); + this.ingredients = medication.ingredientsProperty(); + this.possibleSideEffects = medication.possibleSideEffectsProperty(); + this.administrationMethod = medication.administrationMethodProperty(); + this.currentStock = medication.currentStockProperty(); + this.alternativeMedication = medication.alternativeMedicationProperty(); } public int getId() { @@ -124,6 +143,18 @@ public class Medication { this.possibleSideEffects.set(possibleSideEffects); } + public ObservableList getAlternativeMedication() { + return alternativeMedication.get(); + } + + public SimpleListProperty alternativeMedicationProperty() { + return alternativeMedication; + } + + public void setAlternativeMedication(List alternativeMedication) { + this.alternativeMedication.set(FXCollections.observableArrayList(alternativeMedication)); + } + public String getAdministrationMethod() { return administrationMethod.get(); } @@ -162,6 +193,7 @@ public class Medication { .add("Possible Side Effects: " + this.getPossibleSideEffects()) .add("Administration Method: " + this.getAdministrationMethod()) .add("Current Stock: " + this.getCurrentStock()) + .add("Alternative Medication" + this.getAlternativeMedication()) .toString(); } diff --git a/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java b/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java index 608267b..fe82287 100644 --- a/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java +++ b/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java @@ -1,10 +1,9 @@ package de.hitec.nhplus.medication; -import java.util.List; - import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.control.Button; import javafx.scene.control.ListCell; @@ -13,6 +12,8 @@ import javafx.scene.layout.BorderPane; import javafx.scene.text.Text; import org.controlsfx.control.SearchableComboBox; +import java.util.List; + /** * A custom implementation of the {@link ListCell} for {@link Ingredient}s. * This implementation contains an automatic resizing of the parent {@link ListView}. @@ -21,20 +22,19 @@ import org.controlsfx.control.SearchableComboBox; */ public class MedicationListCell extends ListCell { private final Button deleteButton; - private SearchableComboBox comboBox; + private final SearchableComboBox comboBox; private static final double BUILTIN_BORDER_WIDTH = 1; private static final double CELL_SPACING = 4; private static final double CELL_PADDING = 4; private static final double BUTTON_PADDING_X = 8; private static final double BUTTON_PADDING_Y = 4; private final double totalSpacing; - private final List allOtherMedications; + private boolean firstUpdate = true; public MedicationListCell(List allOtherMedications) { - this.allOtherMedications = allOtherMedications; this.setPadding(new Insets(CELL_PADDING)); - comboBox = new SearchableComboBox(); + comboBox = new SearchableComboBox<>(); ObservableList list = FXCollections.observableArrayList(); list.setAll(allOtherMedications); comboBox.setItems(list); @@ -45,12 +45,12 @@ public class MedicationListCell extends ListCell { deleteButton = new Button("-"); deleteButton.setPadding(new Insets( - BUTTON_PADDING_Y, - BUTTON_PADDING_X, - BUTTON_PADDING_Y, - BUTTON_PADDING_X + BUTTON_PADDING_Y, + BUTTON_PADDING_X, + BUTTON_PADDING_Y, + BUTTON_PADDING_X )); - deleteButton.setOnAction(event -> getListView().getItems().remove(getItem())); + deleteButton.setOnAction(this::handleDeleteButton); // Calculate Delete Button Width Text textNode = new Text(deleteButton.getText()); @@ -58,34 +58,49 @@ public class MedicationListCell extends ListCell { double calculatedDeleteButtonWidth = textNode.getLayoutBounds().getWidth() + BUTTON_PADDING_X * 2; totalSpacing = BUILTIN_BORDER_WIDTH * 2 // List View - + CELL_PADDING * 2 - + CELL_SPACING - + calculatedDeleteButtonWidth; + + CELL_PADDING * 2 + + CELL_SPACING + + calculatedDeleteButtonWidth; } + + /** + * A Callback for use as a Listener for the {@link MedicationListCell#comboBox}. + */ private void onComboBoxChange( - ObservableValue observableValue, - Medication oldValue, - Medication newValue + ObservableValue observableValue, + Medication oldValue, + Medication newValue ) { + getItem().replace(comboBox.getValue()); + ListView listView = getListView(); double max = listView.lookupAll("*") - .stream() - .filter(node -> node instanceof MedicationListCell) - .mapToDouble(node -> getComboBoxWidth()) - .max() - .orElse(0); + .stream() + .filter(node -> node instanceof MedicationListCell) + .mapToDouble(node -> comboBox.getWidth()) + .max() + .orElse(0); listView.setMinWidth(max + totalSpacing); } - @Override protected void updateItem(Medication item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { setGraphic(null); } else { + if ( + firstUpdate + && comboBox.getSelectionModel().getSelectedItem() == null + && item.getName() != null + && comboBox.getItems().contains(item) + ) { + comboBox.getSelectionModel().select(item); + } + firstUpdate = false; + BorderPane cellPane = new BorderPane(); cellPane.setCenter(comboBox); cellPane.setRight(deleteButton); @@ -94,10 +109,6 @@ public class MedicationListCell extends ListCell { } } - private double getComboBoxWidth(){ - return comboBox.getWidth(); - } - private ListCell comboBoxFactory(Object param) { return new ListCell() { @@ -105,9 +116,9 @@ public class MedicationListCell extends ListCell { protected void updateItem(Medication med, boolean empty) { super.updateItem(med, empty); setText( - empty || med == null - ? null - : med.getComboBoxString() + empty || med == null + ? null + : med.getComboBoxString() ); } }; @@ -119,11 +130,17 @@ public class MedicationListCell extends ListCell { protected void updateItem(Medication med, boolean empty) { super.updateItem(med, empty); setText( - empty || med == null - ? comboBox.getPromptText() - : med.getComboBoxString() + empty || med == null + ? comboBox.getPromptText() + : med.getComboBoxString() ); } }; } + + private void handleDeleteButton(ActionEvent event) { + if (getItem() == null) { + getListView().getItems().remove(getItem()); + } + } } diff --git a/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java b/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java index fa89b39..12c7e9d 100644 --- a/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java +++ b/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java @@ -1,11 +1,5 @@ package de.hitec.nhplus.medication; -import static de.hitec.nhplus.utils.Validator.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; - import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -16,6 +10,15 @@ import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.stage.Stage; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static de.hitec.nhplus.utils.Validator.*; + /** * The controller for creating and editing a specific {@link Medication}. * @@ -53,9 +56,9 @@ public class MedicationModalController { */ @FXML public void initialize( - Stage stage, - AllMedicationController controller, - Medication medication + Stage stage, + AllMedicationController controller, + Medication medication ) { this.stage = stage; this.controller = controller; @@ -65,12 +68,13 @@ public class MedicationModalController { } else { isNewMedication = true; this.medication = new Medication( - "", - "", - new ArrayList<>(), - "", - "", - 0 + "", + "", + new ArrayList<>(), + "", + "", + 0, + new ArrayList<>() ); this.buttonSave.setDisable(true); } @@ -85,9 +89,9 @@ public class MedicationModalController { ChangeListener inputMedicationValidationListener = (observableValue, oldText, newText) -> { boolean isValid = isValidMedicationName(textFieldName.getText()) - && isValidMedicationManufacturer(textFieldManufacturer.getText()) - && isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText()) - && isValidStock(textFieldCurrentStock.getText()); + && isValidMedicationManufacturer(textFieldManufacturer.getText()) + && isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText()) + && isValidStock(textFieldCurrentStock.getText()); this.buttonSave.setDisable(!isValid); }; @@ -103,13 +107,18 @@ public class MedicationModalController { */ private void showData() { ingredients.setAll(medication.getIngredients()); + alternativeMediaction.setAll(medication.getAlternativeMedication()); + Map currentAlternatives = alternativeMediaction + .stream() + .collect(Collectors.toMap(Medication::getId, med -> med)); try { allOtherMedications = controller.getDao().readAll(); if (!isNewMedication) { allOtherMedications = allOtherMedications - .stream() - .filter(med -> med.getId() != medication.getId()) - .toList(); + .stream() + .filter(med -> med.getId() != medication.getId()) + .map(med -> currentAlternatives.getOrDefault(med.getId(), med)) + .toList(); } } catch (Exception exception) { exception.printStackTrace(); @@ -129,12 +138,21 @@ public class MedicationModalController { this.medication.setCurrentStock(Integer.parseInt(textFieldCurrentStock.getText())); this.medication.setPossibleSideEffects(textAreaPossibleSideEffects.getText()); this.medication.setIngredients(ingredients - .stream() - .map(Ingredient::getName) - .distinct() - .filter(Predicate.not(String::isEmpty)) - .map(Ingredient::new) - .toList() + .stream() + .map(Ingredient::getName) + .distinct() + .filter(Predicate.not(String::isEmpty)) + .map(Ingredient::new) + .toList() + ); + + this.medication.setAlternativeMedication( + alternativeMediaction + .stream() + .distinct() + .filter(Predicate.not(Objects::isNull)) + .filter(Predicate.not(med -> med.idProperty() == null)) + .toList() ); if (isNewMedication) { @@ -159,12 +177,13 @@ public class MedicationModalController { public void handleAddAlternativeMedication() { alternativeMediaction.add(new Medication( - null, - null, - List.of(), - null, - null, - -1 + null, + null, + new ArrayList<>(), + null, + null, + -1, + new ArrayList<>() )); } } diff --git a/src/main/java/de/hitec/nhplus/medication/database/MedicationDao.java b/src/main/java/de/hitec/nhplus/medication/database/MedicationDao.java index e226770..d953114 100644 --- a/src/main/java/de/hitec/nhplus/medication/database/MedicationDao.java +++ b/src/main/java/de/hitec/nhplus/medication/database/MedicationDao.java @@ -8,10 +8,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * The {@link MedicationDao} is an implementation of the{@link de.hitec.nhplus.datastorage.Dao Dao} @@ -63,36 +60,45 @@ public class MedicationDao implements Dao { ingredientStatement.setString(2, ingredient.getName()); ingredientStatement.execute(); } + + final String alternativeMedicationSQL = """ + INSERT INTO medication_alternative + (id, alternativeId) + VALUES (?, ?); + """; + for (Medication alternative : medication.getAlternativeMedication()) { + PreparedStatement alternativeStatement = this.connection.prepareStatement(alternativeMedicationSQL); + alternativeStatement.setInt(1, newId); + alternativeStatement.setInt(2, alternative.getId()); + alternativeStatement.execute(); + } } @Override public Medication read(int id) throws SQLException { - final String SQL = """ - SELECT medication.*, medication_ingredient.id - FROM medication - LEFT JOIN medication_ingredient ON medication.id = medication_ingredient.id - WHERE medication.id = ? - """; - PreparedStatement statement = this.connection.prepareStatement(SQL); - statement.setInt(1, id); - ResultSet result = statement.executeQuery(); + ResultSet result = getReadStatement(id).executeQuery(); return getInstanceFromResultSet(result); } @Override public List readAll() throws SQLException { final String SQL = """ - SELECT medication.*, medication_ingredient.name - FROM medication LEFT JOIN + SELECT medication.*, medication_ingredient.name, medication_alternative.alternativeId + FROM medication + LEFT JOIN medication_ingredient ON medication.id = medication_ingredient.id + LEFT JOIN + medication_alternative ON medication.id = medication_alternative.id """; ResultSet result = connection.prepareStatement(SQL).executeQuery(); - List medications = new ArrayList<>(); + Map medications = new HashMap<>(); Map> ingredientMap = new HashMap<>(); + Map> alternativesMap = new HashMap<>(); int currentMedicationId; int lastMedicationId = -1; + String latIngredient = ""; while (result.next()) { currentMedicationId = result.getInt(1); if (currentMedicationId != lastMedicationId) { @@ -103,27 +109,42 @@ public class MedicationDao implements Dao { new ArrayList<>(), result.getString(4), result.getString(5), - result.getInt(6) + result.getInt(6), + new ArrayList<>() ); - medications.add(medication); + medications.put(currentMedicationId, medication); } + List ingredients = ingredientMap.computeIfAbsent(currentMedicationId, k -> new ArrayList<>()); String ingredientName = result.getString(7); - if(ingredientName == null){ - continue; + if (ingredientName != null && !latIngredient.equals(ingredientName)) { + ingredients.add(new Ingredient(ingredientName)); + latIngredient = ingredientName; } - ingredients.add(new Ingredient(ingredientName)); + + Set alternatives = alternativesMap.computeIfAbsent(currentMedicationId, k -> new HashSet<>()); + int alternativeId = result.getInt(8); + if (alternativeId != 0) { + alternatives.add(alternativeId); + } + lastMedicationId = currentMedicationId; } - for (Medication medication : medications) { + for (Medication medication : medications.values()) { List ingredients = ingredientMap.get(medication.getId()); - if(ingredients.isEmpty()){ + if (ingredients.isEmpty()) { continue; } medication.setIngredients(ingredientMap.get(medication.getId())); + Set alternativeIds = alternativesMap.get(medication.getId()); + List alternatives = new ArrayList<>(); + for (Integer alternativeId : alternativeIds) { + alternatives.add(medications.get(alternativeId)); + } + medication.setAlternativeMedication(alternatives); } - return medications; + return medications.values().stream().toList(); } @Override @@ -164,6 +185,25 @@ public class MedicationDao implements Dao { statement.setString(2, ingredient.getName()); statement.execute(); } + + final String alternativeDeleteSQL = """ + DELETE FROM medication_alternative WHERE id = ? + """; + PreparedStatement alternativeStatement = this.connection.prepareStatement(alternativeDeleteSQL); + alternativeStatement.setInt(1, medication.getId()); + alternativeStatement.executeUpdate(); + + final String alternativeCreateSQL = """ + INSERT INTO medication_alternative + (id, alternativeId) + VALUES (?, ?); + """; + for (Medication alternative : medication.getAlternativeMedication()) { + PreparedStatement statement = this.connection.prepareStatement(alternativeCreateSQL); + statement.setInt(1, medication.getId()); + statement.setInt(2, alternative.getId()); + statement.execute(); + } } @Override @@ -176,6 +216,23 @@ public class MedicationDao implements Dao { preparedStatement.executeUpdate(); } + /** + * @param id The ID of the database entry to read. + * @return A {@link PreparedStatement} to read a specific entry by its ID. + */ + public PreparedStatement getReadStatement(int id) throws SQLException { + final String SQL = """ + SELECT medication.*, medication_ingredient.name, medication_alternative.alternativeId + FROM medication + LEFT JOIN medication_ingredient ON medication.id = medication_ingredient.id + LEFT JOIN medication_alternative ON medication.id = medication_alternative.id + WHERE medication.id = ? + """; + PreparedStatement statement = this.connection.prepareStatement(SQL); + statement.setInt(1, id); + return statement; + } + /** * Constructs a {@link Medication} object from the {@link ResultSet} obtained after executing a database query. * This method is used internally to map the {@link ResultSet} data to a {@link Medication} object. @@ -188,18 +245,40 @@ public class MedicationDao implements Dao { result.getInt(1), result.getString(2), result.getString(3), - List.of(), + new ArrayList<>(), result.getString(4), result.getString(5), - result.getInt(6) + result.getInt(6), + new ArrayList<>() ); List ingredients = new ArrayList<>(); + List alternatives = new ArrayList<>(); while (result.next()) { - ingredients.add(new Ingredient(result.getString(2))); - + String ingredientName = result.getString(7); + if (ingredientName != null) { + ingredients.add(new Ingredient(ingredientName)); + } + int alternativeId = result.getInt(8); + if (alternativeId != 0) { + ResultSet alternativeResult = getReadStatement(alternativeId).executeQuery(); + Medication alternativeMedication = new Medication( + alternativeResult.getInt(1), + alternativeResult.getString(2), + alternativeResult.getString(3), + new ArrayList<>(), + alternativeResult.getString(4), + alternativeResult.getString(5), + alternativeResult.getInt(6), + new ArrayList<>() + ); + alternatives.add(alternativeMedication); + } } medication.setIngredients(ingredients); + medication.setAlternativeMedication(alternatives); return medication; + + } } diff --git a/src/main/resources/de/hitec/nhplus/medication/database/Medication_Alternative.sql b/src/main/resources/de/hitec/nhplus/medication/database/Medication_Alternative.sql new file mode 100644 index 0000000..76e3364 --- /dev/null +++ b/src/main/resources/de/hitec/nhplus/medication/database/Medication_Alternative.sql @@ -0,0 +1,7 @@ +CREATE TABLE medication_alternative +( + id INTEGER NOT NULL , + alternativeId INTEGER NOT NULL , + FOREIGN KEY (id) REFERENCES medication (id) ON DELETE CASCADE, + FOREIGN KEY (alternativeId) REFERENCES medication (id) ON DELETE CASCADE +) \ No newline at end of file