Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,26 +1,55 @@
package com.codedifferently.lesson25.factory;

import com.codedifferently.lesson25.models.LibraryDataModel;
import com.codedifferently.lesson25.repository.LibraryGuestRepository;
import com.codedifferently.lesson25.repository.MediaItemRepository;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import com.codedifferently.lesson25.library.user.LibraryUserRepository;
import org.springframework.stereotype.Service;

/** A data loader that loads library data from a database. */
@Service
public final class LibraryDbDataLoader implements LibraryDataLoader {
import java.io.IOException;
import java.net.URL;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;

@Autowired private MediaItemRepository mediaItemsRepository;
@Autowired private LibraryGuestRepository libraryGuestRepository;
/**
* Loads the library data from the SQLite DB in resources/sqlite/library.db.
* Matches LibraryDataLoader#loadData() returning models.LibraryDataModel.
*/
@Service
public class LibraryDbDataLoader implements LibraryDataLoader {

@Override
public LibraryDataModel loadData() throws IOException {
var model = new LibraryDataModel();
// Locate sqlite/library.db on the classpath
URL url = Thread.currentThread()
.getContextClassLoader()
.getResource("sqlite/library.db");
if (url == null) {
throw new IllegalStateException("sqlite/library.db not found on classpath");
}

try {
// Put toURI() inside try so URISyntaxException is caught
String dbPath = Paths.get(url.toURI()).toString();

try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath)) {
var dataModel = new LibraryDataModel();

// TODO: Mirror your CSV/JSON loaders to populate these into dataModel:
// - mediaItems (dataModel.mediaItems or dataModel.setMediaItems(...))
// - guests (dataModel.guests or dataModel.setGuests(...))
// - checkoutsByEmail (dataModel.checkoutsByEmail or setter)
//
// Look at LibraryCsvDataLoader and LibraryFactory for the expected shapes.

model.mediaItems = mediaItemsRepository.findAll();
model.guests = libraryGuestRepository.findAll();
// Lesson 25: load users from DB
var userRepo = new LibraryUserRepository(connection);
dataModel.setUsers(userRepo.findAll());

return model;
return dataModel;
}
} catch (Exception e) {
// Wrap anything (SQL/URI) as IOException to satisfy the interface
throw new IOException("Failed loading DB data", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.codedifferently.lesson25.library.user;

public class LibraryUserModel {
private final String id;
private final String email;
private final String firstName;
private final String lastName;
private final String passwordHash;

public LibraryUserModel(String id, String email, String firstName, String lastName, String passwordHash) {
this.id = id;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.passwordHash = passwordHash;
}

public String getId() { return id; }
public String getEmail() { return email; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getPasswordHash() { return passwordHash; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.codedifferently.lesson25.library.user;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class LibraryUserRepository {
private final Connection connection;

public LibraryUserRepository(Connection connection) {
this.connection = connection;
}

public List<LibraryUserModel> findAll() throws Exception {
String sql = "SELECT id, email, first_name, last_name, password_hash FROM library_users";
try (PreparedStatement ps = connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
List<LibraryUserModel> users = new ArrayList<>();
while (rs.next()) {
users.add(new LibraryUserModel(
rs.getString("id"),
rs.getString("email"),
rs.getString("first_name"),
rs.getString("last_name"),
rs.getString("password_hash")
));
}
return users;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,62 +1,35 @@
package com.codedifferently.lesson25.models;

import com.codedifferently.lesson25.library.Book;
import com.codedifferently.lesson25.library.Dvd;
import com.codedifferently.lesson25.library.Librarian;
import com.codedifferently.lesson25.library.LibraryGuest;
import com.codedifferently.lesson25.library.Magazine;
import com.codedifferently.lesson25.library.MediaItem;
import com.codedifferently.lesson25.library.Newspaper;
import com.codedifferently.lesson25.library.Patron;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.codedifferently.lesson25.library.user.LibraryUserModel;

/**
* Data model used by loaders/factory.
* Includes legacy fields expected by CSV/Factory code + Lesson 25 users.
*
* Note: legacy collections are kept as raw types to avoid package coupling
* (the factory/CSV code already uses them and will compile with unchecked warnings).
*/
public class LibraryDataModel {

public List<MediaItemModel> mediaItems;
public List<LibraryGuestModel> guests;
// ===== Legacy fields expected by LibraryCsvDataLoader / LibraryFactory =====
// (CSV loader assigns to these directly: model.mediaItems, model.guests)
public List mediaItems = new ArrayList(); // e.g., List<MediaItem>
public List guests = new ArrayList(); // e.g., List<LibraryGuest>
public Map checkoutsByEmail = new HashMap(); // e.g., Map<String, List<CheckoutModel>>

public List<MediaItem> getMediaItems() {
List<MediaItem> results = new ArrayList<>();
for (MediaItemModel mediaItemModel : mediaItems) {
switch (mediaItemModel.type) {
case "book" ->
results.add(
new Book(
mediaItemModel.id,
mediaItemModel.title,
mediaItemModel.isbn,
mediaItemModel.authors,
mediaItemModel.pages));
case "dvd" -> results.add(new Dvd(mediaItemModel.id, mediaItemModel.title));
case "magazine" -> results.add(new Magazine(mediaItemModel.id, mediaItemModel.title));
case "newspaper" -> results.add(new Newspaper(mediaItemModel.id, mediaItemModel.title));
default ->
throw new IllegalArgumentException("Unknown media item type: " + mediaItemModel.type);
}
}
return results;
}
// Legacy getters used by LibraryFactory
public List getMediaItems() { return mediaItems; }
public List getGuests() { return guests; }
public Map getCheckoutsByEmail() { return checkoutsByEmail; }

public List<LibraryGuest> getGuests() {
List<LibraryGuest> results = new ArrayList<>();
for (LibraryGuestModel guestModel : this.guests) {
switch (guestModel.type) {
case "librarian" -> results.add(new Librarian(guestModel.name, guestModel.email));
case "patron" -> results.add(new Patron(guestModel.name, guestModel.email));
default -> throw new AssertionError();
}
}
return results;
}
// ===== Lesson 25: Users loaded from DB =====
private List<LibraryUserModel> users = new ArrayList<>();

public Map<String, List<CheckoutModel>> getCheckoutsByEmail() {
Map<String, List<CheckoutModel>> results = new HashMap<>();
for (LibraryGuestModel guest : this.guests) {
results.put(guest.email, guest.checkedOutItems);
}
return results;
}
public List<LibraryUserModel> getUsers() { return users; }
public void setUsers(List<LibraryUserModel> users) { this.users = users; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT type, COUNT(*) AS item_count
FROM media_items
GROUP BY type
ORDER BY type;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
SELECT g.id AS guest_id,
g.first_name,
g.last_name,
coi.item_id,
coi.checked_out_at,
coi.due_at,
coi.returned_at
FROM guests g
LEFT JOIN checked_out_items coi ON coi.guest_id = g.id
ORDER BY g.last_name, g.first_name;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT SUM(mi.pages) AS total_pages_checked_out
FROM checked_out_items coi
JOIN media_items mi ON mi.id = coi.item_id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS library_users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
password_hash TEXT NOT NULL
);

INSERT OR IGNORE INTO library_users (id, email, first_name, last_name, password_hash) VALUES
('11111111-1111-1111-1111-111111111111','joy@example.com','Joy','Brown','$2a$10$CwTycUXWue0Thq9StjUM0uJ8o2s2z9kV0xG0jVQjXZZZ3wG3o/3SO'),
('22222222-2222-2222-2222-222222222222','pyes@example.com','Pyes','Brown','$2a$10$CwTycUXWue0Thq9StjUM0uJ8o2s2z9kV0xG0jVQjXZZZ3wG3o/3SO'),
('33333333-3333-3333-3333-333333333333','zach@example.com','Zach','Brown','$2a$10$CwTycUXWue0Thq9StjUM0uJ8o2s2z9kV0xG0jVQjXZZZ3wG3o/3SO');
Loading