异常:Error: executeQueryForObject returned too many results.

本文记录了一次因SQL查询返回了多个结果而非预期的单一结果导致的UncategorizedSQLException异常。异常发生在使用Spring框架和iBatis进行数据库操作时,具体表现为在尝试获取唯一对象时接收到多个结果。

Thanks to your work so far, the library now has a functioning system. However, librarians have reported that some records in the CSV files are corrupted with invalid values. This has caused misbehaviour in the system, sometimes even crashing during busy exam periods, creating unnecessary stress for students. In real-world software engineering, robustness and correctness are just as important as functionality. Programs must gracefully handle unexpected situations and invalid inputs without failing. Two key approaches developers use are: Assertions: Internal checks that validate assumptions made by the programmer (e.g., "This variable should never be negative"). Assertions are mainly used during development and debugging. Defensive programming: Validates inputs and usage patterns to prevent invalid or unexpected behaviour at runtime. This includes checking for correct types, value ranges, and providing helpful error messages when something goes wrong. Description In this task, you will extend your library system by adding custom error handling to make it more robust. Python already raises built-in errors, but here you must define and use custom exceptions (in custom_errors.py) to handle library-specific error conditions. Building upon Task 3, update your user.py and book.py modules to handle invalid input, and modify task4.py to handle exceptions gracefully so that the system does not crash. You must ensure that this system is: Correct: Through use of assert to validate assumptions in your logic. Safe: Through defensive programming techniques that prevent invalid inputs (e.g. wrong types, negative amounts). In this task we will be using custom errors as python will already raise some of these errors when we execute invalid code. Our new errors are located in cusom_errors.py. You must use these custom errors, please see rubric for related penalties in this task. After auditing the CSV data, the librarian has introduced new validation rules to be applied in addition to the requirements described in Tasks 1–3. These rules strengthen data quality and prevent corrupted records from entering the system. Always include a helpful and descriptive error message when raising errors. Doing so contributes to the readability and overall quality of your assignment. Users Every user must have a password. Passwords cannot be empty. Every word in a user's name must contain only alphabetic characters. Numbers, symbols, and special characters are not allowed. Department is mandatory for students and staff, but must remain empty for other users. The only valid departments are: IT, Business, Arts, Science, Engineering, Education, Medicine and Library. Books The library only recognizes two valid book types: physical and online. The following fields are required for all books: Title, Author, Year Each book may have at most 5 keywords. Each word in keywords must consist of letters and numbers only. The only allowed special character is the hyphen (-). Loans Every loan record must be associated with an existing user and an existing book. The due date is a required field and cannot be left empty. Defensive Programming The following exceptions are provided to you in custom_errors.py: CustomTypeError: A value is the wrong type (e.g. a string instead of a float or int). CustomValueError: A value is of the correct type but invalid (e.g. negative copies). CustomDateError: Raised when a date is in the wrong format or when the date logic is invalid (e.g., borrow date occurs in the future). MissingRequiredFieldError: Raised when a required field is missing. UserNotFoundError or BookNotFoundError: Raised when a loan references a user or book that does not exist. CustomOperationError : Raised when an invalid borrow or return action is attempted. CustomLimitError: Raised when a user exceeds their quota or borrowing limit. CustomKeyError: Occurs when you try to access a key in the dictionary that doesn't exist.
最新发布
10-18
```python # custom_errors.py class CustomTypeError(Exception): """Raised when a value is of the wrong type.""" pass class CustomValueError(Exception): """Raised when a value is of correct type but invalid.""" pass class CustomDateError(Exception): """Raised when a date is in wrong format or logically invalid.""" pass class MissingRequiredFieldError(Exception): """Raised when a required field is missing.""" pass class UserNotFoundError(Exception): """Raised when a user does not exist.""" pass class BookNotFoundError(Exception): """Raised when a book does not exist.""" pass class CustomOperationError(Exception): """Raised when an invalid borrow/return operation is attempted.""" pass class CustomLimitError(Exception): """Raised when a borrowing limit is exceeded.""" pass class CustomKeyError(Exception): """Raised when trying to access a non-existent key.""" pass ``` --- ### ✅ **Updated `user.py` with Defensive Programming and Assertions** ```python # user.py from datetime import datetime from custom_errors import CustomTypeError, CustomValueError, MissingRequiredFieldError class User: VALID_DEPARTMENTS = {"IT", "Business", "Arts", "Science", "Engineering", "Education", "Medicine", "Library"} def __init__(self, user_id: str, name: str, user_type: str, department: str = "", password: str = ""): # Type checks if not isinstance(user_id, str): raise CustomTypeError(f"User ID must be a string. Got {type(user_id).__name__}.") if not isinstance(name, str): raise CustomTypeError(f"Name must be a string. Got {type(name).__name__}.") if not isinstance(user_type, str): raise CustomTypeError(f"User type must be a string. Got {type(user_type).__name__}.") if not isinstance(department, str): raise CustomTypeError(f"Department must be a string. Got {type(department).__name__}.") if not isinstance(password, str): raise CustomTypeError(f"Password must be a string. Got {type(password).__name__}.") # Required fields if not user_id.strip(): raise MissingRequiredFieldError("User ID cannot be empty.") if not name.strip(): raise MissingRequiredFieldError("Name cannot be empty.") if not user_type.strip(): raise MissingRequiredFieldError("User type cannot be empty.") if not password.strip(): raise MissingRequiredFieldError("Password cannot be empty.") # Validate name: each word must be alphabetic only for word in name.strip().split(): if not word.isalpha(): raise CustomValueError(f"Name contains invalid word '{word}'. Only alphabetic characters are allowed.") # Department logic if user_type.lower() in ["student", "staff"]: if not department.strip(): raise MissingRequiredFieldError(f"Department is required for {user_type}.") dept = department.strip() if dept not in self.VALID_DEPARTMENTS: raise CustomValueError(f"Invalid department: '{dept}'. Valid options: {', '.join(sorted(self.VALID_DEPARTMENTS))}") else: if department.strip(): raise CustomValueError(f"Only students and staff can have a department. Found '{department}' for {user_type}.") # Assign attributes self.user_id = user_id.strip() self.name = name.strip() self.user_type = user_type.strip().lower() self.department = department.strip() if department else "" self.password = password # Already checked non-empty above self.borrowed_books = [] # Assert internal consistency assert isinstance(self.user_id, str) and len(self.user_id) > 0, "User ID failed post-initialization validation." assert isinstance(self.name, str) and len(self.name) > 0, "Name failed post-initialization validation." assert self.user_type in ["student", "staff", "other"], f"Unexpected user_type after init: {self.user_type}" def can_borrow(self) -> bool: max_books = 5 if self.user_type == "student" else 10 return len(self.borrowed_books) < max_books def borrow_book(self, book): from book import Book # Avoid circular import if not isinstance(book, Book): raise CustomTypeError(f"Expected Book object, got {type(book).__name__}.") if not self.can_borrow(): raise CustomLimitError(f"{self.name} has reached the borrowing limit ({len(self.borrowed_books)} books).") self.borrowed_books.append(book) def return_book(self, book): if book not in self.borrowed_books: raise CustomOperationError(f"{self.name} does not have book '{book.title}'. Cannot return.") self.borrowed_books.remove(book) def __repr__(self): return f"User(ID={self.user_id}, Name='{self.name}', Type='{self.user_type}', Dept='{self.department}')" ``` --- ### ✅ **Updated `book.py` with Defensive Programming and Assertions** ```python # book.py from custom_errors import CustomTypeError, CustomValueError, MissingRequiredFieldError class Book: VALID_TYPES = ("physical", "online") def __init__(self, isbn: str, title: str, author: str, year: int, book_type: str, keywords: list = None): # Type checks if not isinstance(isbn, str): raise CustomTypeError(f"ISBN must be a string. Got {type(isbn).__name__}.") if not isinstance(title, str): raise CustomTypeError(f"Title must be a string. Got {type(title).__name__}.") if not isinstance(author, str): raise CustomTypeError(f"Author must be a string. Got {type(author).__name__}.") if not isinstance(year, int): raise CustomTypeError(f"Year must be an integer. Got {type(year).__name__}.") if not isinstance(book_type, str): raise CustomTypeError(f"Book type must be a string. Got {type(book_type).__name__}.") if keywords is not None and not isinstance(keywords, list): raise CustomTypeError(f"Keywords must be a list or None. Got {type(keywords).__name__}.") # Required fields if not isbn.strip(): raise MissingRequiredFieldError("ISBN cannot be empty.") if not title.strip(): raise MissingRequiredFieldError("Title cannot be empty.") if not author.strip(): raise MissingRequiredFieldError("Author cannot be empty.") if year < 1000 or year > datetime.now().year: raise CustomValueError(f"Year must be between 1000 and current year. Got {year}.") if book_type.lower() not in self.VALID_TYPES: raise CustomValueError(f"Invalid book type: '{book_type}'. Must be one of {self.VALID_TYPES}.") # Keywords validation keywords = keywords or [] if len(keywords) > 5: raise CustomLimitError(f"A book can have at most 5 keywords. Got {len(keywords)}.") for kw in keywords: if not isinstance(kw, str): raise CustomTypeError(f"Keyword must be a string. Got {type(kw).__name__}.") if not kw.strip(): raise CustomValueError("Keywords cannot be empty.") # Only letters, digits, and hyphens allowed if not all(c.isalnum() or c == '-' for c in kw): raise CustomValueError(f"Invalid keyword '{kw}'. Only letters, numbers, and hyphens are allowed.") # Assign values self.isbn = isbn.strip() self.title = title.strip() self.author = author.strip() self.year = year self.type = book_type.lower() self.keywords = [kw.strip() for kw in keywords] # Assert internal state assert isinstance(self.isbn, str) and len(self.isbn) > 0, "ISBN validation failed after assignment." assert isinstance(self.title, str) and len(self.title) > 0, "Title validation failed after assignment." assert isinstance(self.author, str) and len(self.author) > 0, "Author validation failed after assignment." assert self.year >= 1000 and self.year <= datetime.now().year, "Year out of range after assignment." assert self.type in self.VALID_TYPES, "Book type invalid after assignment." assert len(self.keywords) <= 5, "Too many keywords stored." def matches_keyword(self, keyword: str) -> bool: if not isinstance(keyword, str): raise CustomTypeError(f"Keyword to match must be a string. Got {type(keyword).__name__}.") return any(keyword.lower() in kw.lower() for kw in self.keywords) def __repr__(self): return f"Book(ISBN={self.isbn}, Title='{self.title}', Author='{self.author}', Year={self.year}, Type='{self.type}')" ``` --- ### ✅ **Updated `task4.py` – Robust Main Program with Exception Handling** ```python # task4.py import csv from datetime import datetime from custom_errors import ( MissingRequiredFieldError, CustomTypeError, CustomValueError, UserNotFoundError, BookNotFoundError, CustomDateError, CustomOperationError, CustomLimitError ) from user import User from book import Book class LibrarySystem: def __init__(self): self.users = {} # user_id -> User self.books = {} # isbn -> Book self.loans = {} # isbn -> (user_id, due_date) def load_users(self, filename): try: with open(filename, newline='', encoding='utf-8') as file: reader = csv.DictReader(file) for line_num, row in enumerate(reader, start=2): # Start at 2 because header is line 1 try: user_id = row.get('user_id', '').strip() name = row.get('name', '').strip() user_type = row.get('user_type', '').strip() department = row.get('department', '').strip() password = row.get('password', '').strip() if not all([user_id, name, user_type, password]): raise MissingRequiredFieldError(f"Missing required field(s) in row {line_num}: {row}") user = User(user_id, name, user_type, department, password) self.users[user_id] = user except (CustomTypeError, CustomValueError, MissingRequiredFieldError) as e: print(f"[Line {line_num}] Error loading user: {e}") except Exception as e: print(f"[Line {line_num}] Unexpected error: {e}") except FileNotFoundError: print(f"Error: File '{filename}' not found.") except Exception as e: print(f"Error reading users file: {e}") def load_books(self, filename): try: with open(filename, newline='', encoding='utf-8') as file: reader = csv.DictReader(file) for line_num, row in enumerate(reader, start=2): try: isbn = row.get('isbn', '').strip() title = row.get('title', '').strip() author = row.get('author', '').strip() year_str = row.get('year', '').strip() book_type = row.get('type', '').strip() keywords_str = row.get('keywords', '').strip() if not all([isbn, title, author, year_str, book_type]): raise MissingRequiredFieldError(f"Missing required field(s) in row {line_num}: {row}") try: year = int(year_str) except ValueError: raise CustomTypeError(f"Year must be an integer. Got '{year_str}'.") keywords = [kw.strip() for kw in keywords_str.split(",")] if keywords_str else [] book = Book(isbn, title, author, year, book_type, keywords) self.books[isbn] = book except (CustomTypeError, CustomValueError, MissingRequiredFieldError, CustomLimitError) as e: print(f"[Line {line_num}] Error loading book: {e}") except Exception as e: print(f"[Line {line_num}] Unexpected error: {e}") except FileNotFoundError: print(f"Error: File '{filename}' not found.") except Exception as e: print(f"Error reading books file: {e}") def load_loans(self, filename): try: with open(filename, newline='', encoding='utf-8') as file: reader = csv.DictReader(file) for line_num, row in enumerate(reader, start=2): try: user_id = row.get('user_id', '').strip() isbn = row.get('isbn', '').strip() due_date_str = row.get('due_date', '').strip() if not all([user_id, isbn, due_date_str]): raise MissingRequiredFieldError(f"Missing required loan field(s) in row {line_num}.") # Validate date try: due_date = datetime.strptime(due_date_str, "%Y-%m-%d") if due_date.date() < datetime.now().date(): print(f"[Warning] Due date expired for loan: {isbn} -> {user_id}") except ValueError: raise CustomDateError(f"Invalid due date format: '{due_date_str}'. Use YYYY-MM-DD.") # Check existence if user_id not in self.users: raise UserNotFoundError(f"User ID '{user_id}' not found.") if isbn not in self.books: raise BookNotFoundError(f"ISBN '{isbn}' not found.") # Perform loan action user = self.users[user_id] book = self.books[isbn] if not user.can_borrow(): raise CustomLimitError(f"User '{user.name}' cannot borrow more books.") user.borrow_book(book) self.loans[isbn] = (user_id, due_date) except (UserNotFoundError, BookNotFoundError, CustomDateError, CustomLimitError, MissingRequiredFieldError, CustomOperationError) as e: print(f"[Line {line_num}] Error processing loan: {e}") except Exception as e: print(f"[Line {line_num}] Unexpected error: {e}") except FileNotFoundError: print(f"Error: File '{filename}' not found.") except Exception as e: print(f"Error reading loans file: {e}") def return_book(self, user_id: str, isbn: str): try: if user_id not in self.users: raise UserNotFoundError(f"User ID '{user_id}' not found.") if isbn not in self.books: raise BookNotFoundError(f"ISBN '{isbn}' not found.") if isbn not in self.loans: raise CustomOperationError(f"Book '{isbn}' is not currently on loan.") user = self.users[user_id] book = self.books[isbn] user.return_book(book) del self.loans[isbn] print(f"Book '{book.title}' returned successfully by {user.name}.") except (UserNotFoundError, BookNotFoundError, CustomOperationError) as e: print(f"Return failed: {e}") except Exception as e: print(f"Unexpected error during return: {e}") def search_books(self, keyword: str): try: if not keyword or not isinstance(keyword, str): raise CustomTypeError("Search keyword must be a non-empty string.") results = [book for book in self.books.values() if book.matches_keyword(keyword)] return results except Exception as e: print(f"Search error: {e}") return [] # Example usage if __name__ == "__main__": lib = LibrarySystem() lib.load_users("users.csv") lib.load_books("books.csv") lib.load_loans("loans.csv") print("\n--- Search Results for 'Python' ---") for book in lib.search_books("Python"): print(book) print("\n--- Returning Book ---") lib.return_book("U003", "9780134685991") # Example return ``` --- ### 🔍 Explanation - **Custom Errors**: Defined in `custom_errors.py`, used consistently across modules. - **Defensive Input Validation**: - All inputs are checked for type (`isinstance`) and validity (e.g., department, name). - Empty passwords, invalid names, or malformed departments trigger appropriate exceptions. - **Assertions**: Used internally to ensure class invariants (e.g., user type is valid post-init). - **CSV Loading Resilience**: Each row is processed individually; errors are caught per-line to avoid full system crash. - **Loan System Integrity**: Validates due dates, existing users/books, and borrowing limits before creating loans. - **Graceful Degradation**: The system logs errors and continues instead of crashing. --- ### ✅ Summary of Key Improvements | Feature | Implemented | |-------|-------------| | Custom Exceptions | ✅ Used throughout | | Type Safety | ✅ `isinstance` checks | | Value Validation | ✅ Names, departments, years, keywords | | Required Fields | ✅ Enforced via `MissingRequiredFieldError` | | Max Keywords | ✅ Limited to 5 | | Loan Integrity | ✅ Checks user/book existence, due date | | Error Messages | ✅ Descriptive and helpful | | System Stability | ✅ No crashes on bad data | ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值