-
Notifications
You must be signed in to change notification settings - Fork 6
Salem B #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Salem B #6
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,92 @@ | ||
| -- Write here your DDL statements to create the tables for the database. | ||
| CREATE TABLE cards | ||
| ( | ||
| card_id SERIAL PRIMARY KEY, | ||
| card_number TEXT UNIQUE NOT NULL, | ||
| card_issued_on DATE NOT NULL, | ||
| card_expires_on DATE NOT NULL | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a good opportunity to add a contraint on the issued en expiry dates: CHECK (card_expires_on > card_issued_on). Also consider placing date/timestamp contraints on the borrows table below. |
||
| ); | ||
|
|
||
| CREATE TABLE members | ||
| ( | ||
| member_id SERIAL PRIMARY KEY, | ||
| card_id INT UNIQUE, | ||
| member_email TEXT UNIQUE NOT NULL, | ||
| member_name TEXT NOT NULL, | ||
| member_phone TEXT NOT NULL, | ||
| member_address TEXT NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could be nice to further normalize addresses in a separate table. This allows working with e.g. postal codes to uniquely identify addresses. This would make address verification easier. |
||
| FOREIGN KEY (card_id) REFERENCES cards (card_id) ON DELETE CASCADE | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this cascading delete really what you intended? Now if you delete a card then the member will also be deleted. Also as card_id can be NULL it looks like ON DELETE SET NULL would be the intended choice. |
||
| ); | ||
|
|
||
| CREATE TABLE publishers | ||
| ( | ||
| publisher_id SERIAL PRIMARY KEY, | ||
| publisher_name TEXT UNIQUE NOT NULL | ||
| ); | ||
|
|
||
| CREATE TABLE authors | ||
| ( | ||
| author_id SERIAL PRIMARY KEY, | ||
| author_name TEXT UNIQUE NOT NULL | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although it is indeed rare, it could be possible that two different authors happen to have the same name. Relaxing the UNIQUE contraint allows for indicating that there are multiple different authors with the same name. |
||
| ); | ||
|
|
||
| CREATE TABLE tags | ||
| ( | ||
| tag_id SERIAL PRIMARY KEY, | ||
| tag_name TEXT UNIQUE NOT NULL | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tags are usually short so VARCHAR would be a better choice than arbitrary TEXT. |
||
| ); | ||
|
|
||
| CREATE TABLE books | ||
| ( | ||
| book_id SERIAL PRIMARY KEY, | ||
| book_isbn VARCHAR(13) UNIQUE NOT NULL, | ||
| book_title TEXT NOT NULL, | ||
| publisher_id INT NOT NULL, | ||
| FOREIGN KEY (publisher_id) REFERENCES publishers (publisher_id) | ||
| ); | ||
|
|
||
| CREATE TABLE book_authors | ||
| ( | ||
| book_id INT NOT NULL, | ||
| author_id INT NOT NULL, | ||
| PRIMARY KEY (book_id, author_id), | ||
| FOREIGN KEY (book_id) REFERENCES books (book_id) ON DELETE CASCADE, | ||
| FOREIGN KEY (author_id) REFERENCES authors (author_id) ON DELETE CASCADE | ||
| ); | ||
|
|
||
| CREATE TABLE book_tags | ||
| ( | ||
| book_id INT NOT NULL, | ||
| tag_id INT NOT NULL, | ||
| PRIMARY KEY (book_id, tag_id), | ||
| FOREIGN KEY (book_id) REFERENCES books (book_id) ON DELETE CASCADE, | ||
| FOREIGN KEY (tag_id) REFERENCES tags (tag_id) ON DELETE CASCADE | ||
| ); | ||
|
|
||
| CREATE TABLE shelves | ||
| ( | ||
| shelf_id SERIAL PRIMARY KEY, | ||
| shelf_code TEXT UNIQUE NOT NULL | ||
| ); | ||
|
|
||
| CREATE TABLE copies | ||
| ( | ||
| copy_id SERIAL PRIMARY KEY, | ||
| copy_barcode TEXT UNIQUE NOT NULL, | ||
| book_id INT NOT NULL, | ||
| shelf_id INT, | ||
| FOREIGN KEY (book_id) REFERENCES books (book_id) ON DELETE CASCADE, | ||
| FOREIGN KEY (shelf_id) REFERENCES shelves (shelf_id) | ||
| ); | ||
|
|
||
| CREATE TABLE borrows | ||
| ( | ||
| borrow_id SERIAL PRIMARY KEY, | ||
| member_id INT NOT NULL, | ||
| copy_id INT NOT NULL, | ||
| borrowed_at TIMESTAMP NOT NULL, | ||
| due_date TIMESTAMP NOT NULL, | ||
| returned_at TIMESTAMP, | ||
| fine_eur NUMERIC(8,2) NOT NULL DEFAULT 0.00, | ||
| FOREIGN KEY (member_id) REFERENCES members (member_id), | ||
| FOREIGN KEY (copy_id) REFERENCES copies (copy_id) | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,174 @@ | ||
| package org.hyf; | ||
|
|
||
| import java.sql.Connection; | ||
| import java.sql.SQLException; | ||
| import java.sql.DriverManager; | ||
| import java.sql.ResultSet; | ||
| import java.sql.PreparedStatement; | ||
|
|
||
| public class Main { | ||
|
|
||
| private static Connection getConnection() throws SQLException { | ||
| String url = "jdbc:postgresql://localhost:5432/test"; | ||
| String user = "hyfuser"; | ||
| String pass = "hyfpassword"; | ||
| return DriverManager.getConnection(url, user, pass); | ||
| } | ||
|
|
||
| public static void insertData() { | ||
| try (Connection conn = getConnection()) { | ||
|
|
||
| // 1. Publishers | ||
| PreparedStatement pub = conn.prepareStatement( | ||
| "INSERT INTO publishers (publisher_name) VALUES (?)" | ||
| ); | ||
| pub.setString(1, "O'Reilly"); | ||
| pub.executeUpdate(); | ||
| pub.setString(1, "Addison-Wesley"); | ||
| pub.executeUpdate(); | ||
|
|
||
| // 2. Authors | ||
| PreparedStatement auth = conn.prepareStatement( | ||
| "INSERT INTO authors (author_name) VALUES (?)" | ||
| ); | ||
| auth.setString(1, "Douglas Crockford"); | ||
| auth.executeUpdate(); | ||
| auth.setString(1, "Eric Freeman"); | ||
| auth.executeUpdate(); | ||
|
|
||
| // 3. Books | ||
| PreparedStatement book = conn.prepareStatement( | ||
| "INSERT INTO books (book_isbn, book_title, publisher_id) VALUES (?, ?, ?)" | ||
| ); | ||
| book.setString(1, "9780596517748"); | ||
| book.setString(2, "JavaScript: The Good Parts"); | ||
| book.setInt(3, 1); | ||
| book.executeUpdate(); | ||
|
|
||
| book.setString(1, "9780596007126"); | ||
| book.setString(2, "Head First Design Patterns"); | ||
| book.setInt(3, 1); | ||
| book.executeUpdate(); | ||
|
|
||
| // 4. Cards | ||
| PreparedStatement card = conn.prepareStatement( | ||
| "INSERT INTO cards (card_number, card_issued_on, card_expires_on) VALUES (?, ?, ?)" | ||
| ); | ||
| card.setString(1, "C-1008"); | ||
| card.setDate(2, java.sql.Date.valueOf("2024-01-30")); | ||
| card.setDate(3, java.sql.Date.valueOf("2027-01-30")); | ||
| card.executeUpdate(); | ||
|
|
||
| card.setString(1, "C-1004"); | ||
| card.setDate(2, java.sql.Date.valueOf("2023-11-20")); | ||
| card.setDate(3, java.sql.Date.valueOf("2026-11-20")); | ||
| card.executeUpdate(); | ||
|
|
||
| // 5. Members | ||
| PreparedStatement mem = conn.prepareStatement( | ||
| "INSERT INTO members (card_id, member_email, member_name, member_phone, member_address) VALUES (?, ?, ?, ?, ?)" | ||
| ); | ||
| mem.setInt(1, 1); | ||
| mem.setString(2, "omar@example.com"); | ||
| mem.setString(3, "Omar"); | ||
| mem.setString(4, "+31689012345"); | ||
| mem.setString(5, "Rijnkade 19, Utrecht"); | ||
| mem.executeUpdate(); | ||
|
|
||
| mem.setInt(1, 2); | ||
| mem.setString(2, "tom@example.com"); | ||
| mem.setString(3, "Tom"); | ||
| mem.setString(4, "+31645678901"); | ||
| mem.setString(5, "Biltstraat 88, Utrecht"); | ||
| mem.executeUpdate(); | ||
|
|
||
| // 6. Shelves | ||
| PreparedStatement shelf = conn.prepareStatement( | ||
| "INSERT INTO shelves (shelf_code) VALUES (?)" | ||
| ); | ||
| shelf.setString(1, "B-03"); | ||
| shelf.executeUpdate(); | ||
|
|
||
| // 7. Copies | ||
| PreparedStatement copy = conn.prepareStatement( | ||
| "INSERT INTO copies (copy_barcode, book_id, shelf_id) VALUES (?, ?, ?)" | ||
| ); | ||
| copy.setString(1, "BC-0011"); | ||
| copy.setInt(2, 1); | ||
| copy.setInt(3, 1); | ||
| copy.executeUpdate(); | ||
|
|
||
| copy.setString(1, "BC-0013"); | ||
| copy.setInt(2, 1); | ||
| copy.setInt(3, 1); | ||
| copy.executeUpdate(); | ||
|
|
||
| // 8. Borrows | ||
| PreparedStatement bor = conn.prepareStatement( | ||
| "INSERT INTO borrows (member_id, copy_id, borrowed_at, due_date, returned_at, fine_eur) VALUES (?, ?, ?, ?, ?, ?)" | ||
| ); | ||
| bor.setInt(1, 1); | ||
| bor.setInt(2, 1); | ||
| bor.setTimestamp(3, java.sql.Timestamp.valueOf("2025-12-02 08:08:00")); | ||
| bor.setTimestamp(4, java.sql.Timestamp.valueOf("2025-12-23 08:08:00")); | ||
| bor.setTimestamp(5, java.sql.Timestamp.valueOf("2025-12-13 08:08:00")); | ||
| bor.setBigDecimal(6, new java.math.BigDecimal("0.00")); | ||
| bor.executeUpdate(); | ||
|
|
||
| bor.setInt(1, 2); | ||
| bor.setInt(2, 2); | ||
| bor.setTimestamp(3, java.sql.Timestamp.valueOf("2026-01-10 08:51:00")); | ||
| bor.setTimestamp(4, java.sql.Timestamp.valueOf("2026-01-31 08:51:00")); | ||
| bor.setTimestamp(5, java.sql.Timestamp.valueOf("2026-01-25 08:51:00")); | ||
| bor.setBigDecimal(6, new java.math.BigDecimal("0.00")); | ||
| bor.executeUpdate(); | ||
|
|
||
| System.out.println("Inserted data successfully."); | ||
|
|
||
| } catch (SQLException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
|
|
||
| public static void fetchData() { | ||
| String sql = """ | ||
| SELECT | ||
| b.book_title, | ||
| p.publisher_name, | ||
| c.copy_barcode, | ||
| m.member_name, | ||
| br.borrowed_at, | ||
| br.returned_at | ||
| FROM borrows br | ||
| JOIN copies c ON br.copy_id = c.copy_id | ||
| JOIN books b ON c.book_id = b.book_id | ||
| JOIN publishers p ON b.publisher_id = p.publisher_id | ||
| JOIN members m ON br.member_id = m.member_id | ||
| ORDER BY br.borrowed_at | ||
| LIMIT 10 | ||
| """; | ||
|
|
||
| try (Connection conn = getConnection(); | ||
| PreparedStatement ps = conn.prepareStatement(sql); | ||
| ResultSet rs = ps.executeQuery()) { | ||
|
|
||
| System.out.println("- Borrows -"); | ||
|
|
||
| while (rs.next()) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fetchData query is very nice and returns a relevant non-technical view without primary keys. The program could present richer output based on the query. For example, like so: String row = rs.getString("member_name") + " -> " + |
||
| String row = rs.getString("member_name") + " -> " + | ||
| rs.getString("book_title") + " (" + | ||
| rs.getString("copy_barcode") + ")"; | ||
|
|
||
| System.out.println(row); | ||
| } | ||
|
|
||
| } catch (SQLException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
|
|
||
| public static void main(String[] args) { | ||
| System.out.println("You code goes here :) You can overwrite this entire method."); | ||
| insertData(); | ||
| fetchData(); | ||
| } | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compiled .class files do not belong in Git, but sometimes get accidentally commited. To make this easier a .gitignore file with the line: target/ would prevent build output to be accidentally committed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this is a well-structured schema with clear primary keys, foreign keys, and many-to-many relationship tables.