001package edu.pdx.cs.joy.family;
002
003import java.sql.*;
004import java.util.ArrayList;
005import java.util.List;
006import java.util.Map;
007
008/**
009 * Data Access Object implementation for managing Marriage entities in the database.
010 */
011public class MarriageDAOImpl implements MarriageDAO {
012
013  private final Connection connection;
014  private final Map<Integer, Person> personCache;
015
016  /**
017   * Creates a new MarriageDAOImpl with the specified database connection.
018   *
019   * @param connection the database connection to use
020   * @param personCache a cache of persons to resolve marriage partners
021   */
022  public MarriageDAOImpl(Connection connection, Map<Integer, Person> personCache) {
023    this.connection = connection;
024    this.personCache = personCache;
025  }
026
027  /**
028   * Drops the marriages table from the database if it exists.
029   *
030   * @param connection the database connection to use
031   * @throws SQLException if a database error occurs
032   */
033  public static void dropTable(Connection connection) throws SQLException {
034    try (Statement statement = connection.createStatement()) {
035      statement.execute("DROP TABLE IF EXISTS marriages");
036    }
037  }
038
039  /**
040   * Creates the marriages table in the database.
041   *
042   * @param connection the database connection to use
043   * @throws SQLException if a database error occurs
044   */
045  public static void createTable(Connection connection) throws SQLException {
046    try (Statement statement = connection.createStatement()) {
047      statement.execute(
048        "CREATE TABLE IF NOT EXISTS marriages (" +
049        "  husband_id INTEGER NOT NULL," +
050        "  wife_id INTEGER NOT NULL," +
051        "  marriage_date TIMESTAMP," +
052        "  location VARCHAR(255)," +
053        "  PRIMARY KEY (husband_id, wife_id)," +
054        "  FOREIGN KEY (husband_id) REFERENCES persons(id)," +
055        "  FOREIGN KEY (wife_id) REFERENCES persons(id)" +
056        ")"
057      );
058    }
059  }
060
061  /**
062   * Saves a marriage to the database.
063   *
064   * @param marriage the marriage to save
065   * @throws SQLException if a database error occurs
066   */
067  @Override
068  public void save(Marriage marriage) throws SQLException {
069    String sql = "INSERT INTO marriages (husband_id, wife_id, marriage_date, location) " +
070                 "VALUES (?, ?, ?, ?)";
071
072    try (PreparedStatement statement = connection.prepareStatement(sql)) {
073      statement.setInt(1, marriage.getHusband().getId());
074      statement.setInt(2, marriage.getWife().getId());
075
076      if (marriage.getDate() == null) {
077        statement.setNull(3, Types.TIMESTAMP);
078      } else {
079        statement.setTimestamp(3, new Timestamp(marriage.getDate().getTime()));
080      }
081
082      statement.setString(4, marriage.getLocation());
083
084      statement.executeUpdate();
085    }
086  }
087
088  /**
089   * Finds all marriages for a specific person ID.
090   *
091   * @param personId the person ID
092   * @return a list of marriages involving the person
093   * @throws SQLException if a database error occurs
094   */
095  @Override
096  public List<Marriage> findByPersonId(int personId) throws SQLException {
097    List<Marriage> marriages = new ArrayList<>();
098    String sql = "SELECT husband_id, wife_id, marriage_date, location " +
099                 "FROM marriages WHERE husband_id = ? OR wife_id = ?";
100
101    try (PreparedStatement statement = connection.prepareStatement(sql)) {
102      statement.setInt(1, personId);
103      statement.setInt(2, personId);
104
105      try (ResultSet resultSet = statement.executeQuery()) {
106        while (resultSet.next()) {
107          marriages.add(extractMarriageFromResultSet(resultSet));
108        }
109      }
110    }
111
112    return marriages;
113  }
114
115  /**
116   * Finds all marriages in the database.
117   *
118   * @return a list of all marriages
119   * @throws SQLException if a database error occurs
120   */
121  @Override
122  public List<Marriage> findAll() throws SQLException {
123    List<Marriage> marriages = new ArrayList<>();
124    String sql = "SELECT husband_id, wife_id, marriage_date, location FROM marriages";
125
126    try (Statement statement = connection.createStatement();
127         ResultSet resultSet = statement.executeQuery(sql)) {
128      while (resultSet.next()) {
129        marriages.add(extractMarriageFromResultSet(resultSet));
130      }
131    }
132
133    return marriages;
134  }
135
136  /**
137   * Deletes a marriage from the database.
138   *
139   * @param husbandId the husband's ID
140   * @param wifeId the wife's ID
141   * @throws SQLException if a database error occurs
142   */
143  @Override
144  public void delete(int husbandId, int wifeId) throws SQLException {
145    String sql = "DELETE FROM marriages WHERE husband_id = ? AND wife_id = ?";
146
147    try (PreparedStatement statement = connection.prepareStatement(sql)) {
148      statement.setInt(1, husbandId);
149      statement.setInt(2, wifeId);
150      int rowsAffected = statement.executeUpdate();
151
152      if (rowsAffected == 0) {
153        throw new SQLException("Delete failed, no marriage found for husband ID: " +
154                              husbandId + " and wife ID: " + wifeId);
155      }
156    }
157  }
158
159  /**
160   * Extracts a Marriage object from the current row of a ResultSet.
161   *
162   * @param resultSet the result set positioned at a marriage row
163   * @return a Marriage object with data from the result set
164   * @throws SQLException if a database error occurs
165   */
166  private Marriage extractMarriageFromResultSet(ResultSet resultSet) throws SQLException {
167    int husbandId = resultSet.getInt("husband_id");
168    int wifeId = resultSet.getInt("wife_id");
169
170    Person husband = personCache.get(husbandId);
171    Person wife = personCache.get(wifeId);
172
173    if (husband == null || wife == null) {
174      throw new SQLException("Cannot create marriage: Person not found in cache. " +
175                            "Husband ID: " + husbandId + ", Wife ID: " + wifeId);
176    }
177
178    Marriage marriage = new Marriage(husband, wife);
179
180    Timestamp marriageDate = resultSet.getTimestamp("marriage_date");
181    if (marriageDate != null) {
182      marriage.setDate(new java.util.Date(marriageDate.getTime()));
183    }
184
185    marriage.setLocation(resultSet.getString("location"));
186
187    return marriage;
188  }
189}
190