-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBookDAO.java
More file actions
338 lines (292 loc) · 11.2 KB
/
Copy pathBookDAO.java
File metadata and controls
338 lines (292 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package dao;
import database.DatabaseConnection;
import models.Book;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* BookDAO.java - Data Access Object for Book entity
*
* Design Pattern: DAO (Data Access Object)
* Purpose: Encapsulates all database operations for books
* Benefits:
* - Separates business logic from database access
* - Easy to test (can mock this class)
* - Database changes don't affect UI code
*
* Methods: CRUD operations + custom queries
*/
public class BookDAO {
private static final Logger logger = Logger.getLogger(BookDAO.class.getName());
// SQL Queries
private static final String INSERT_BOOK =
"INSERT INTO books (title, author, isbn, publisher, publication_year, " +
"total_copies, available_copies, category, price) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String SELECT_BY_ID =
"SELECT * FROM books WHERE book_id = ?";
private static final String SELECT_ALL =
"SELECT * FROM books ORDER BY title ASC";
private static final String UPDATE_BOOK =
"UPDATE books SET title = ?, author = ?, isbn = ?, publisher = ?, " +
"publication_year = ?, total_copies = ?, available_copies = ?, " +
"category = ?, price = ? WHERE book_id = ?";
private static final String DELETE_BOOK =
"DELETE FROM books WHERE book_id = ?";
private static final String SELECT_BY_ISBN =
"SELECT * FROM books WHERE isbn = ?";
private static final String SELECT_BY_TITLE =
"SELECT * FROM books WHERE title LIKE ? ORDER BY title ASC";
private static final String UPDATE_COPIES =
"UPDATE books SET available_copies = ? WHERE book_id = ?";
private static final String GET_COPIES_COUNT =
"SELECT total_copies, available_copies FROM books WHERE book_id = ?";
// ===== CREATE Operations =====
/**
* Adds a new book to the database
*
* @param book Book object with details to insert
* @return true if insertion successful
* @throws Exception if book already exists or database error
*/
public boolean addBook(Book book) throws Exception {
// Validate book
if (book == null) {
throw new IllegalArgumentException("Book cannot be null");
}
// Check if ISBN already exists
if (getBookByISBN(book.getIsbn()) != null) {
throw new SQLException("Book with ISBN " + book.getIsbn() + " already exists");
}
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(INSERT_BOOK)) {
// Set parameters (indices start at 1 in JDBC)
pstmt.setString(1, book.getTitle());
pstmt.setString(2, book.getAuthor());
pstmt.setString(3, book.getIsbn());
pstmt.setString(4, book.getPublisher());
pstmt.setInt(5, book.getPublicationYear());
pstmt.setInt(6, book.getTotalCopies());
pstmt.setInt(7, book.getAvailableCopies());
pstmt.setString(8, book.getCategory());
pstmt.setDouble(9, book.getPrice());
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
logger.info("✓ Book added: " + book.getTitle());
return true;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error adding book", e);
throw e;
}
return false;
}
// ===== READ Operations =====
/**
* Retrieves a book by its ID
*
* @param bookId Book ID to search
* @return Book object if found, null otherwise
*/
public Book getBookById(int bookId) {
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(SELECT_BY_ID)) {
pstmt.setInt(1, bookId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return mapResultSetToBook(rs);
}
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error fetching book by ID", e);
}
return null;
}
/**
* Retrieves a book by ISBN
*
* @param isbn ISBN to search
* @return Book object if found, null otherwise
*/
public Book getBookByISBN(String isbn) {
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(SELECT_BY_ISBN)) {
pstmt.setString(1, isbn);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return mapResultSetToBook(rs);
}
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error fetching book by ISBN", e);
}
return null;
}
/**
* Retrieves all books from database
*
* @return List of all books
*/
public List<Book> getAllBooks() {
List<Book> books = new ArrayList<>();
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
books.add(mapResultSetToBook(rs));
}
logger.info("✓ Retrieved " + books.size() + " books from database");
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error fetching all books", e);
}
return books;
}
/**
* Searches books by title (partial match)
*
* @param titleKeyword Keyword to search in title
* @return List of matching books
*/
public List<Book> searchBooksByTitle(String titleKeyword) {
List<Book> books = new ArrayList<>();
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(SELECT_BY_TITLE)) {
// Use LIKE with wildcards for partial matching
pstmt.setString(1, "%" + titleKeyword + "%");
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
books.add(mapResultSetToBook(rs));
}
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error searching books", e);
}
return books;
}
// ===== UPDATE Operations =====
/**
* Updates book details
*
* @param book Book object with updated details
* @return true if update successful
*/
public boolean updateBook(Book book) {
if (book == null || book.getBookId() <= 0) {
throw new IllegalArgumentException("Invalid book data");
}
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(UPDATE_BOOK)) {
pstmt.setString(1, book.getTitle());
pstmt.setString(2, book.getAuthor());
pstmt.setString(3, book.getIsbn());
pstmt.setString(4, book.getPublisher());
pstmt.setInt(5, book.getPublicationYear());
pstmt.setInt(6, book.getTotalCopies());
pstmt.setInt(7, book.getAvailableCopies());
pstmt.setString(8, book.getCategory());
pstmt.setDouble(9, book.getPrice());
pstmt.setInt(10, book.getBookId());
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
logger.info("✓ Book updated: " + book.getTitle());
return true;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error updating book", e);
}
return false;
}
/**
* Updates available copies when book is issued/returned
*
* @param bookId Book ID to update
* @param newAvailableCount New available count
* @return true if update successful
*/
public boolean updateAvailableCopies(int bookId, int newAvailableCount) {
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(UPDATE_COPIES)) {
pstmt.setInt(1, newAvailableCount);
pstmt.setInt(2, bookId);
int affectedRows = pstmt.executeUpdate();
return affectedRows > 0;
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error updating available copies", e);
}
return false;
}
// ===== DELETE Operations =====
/**
* Deletes a book from database
* Note: Use with caution - may violate referential integrity
*
* @param bookId Book ID to delete
* @return true if deletion successful
*/
public boolean deleteBook(int bookId) {
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(DELETE_BOOK)) {
pstmt.setInt(1, bookId);
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
logger.info("✓ Book deleted: ID " + bookId);
return true;
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error deleting book", e);
}
return false;
}
// ===== Helper Methods =====
/**
* Maps a ResultSet row to a Book object
* Prevents code duplication across query methods
*
* @param rs ResultSet to map
* @return Book object populated with data
*/
private Book mapResultSetToBook(ResultSet rs) throws SQLException {
return new Book(
rs.getInt("book_id"),
rs.getString("title"),
rs.getString("author"),
rs.getString("isbn"),
rs.getString("publisher"),
rs.getInt("publication_year"),
rs.getInt("total_copies"),
rs.getInt("available_copies"),
rs.getString("category"),
rs.getDouble("price")
);
}
/**
* Checks if book with given ID exists
*
* @param bookId Book ID to check
* @return true if book exists
*/
public boolean bookExists(int bookId) {
return getBookById(bookId) != null;
}
/**
* Gets available books count
*
* @return Number of books with available copies
*/
public int getAvailableBooksCount() {
int count = 0;
String sql = "SELECT COUNT(*) as count FROM books WHERE available_copies > 0";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
count = rs.getInt("count");
}
} catch (SQLException e) {
logger.log(Level.SEVERE, "Error counting available books", e);
}
return count;
}
}