使用JDBC+javafx写一个简单功能齐全的图书管理系统
Posted 冷兮雪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用JDBC+javafx写一个简单功能齐全的图书管理系统相关的知识,希望对你有一定的参考价值。
目录
在文章结尾有图书管理系统的效果展示
1、JDBC的使用
后面会用到大量jdbc语句来实现数据库的增删改查,可以先简单看看下面这篇文章。
2、对应包和Java文件的层级关系及对应的含义
上面DBUtils为jdbc的封装,详情可以看jdbc的简单使用与封装,还有dp.properties文件也在这篇文章中收到,我后面就不再过多概述。
3、数据库
admin:
book:
category:
4、相关代码
1)、bookmanager包
Ⅰ、main函数
BookManagerApplication
package com.hk.sky.bookmanager;
import com.hk.sky.bookmanager.bean.Admin;
import com.hk.sky.bookmanager.controller.LoginController;
import com.hk.sky.bookmanager.dao.AdminDao;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class BookManagerApplication extends Application
AdminDao adminDao=new AdminDao();
//获取上一次用户登录的信息
Admin admin=adminDao.getLast();
@Override
public void start(Stage stage) throws IOException
//登录页面的资源文件为login/login.fxml
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("login/login.fxml"));
//设置登录窗口的长和宽
Scene scene = new Scene(fxmlLoader.load(), 290, 240);
stage.setTitle("管理员登录");
stage.setScene(scene);
LoginController loginController=fxmlLoader.getController();
//如果admin不是为null,则说明上一次有用户登录,则直接在登录页面显示账号和密码
if (admin!=null)
loginController.set(admin.getAccount(),admin.getPassword());
stage.show();
//启动
public static void main(String[] args)
launch();
Ⅱ、utils包
DBUtils
package com.hk.sky.bookmanager.utils;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* 封装数据连接,关闭的工具类
*/
public class DBUtils
private static String url;
private static String username;
private static String password;
// 读取配置文件
static
Properties prop = new Properties();
try
// DBUtils.class.getClassLoader().getResourceAsStream()方法可以从类路径中读取资源文件
prop.load(DBUtils.class.getClassLoader().getResourceAsStream("db.properties"));
// 通过key获取value
url = prop.getProperty("jdbc.url");
username = prop.getProperty("jdbc.username");
password = prop.getProperty("jdbc.password");
catch (IOException e)
throw new RuntimeException(e);
// 将创建连接的方法封装成静态方法,方便调用
// 如何将url、username、password放到配置文件当中,然后读取出来
public static Connection getConnection()
// 创建连接的时候,有会异常:SQLException,不建议抛出,建立捕获
Connection conn = null;
try
conn = DriverManager.getConnection(
url,
username,
password
);
catch (SQLException e)
e.printStackTrace();
return conn;
// 将关闭连接的方法封装成静态方法,方便调用
public static void close(ResultSet rs, PreparedStatement pStmt, Connection conn)
if (rs != null)
try
rs.close();
catch (SQLException e)
e.printStackTrace();
if (pStmt != null)
try
pStmt.close();
catch (SQLException e)
e.printStackTrace();
if (conn != null)
try
conn.close();
catch (SQLException e)
e.printStackTrace();
Ⅲ、bean包
Admin
package com.hk.sky.bookmanager.bean;
public class Admin
//账号
private String account;
//密码
private String password;
//上一次登录
private int last;
public String getAccount()
return account;
public String getPassword()
return password;
public void setAccount(String account)
this.account = account;
public void setPassword(String password)
this.password = password;
public int getLast()
return last;
public void setLast(int last)
this.last = last;
@Override
public String toString()
return "Admin" +
"account='" + account + '\\'' +
", password='" + password + '\\'' +
", last=" + last +
'';
book
package com.hk.sky.bookmanager.bean;
public class Book
private int id;
private String bookName;
private String author;
private int price;
private int stock;
private String publisher;
private String detail;
private int typeId;
public void setDetail(String detail)
this.detail = detail;
public String getDetail()
return detail;
public int getId()
return id;
public String getBookName()
return bookName;
public String getAuthor()
return author;
public int getPrice()
return price;
public int getStock()
return stock;
public String getPublisher()
return publisher;
public void setId(int id)
this.id = id;
public void setBookName(String bookName)
this.bookName = bookName;
public void setAuthor(String author)
this.author = author;
public void setPrice(int price)
this.price = price;
public void setStock(int stock)
this.stock = stock;
public void setPublisher(String publisher)
this.publisher = publisher;
public void setTypeId(int typeId)
this.typeId = typeId;
public int getTypeId()
return typeId;
public Book()
public Book(String name, String author, String publisher, int price, String detail, int stock, int typeId)
this.bookName = name;
this.author = author;
this.publisher = publisher;
this.price = price;
this.detail = detail;
this.stock = stock;
this.typeId = typeId;
Category
package com.hk.sky.bookmanager.bean;
public class Category
private int id;
private String typeName;
public int getId()
return id;
public void setId(int id)
this.id = id;
public String getTypeName()
return typeName;
public void setTypeName(String typeName)
this.typeName = typeName;
@Override
public String toString()
return typeName;
Ⅳ、controller包
AddController
package com.hk.sky.bookmanager.controller;
import com.hk.sky.bookmanager.bean.Book;
import com.hk.sky.bookmanager.bean.Category;
import com.hk.sky.bookmanager.dao.BookDao;
import com.hk.sky.bookmanager.dao.CategoryDao;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.stage.Stage;
import java.net.URL;
import java.sql.SQLException;
import java.util.*;
public class AddController implements Initializable
@FXML
public Button add;
@FXML
public Button edit;
@FXML
private ComboBox<Category> categoryComboBox;
private ObservableList<Category> observableList = FXCollections.observableArrayList();
private CategoryDao categoryDao = new CategoryDao();
private BookDao bookDao = new BookDao();
@FXML
private TextField bookName;
@FXML
private TextField bookAuthor;
@FXML
private TextField bookPublisher;
@FXML
private TextField bookPrice;
@FXML
private TextField detail;
@FXML
private TextField bookStock;
public Book book;
public void setBook(Book book)
this.book = book;
//B用来判断是添加书籍还是编辑书籍
public boolean B;
public void setB(boolean i)
this.B=i;
@Override
public void initialize(URL url, ResourceBundle resourceBundle)
Platform.runLater(() ->
// 页面第一次加载的时候,就显示下拉列表
observableList.clear();
List<Category> categories = categoryDao.getAllCategories();
observableList.addAll(categories);
categoryComboBox.setItems(observableList);
//添加书籍 修改书籍
if (B)
add.setVisible(true);
add.setText("添加");
edit.setVisible(false);
edit.setText("");
categoryComboBox.setValue(categories.get(0));
else
bookName.setText(book.getBookName());
bookAuthor.setText(book.getAuthor());
bookPublisher.setText(book.getPublisher());
bookPrice.setText(String.valueOf(book.getPrice()));
detail.setText(book.getDetail());
bookStock.setText(String.valueOf(book.getStock()));
categoryComboBox.setValue(categories.get(book.getTypeId()));
edit.setVisible(true);
edit.setText("修改");
add.setDisable(false);
add.setText("");
);
//给添加按钮设置点击事件
public void addBook() throws SQLException
// 用户点击添加按钮时,获取所有的数据,并插入到数据库当中
String name = bookName.getText();
String author = bookAuthor.getText();
String publisher = bookPublisher.getText();
String detail1 = detail.getText();
// 将格式进行转换
// 表单验证,判断用户是否输入内容
if (name.equals("") || author.equals("") || publisher.equals("") || detail1.equals("") ||
bookPrice.getText().equals("") || bookStock.getText().equals(""))
Alert error = new Alert(Alert.AlertType.ERROR, "输入错误");
Button err = new Button();
error.setTitle("您的书籍信息输入错误!");
error.setHeaderText("请正确填写所要添加书籍的信息!");
err.setOnAction((ActionEvent e) ->
error.showAndWait();
);
Optional<ButtonType> result = error.showAndWait();
else if (!bookPrice.getText().matches("[1-9]+[0-9]*")||!bookStock.getText().matches("[1-9]+[0-9]*"))
Alert error = new Alert(Alert.AlertType.ERROR, "输入错误");
Button err = new Button();
error.setTitle("您的书籍信息输入错误!");
error.setHeaderText("库存和价格必须为整数!");
err.setOnAction((ActionEvent e) ->
error.showAndWait();
);
Optional<ButtonType> result = error.showAndWait();
else
// 思路:将上面获取到的数据封装成book,然后插入到数据库当中
// 但是仔细思考,我们会碰到一个难点:如何获取ComboBox的选项?也就是说如何知道用户选的是哪一个类型的书籍?
Category selectedCategory = categoryComboBox.getValue();
int price = Integer.parseInt(bookPrice.getText());
int stock = Integer.parseInt(bookStock.getText());
// 将所有的数据封装成书籍
Book book = new Book(name, author, publisher, price, detail1, stock, selectedCategory.getId());
// 调用BookDao中的方法将数据插入到数据库当中
Alert information = new Alert(Alert.AlertType.INFORMATION, "恭喜您书籍添加成功");
Button err = new Button();
information.setTitle("添加成功!");
information.setHeaderText("林氏图书馆又收录了一本新书,感谢!");
err.setOnAction((ActionEvent e) ->
information.showAndWait();
);
Optional<ButtonType> result = information.showAndWait();
// 插入成功之后,首先当前属性应该关闭
if (bookDao.insertBook(book))
((Stage) bookName.getScene().getWindow()).close();
//给编辑按钮设置点击事件
public void editBook(ActionEvent actionEvent) throws SQLException
// 用户点击添加按钮时 ,获取所有的数据,并插入到数据库当中
String name = bookName.getText();
String author = bookAuthor.getText();
String publisher = bookPublisher.getText();
String detail1 = detail.getText();
// 将格式进行转换
// 表单验证,判断用户是否输入内容
if (name.equals("")||author.equals("")||publisher.equals("")||detail1.equals("")||
bookPrice.getText().equals("")||bookStock.getText().equals(""))
Alert error=new Alert(Alert.AlertType.ERROR,"输入错误");
Button err=new Button();
error.setTitle("您的书籍信息输入错误!");
error.setHeaderText("请正确填写所要修改书籍的信息!");
err.setOnAction((ActionEvent e)->
error.showAndWait();
);
Optional<ButtonType> result = error.showAndWait();
else if (!bookPrice.getText().matches("[1-9]+[0-9]*")||!bookStock.getText().matches("[1-9]+[0-9]*"))
Alert error = new Alert(Alert.AlertType.ERROR, "输入错误");
Button err = new Button();
error.setTitle("您的书籍信息输入错误!");
error.setHeaderText("库存和价格必须为整数!");
err.setOnAction((ActionEvent e) ->
error.showAndWait();
);
Optional<ButtonType> result = error.showAndWait();
else
int price = Integer.parseInt(bookPrice.getText());
int stock = Integer.parseInt(bookStock.getText());
// 思路:将上面获取到的数据封装成book,然后插入到数据库当中
// 但是仔细思考,我们会碰到一个难点:如何获取ComboBox的选项?也就是说如何知道用户选的是哪一个类型的书籍?
Category selectedCategory = categoryComboBox.getValue();
// 将所有的数据封装成书籍
Book book1 = new Book(name, author, publisher, price, detail1, stock, selectedCategory.getId());
book1.setId(book.getId());
// 调用BookDao中的方法将数据插入到数据库当中
Alert information=new Alert(Alert.AlertType.INFORMATION,"恭喜您书籍信息修改成功");
Button err=new Button();
information.setTitle("修改成功!");
information.setHeaderText("谢谢您为林氏图书馆添砖加瓦,感谢!");
err.setOnAction((ActionEvent e)->
information.showAndWait();
);
Optional<ButtonType> result = information.showAndWait();
boolean success = bookDao.updateBook(book1);
// 插入成功之后,首先当前属性应该关闭
if (success)
((Stage)bookName.getScene().getWindow()).close();
EnrollController
package com.hk.sky.bookmanager.controller;
import com.hk.sky.bookmanager.bean.Admin;
import com.hk.sky.bookmanager.dao.AdminDao;
import com.hk.sky.bookmanager.utils.DBUtils;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
import java.util.Optional;
public class EnrollController
@FXML
public TextField username1;
@FXML
public PasswordField password1;
@FXML
public Text errorMsg;//提示错误信息
@FXML
private Button alert;
@FXML
private Button enroll;
@FXML
public Text title;
AdminDao adminDao=new AdminDao();
//给注册按钮设置点击事件
public void enrollBtn()
String account=username1.getText();
String pw=password1.getText();
if (Objects.equals(account, ""))
errorMsg.setText("请填写用户名");
else if (Objects.equals(pw, ""))
errorMsg.setText("请填写密码");
else
boolean b=adminDao.enroll(account,pw);
if(!b)
errorMsg.setText("用户名已使用,请更改用户名!");
else
Alert information=new Alert(Alert.AlertType.INFORMATION,"恭喜"+account+"用户注册成功");
Button err=new Button();
information.setTitle("注册成功!");
information.setHeaderText("林氏图书馆欢迎您的到来!");
err.setOnAction((ActionEvent e)->
information.showAndWait();
);
Optional<ButtonType> result = information.showAndWait();
// 关闭当前Stage,问题:如何获取当前的stage?
Stage currentWindow = (Stage)username1.getScene().getWindow();
currentWindow.close();
//区分是注册还是修改密码,修改密码的话账号无法更改,输入框为不可选择,注册按钮消失,修改按钮显示
public void qf(boolean b)
if (b)
enroll.setVisible(false);
alert.setVisible(true);
username1.setDisable(true);
//s用来保存原密码,修改密码无法更改为原密码
public String s;
public void setS(String ss)
s=ss;
//给修改密码设置点击事件
public void alertBtn() throws SQLException
if (password1.getText().equals(""))
errorMsg.setText("密码不能为空");
else if(password1.getText().equals(s))
errorMsg.setText("不能与最近使用密码相同");
else
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "你是否真的要修改该密码?");
alert.setTitle("修改密码");
alert.setHeaderText("确认修改?");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent())
adminDao.alertPassword(username1.getText(),password1.getText());
Alert information = new Alert(Alert.AlertType.INFORMATION, "恭喜您修改密码成功!");
Button err = new Button();
information.setTitle("修改密码");
information.setHeaderText("修改密码成功!");
err.setOnAction((ActionEvent e) ->
information.showAndWait();
);
Optional<ButtonType> rs = information.showAndWait();
HouTaiController
package com.hk.sky.bookmanager.controller;
import com.hk.sky.bookmanager.BookManagerApplication;
import com.hk.sky.bookmanager.bean.Admin;
import com.hk.sky.bookmanager.bean.Book;
import com.hk.sky.bookmanager.dao.AdminDao;
import com.hk.sky.bookmanager.dao.BookDao;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
public class HouTaiController implements Initializable
/*搜索框*/
public TextField search;
/*下拉框*/
public ComboBox categorySelect;
// 不能直接从这里查询用户信息,因为在当前这个类当中,我不知道是谁登录了
// 通过上一个类传递到当前类当中,我们需要在当前类中定义要传递的数据,然后通过set方法设置该属性的值
private Admin admin;
public String bookName;
public String bookAuthor;
public String Publish;
//表示查询到的行
public int sumPage;
//n表示页数
public int n;
public void setAdmin(Admin admin)
this.admin = admin;
public void setSumPage(int i)
this.sumPage=i;
double j=i/9.0;
n=(int) Math.ceil(j);
public void setBookName(String s)
bookName=s;
@FXML
private Text welcomeMsg;
@FXML
private Text searchCount;
@FXML
private Text searchPage;
@FXML//返回按钮
private Button fh;
//给表添加数据
@FXML
private TableView<Book> bookTable;
// 准备表格中的数据
private ObservableList<Book> data = FXCollections.observableArrayList();
// 准备dao类,它负责到数据库中查询
private BookDao bookDao = new BookDao();
private AdminDao adminDao=new AdminDao();
//添加按钮操作
// 编辑按钮
private TableColumn<Book, Integer> editColumn = new TableColumn<>("编辑");
// 删除按钮
private TableColumn<Book, Integer> delColumn = new TableColumn<>("删除");
// 详情按钮
private TableColumn<Book, Integer> detailColumn = new TableColumn<>("详情");
// 上一个页面中将admin传递过来,我们需要在当前窗口中来获取传递过来的数据,然后将值设置给页面中的元素显示
// 将当前类实现Initializable接口,然后在initialize(初始化)方法中获取设置的值,然后设置给页面中的元素
@Override
public void initialize(URL url, ResourceBundle resourceBundle)
// 通过UI线程获取值,并设置值
Platform.runLater(() ->
welcomeMsg.setText("欢迎" + admin.getAccount() + "管理员登录!");
searchCount.setText("共"+sumPage+"条记录");
categorySelect.getItems().addAll(
"书名",
"出版社",
"作者"
);
categorySelect.setValue("书名");
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books = bookDao.set(search.getText(),0);
data.addAll(books);
bookTable.setItems(data);
//1、添加编辑按钮
editColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().getId()));
editColumn.setCellFactory(param -> new TableCell<>()
final Button editBtn = new Button("编辑");
@Override
protected void updateItem(Integer item, boolean empty)
super.updateItem(item, empty);
if (item == null)
setGraphic(null);
return;
setGraphic(editBtn);
// 添加点击事件
editBtn.setOnMouseClicked(event ->
try
Stage newStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("book/add.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 280, 400);
newStage.setTitle("编辑书籍");
newStage.setScene(scene);
AddController addController=fxmlLoader.getController();
addController.setB(false);
addController.setBook(bookDao.getbookById(item));
newStage.show();
// 我们需要监听添加页面关闭后,刷新tableview
// CloseRequest()只有在用户点击右上角的叉时才会触发
newStage.setOnHiding(evt->
refreshTableView(i);
);
catch (IOException e)
throw new RuntimeException(e);
);
);
//将列添加至表中
bookTable.getColumns().add(editColumn);
//2、添加删除按钮
delColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().getId()));
delColumn.setCellFactory(param -> new TableCell<>()
// 创建删除按钮
final Button editBtn = new Button("删除");
@Override
protected void updateItem(Integer item, boolean empty)
super.updateItem(item, empty);
if (item == null)
setGraphic(null);
return;
setGraphic(editBtn);
// 添加点击事件
editBtn.setOnMouseClicked(event ->
// 添加一个对话框,判断用户是否需要真的删除
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "你是否需要真的删除这本吗?");
alert.setTitle("警告");
alert.setHeaderText("确认删除?");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK)
// 实现删除功能
bookDao.deleteBookById(item);
// 刷新页面
refreshTableView(i);
);
);
//将列添加至表中
bookTable.getColumns().add(delColumn);
// 3、添加详情按钮
detailColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().getId()));
detailColumn.setCellFactory(param -> new TableCell<>()
// 创建详情按钮
final Button editBtn = new Button("详情");
@Override
protected void updateItem (Integer item,boolean empty)
super.updateItem(item, empty);
if (item == null)
setGraphic(null);
return;
setGraphic(editBtn);
// 添加点击事件
editBtn.setOnMouseClicked(event ->
try
Stage newStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("book/show.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 280, 270);
newStage.setTitle("书籍详情");
newStage.setScene(scene);
// 将当前书籍id传递到下一个页面当中
ShowController showController = fxmlLoader.getController();
showController.setBook(bookDao.getbookById(item));
// 显示要打开的Stage,stage.show()
newStage.show();
catch (IOException e)
throw new RuntimeException(e);
);
//将列添加至表中
);
bookTable.getColumns().add(detailColumn);
);
public void refreshTableView(int i)
// 在往集合中添加数据之前,先清空集合
data.clear();
List<Book> books = null;
// 查询数据库,设置ObservableList,并添加至tableView当中
if(categorySelect.getValue().equals("作者")&&bookAuthor!=null)
setSumPage(bookDao.likeSumCountByAuthor(search.getText()));
if (i==n)
i--;
books= bookDao.setByAuthor(bookAuthor, i);
searchCount.setText("共"+bookDao.likeSumCountByAuthor(search.getText())+"条记录");
else if (categorySelect.getValue().equals("出版社")&&Publish!=null)
setSumPage(bookDao.likeSumCountByPublish(search.getText()));
if (i==n)
i--;
books= bookDao.setByPublish(Publish, i);
searchCount.setText("共"+bookDao.likeSumCountByPublish(search.getText())+"条记录");
else
setSumPage(bookDao.likeSumCount(search.getText()));
if (i==n)
i--;
books= bookDao.set(bookName, i);
searchCount.setText("共"+bookDao.likeSumCount(search.getText())+"条记录");
data.addAll(books);
bookTable.setItems(data);
int z=i+1;
searchPage.setText("第"+z+"页");
int i=0;
//首页按钮
public void start()
if (i!=0)
data.clear();
List<Book> books=null;
// 查询数据库,设置ObservableList,并添加至tableView当中
if (categorySelect.getValue().equals("出版社")&&Publish!=null)
books=bookDao.setByPublish(Publish,0);
else if (categorySelect.getValue().equals("作者")&&bookAuthor!=null)
books=bookDao.setByAuthor(bookAuthor,0);
else
books= bookDao.set(bookName,0);
data.addAll(books);
bookTable.setItems(data);
i=0;
searchPage.setText("第"+1+"页");
//尾页按钮
public void end()
if (i!=n-1)
data.clear();
List<Book> books=null;
// 查询数据库,设置ObservableList,并添加至tableView当中
// 查询数据库,设置ObservableList,并添加至tableView当中
if (categorySelect.getValue().equals("出版社")&&Publish!=null)
books=bookDao.setByPublish(Publish,n-1);
else if (categorySelect.getValue().equals("作者")&&bookAuthor!=null)
books=bookDao.setByAuthor(bookAuthor,n-1);
else
books= bookDao.set(bookName,n-1);
data.addAll(books);
bookTable.setItems(data);
i=n-1;
searchPage.setText("第"+n+"页");
//上一页按钮
public void up()
if (i>0&&i<=n-1)
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books=null;
// 查询数据库,设置ObservableList,并添加至tableView当中
if (categorySelect.getValue().equals("出版社")&&Publish!=null)
books=bookDao.setByPublish(Publish,--i);
else if (categorySelect.getValue().equals("作者")&&bookAuthor!=null)
books=bookDao.setByAuthor(bookAuthor,--i);
else
books= bookDao.set(bookName,--i);
data.addAll(books);
bookTable.setItems(data);
int z=i+1;
searchPage.setText("第"+z+"页");
//下一页按钮
public void next()
if (i<n-1&&i>=0)
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books=null;
// 查询数据库,设置ObservableList,并添加至tableView当中
if (categorySelect.getValue().equals("出版社")&&Publish!=null)
books=bookDao.setByPublish(Publish,++i);
else if (categorySelect.getValue().equals("作者")&&bookAuthor!=null)
books=bookDao.setByAuthor(bookAuthor,++i);
else
books= bookDao.set(bookName,++i);
data.addAll(books);
bookTable.setItems(data);
int z=i+1;
searchPage.setText("第"+z+"页");
//搜索按钮
public void searchBtn() throws IOException
if (categorySelect.getValue().equals("书名"))
if (!Objects.equals(search.getText(), ""))
if (bookDao.likeSumCount(search.getText())==0)
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "抱歉,为查找到有关该图书!");
alert.setTitle("查找错误");
alert.setHeaderText("请重新填写所要查找书籍的名称!");
Optional<ButtonType> result = alert.showAndWait();
else
fh.setVisible(true);
bookName=search.getText();
// 在往集合中添加数据之前,先清空集合
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books = bookDao.set(bookName,0);
data.addAll(books);
bookTable.setItems(data);
setSumPage(bookDao.likeSumCount(bookName));
searchCount.setText("共"+bookDao.likeSumCount(search.getText())+"条记录");
i=0;
searchPage.setText("第"+1+"页");
else
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "错误,搜索框中未输入查询数据!");
alert.setTitle("输入错误");
alert.setHeaderText("请填写所要查找书籍的名称,书籍名不能为空!");
Optional<ButtonType> result = alert.showAndWait();
else if (categorySelect.getValue().equals("作者"))
if (!Objects.equals(search.getText(), ""))
if (bookDao.likeSumCountByAuthor(search.getText())==0)
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "抱歉,未查找到有关该图书!");
alert.setTitle("查找错误");
alert.setHeaderText("请重新填写所要查找书籍的作者!");
Optional<ButtonType> result = alert.showAndWait();
else
fh.setVisible(true);
bookAuthor=search.getText();
// 在往集合中添加数据之前,先清空集合
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books = bookDao.setByAuthor(bookAuthor,0);
data.addAll(books);
bookTable.setItems(data);
setSumPage(bookDao.likeSumCountByAuthor(bookAuthor));
searchCount.setText("共"+bookDao.likeSumCountByAuthor(bookAuthor)+"条记录");
i=0;
searchPage.setText("第"+1+"页");
else
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "错误,搜索框中未输入查询数据!");
alert.setTitle("输入错误");
alert.setHeaderText("请填写所要查找书籍的作者,作者名不能为空!");
Optional<ButtonType> result = alert.showAndWait();
else
if (!Objects.equals(search.getText(), ""))
if (bookDao.likeSumCountByPublish(search.getText())==0)
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "抱歉,未查找到有关该图书!");
alert.setTitle("查找错误");
alert.setHeaderText("请重新填写所要查找书籍的出版社!");
Optional<ButtonType> result = alert.showAndWait();
else
fh.setVisible(true);
Publish=search.getText();
// 在往集合中添加数据之前,先清空集合
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books = bookDao.setByPublish(Publish,0);
data.addAll(books);
bookTable.setItems(data);
setSumPage(bookDao.likeSumCountByPublish(Publish));
searchCount.setText("共"+bookDao.likeSumCountByPublish(Publish)+"条记录");
i=0;
searchPage.setText("第"+1+"页");
else
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "错误,搜索框中未输入查询数据!");
alert.setTitle("输入错误");
alert.setHeaderText("请填写所要查找书籍的出版社,出版社名不能为空!");
Optional<ButtonType> result = alert.showAndWait();
//删除按钮
public void deleteBtn()
Book book1=null;
String bookName=search.getText();
book1=bookDao.getBookByBookName(bookName);
if (book1!=null)
// 添加一个对话框,判断用户是否需要真的删除
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "你是否需要真的删除这本书吗?");
alert.setTitle("警告");
alert.setHeaderText("确认删除?");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK)
// 实现删除功能
bookDao.deleteBookByBookName(bookName);
// 刷新页面
refreshTableView(i);
else
// 添加一个对话框,告诉用户没有这本书
Alert error=new Alert(Alert.AlertType.ERROR,"删除错误,该图书馆未收录此书!");
Button err=new Button();
error.setTitle("删除错误!");
error.setHeaderText("请重新填写要删除的书籍!");
err.setOnAction((ActionEvent e)->
error.showAndWait();
);
Optional<ButtonType> result = error.showAndWait();
//添加按钮
public void addBtn()
try
Stage newStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("book/add.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 280, 400);
newStage.setTitle("添加书籍");
newStage.setScene(scene);
AddController addController=fxmlLoader.getController();
addController.setB(true);
newStage.show();
// 我们需要监听添加页面关闭后,刷新tableview
// CloseRequest()只有在用户点击右上角的叉时才会触发
newStage.setOnHiding(event ->
refreshTableView(i);
);
catch (IOException e)
throw new RuntimeException(e);
//返回按钮
public void back(ActionEvent actionEvent)
categorySelect.setValue("书名");
search.setText("");
bookName=search.getText();
// 在往集合中添加数据之前,先清空集合
data.clear();
// 查询数据库,设置ObservableList,并添加至tableView当中
List<Book> books = bookDao.set(bookName,0);
data.addAll(books);
bookTable.setItems(data);
setSumPage(bookDao.likeSumCount(bookName));
searchCount.setText("共"+bookDao.likeSumCount(search.getText())+"条记录");
i=0;
searchPage.setText("第"+1+"页");
fh.setVisible(false);
//注销按钮
public void zx(ActionEvent actionEvent) throws IOException
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "你是否真的要注销该账号?");
alert.setTitle("警告");
alert.setHeaderText("确认注销?");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK)
adminDao.delete(admin.getAccount());
//提醒用户该账号已注销,请重新登录
Alert information = new Alert(Alert.AlertType.INFORMATION, "该账号已注销!");
Button err = new Button();
information.setTitle("登录失败!");
information.setHeaderText("该账号已注销,请重新登录");
err.setOnAction((ActionEvent e) ->
information.showAndWait();
);
Optional<ButtonType> result1 = information.showAndWait();
//关闭 后台 页面
Stage currentWindow = (Stage)search.getScene().getWindow();
currentWindow.close();
//重新显示登录页面
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("login/login.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 290, 240);
stage.setTitle("用户登录");
stage.setScene(scene);
stage.show();
//修改密码
public void xgmm(ActionEvent actionEvent) throws IOException
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("login/enroll.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 290, 251);
stage.setTitle("修改密码");
stage.setScene(scene);
EnrollController enrollController=fxmlLoader.getController();
enrollController.username1.setText(admin.getAccount());
enrollController.password1.setText(admin.getPassword());
String s=admin.getPassword();
enrollController.title.setText("修改密码");
enrollController.setS(admin.getPassword());
enrollController.qf(true);
stage.show();
//添加监听事件,检查密码是否更换
stage.setOnHiding(evt ->
try
if (adminDao.checkPassword(admin.getAccount(),s))
Alert information = new Alert(Alert.AlertType.INFORMATION, "该账号已注销!");
Button err = new Button();
information.setTitle("登录失败!");
information.setHeaderText("该账号已注销,请重新登录");
err.setOnAction((ActionEvent e) ->
information.showAndWait();
);
Optional<ButtonType> result1 = information.showAndWait();
//关闭 后台 页面
Stage currentWindow = (Stage)search.getScene().getWindow();
currentWindow.close();
//重新显示登录页面
Stage stage1 = new Stage();
FXMLLoader fxmlLoader1 = new FXMLLoader(BookManagerApplication.class.getResource("login/login.fxml"));
Scene scene1 = new Scene(fxmlLoader1.load(), 290, 240);
stage1.setTitle("用户登录");
stage1.setScene(scene1);
LoginController loginController=fxmlLoader1.getController();
loginController.username.setText(admin.getAccount());
stage1.show();
adminDao.reset();
catch (IOException e)
throw new RuntimeException(e);
);
LoginController
package com.hk.sky.bookmanager.controller;
import com.hk.sky.bookmanager.BookManagerApplication;
import com.hk.sky.bookmanager.bean.Admin;
import com.hk.sky.bookmanager.dao.AdminDao;
import com.hk.sky.bookmanager.dao.BookDao;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.io.IOException;
public class LoginController
@FXML
public TextField username;
@FXML
private PasswordField password;
@FXML
private Text errorMsg;
//设置上一次登录账号的账号密码
public void set(String un,String pw)
if (un!=null&&pw!=null)
username.setText(un);
password.setText(pw);
@FXML
//登录按钮 检查是否有该账号以及账号密码的正确性
public void checkLogin() throws IOException
// 获取输入框中的内容
String uname = username.getText();
String pwd = password.getText();
// 判断用户名与密码
AdminDao adminDao = new AdminDao();
Admin admin = adminDao.checkLogin(uname, pwd);
if (admin == null)
errorMsg.setText("用户名或者密码错误");
else
// 跳转到后面页面
// 创建要打开的Stage,问题:stage如何创建?
Stage newStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("book/houtai.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 700, 430);
newStage.setTitle("后台页面");
newStage.setScene(scene);
// 关闭当前Stage,问题:如何获取当前的stage?
Stage currentWindow = (Stage) username.getScene().getWindow();
currentWindow.close();
//消除上次用户登录的信息
adminDao.reset();
//保存这次用户登录的信息
adminDao.setLast(username.getText());
// 将当前用户的信息传递到下一个页面当中
HouTaiController houTaiController = fxmlLoader.getController();
BookDao bookdao=new BookDao();
houTaiController.setSumPage(bookdao.sumPage());
houTaiController.setAdmin(admin);
houTaiController.setBookName("");
// 显示要打开的Stage,stage.show()
newStage.show();
//注册按钮
public void enrollLogin() throws IOException
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(BookManagerApplication.class.getResource("login/enroll.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 290, 251);
stage.setTitle("用户注册");
stage.setScene(scene);
stage.show();
ShowController
package com.hk.sky.bookmanager.controller;
import com.hk.sky.bookmanager.bean.Book;
import com.hk.sky.bookmanager.dao.BookDao;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.text.Text;
import java.net.URL;
import java.util.ResourceBundle;
public class ShowController implements Initializable
@FXML
public Text bookId;
Book book=null;
public void setBook(Book book1)
this.book=book1;
private BookDao bookDao = new BookDao();
@FXML
private Text bookName;
@FXML
private Text bookAuthor;
@FXML
private Text bookPublish;
@FXML
private Text bookPrice;
@FXML
private Text bookStock;
@FXML
private Text bookDetail;
//书籍详情页面显示
@Override
public void initialize(URL url, ResourceBundle resourceBundle)
Platform.runLater(() ->
bookId.setText(String.valueOf(book.getId()));
bookName.setText(book.getBookName());
bookAuthor.setText(book.getAuthor());
bookPublish.setText(book.getPublisher());
bookPrice.setText(String.valueOf(book.getPrice()));
bookStock.setText(String.valueOf(book.getStock()));
bookDetail.setText(book.getDetail());
);
module-info
module com.example.bookmange
requires javafx.controls;
requires javafx.fxml;
requires javafx.web;
requires org.controlsfx.controls;
requires validatorfx;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.bootstrapfx.core;
requires eu.hansolo.tilesfx;
requires java.sql;
opens com.hk.sky.bookmanager to javafx.fxml;
exports com.hk.sky.bookmanager;
exports com.hk.sky.bookmanager.controller;
opens com.hk.sky.bookmanager.controller to javafx.fxml;
opens com.hk.sky.bookmanager.bean to javafx.base;
2)resources(为资源文件包,可以看链接文章了解)
Ⅰ、book包
add.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Text?>
<?import java.net.URL?>
<GridPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.hk.sky.bookmanager.controller.AddController"
prefHeight="400.0" prefWidth="600.0" hgap="20" vgap="20">
<padding>
<Insets left="20" top="30"/>
</padding>
<Text text="类型:" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<ComboBox GridPane.halignment="CENTER" GridPane.rowIndex="0" GridPane.columnIndex="1" fx:id="categoryComboBox">
</ComboBox>
<Text text="书名:" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<TextField fx:id="bookName" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Text text="作者:" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<TextField fx:id="bookAuthor" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Text text="出版社:" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<TextField fx:id="bookPublisher" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<Text text="价格:" GridPane.rowIndex="4" GridPane.columnIndex="0"/>
<TextField fx:id="bookPrice" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<Text text="内容简介:" GridPane.rowIndex="5" GridPane.columnIndex="0"/>
<TextField fx:id="detail" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<Text text="库存:" GridPane.rowIndex="6" GridPane.columnIndex="0"/>
<TextField fx:id="bookStock" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Button fx:id="add" text="添加" onAction="#addBook" GridPane.rowIndex="7" GridPane.columnIndex="0" GridPane.columnSpan="2"
GridPane.halignment="CENTER" styleClass="aBtn" visible="true" > </Button>
<Button fx:id="edit" onAction="#editBook" GridPane.rowIndex="7" GridPane.columnIndex="0" GridPane.columnSpan="2"
GridPane.halignment="CENTER" styleClass="eBtn" visible="false"> </Button>
<stylesheets>
<URL value="@add.css"/>
</stylesheets>
</GridPane>
houtai.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import java.net.URL?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.hk.sky.bookmanager.controller.HouTaiController"
prefHeight="550.0" prefWidth="580.0" styleClass="ht">
<Button fx:id="fh" text="返回" visible="false" onAction="#back"/>
<Text id="wm" fx:id="welcomeMsg" AnchorPane.leftAnchor="290" AnchorPane.topAnchor="10"/>
<Button text="修改密码" onActio基于JavaSE + JDBC的图书管理系统
前言
这个数据库管理系统是纯后端的,基于JavaSE和JDBC,第三方jar包用了druid的数据库连接池,本来想用DButils做CRUD,后来想想自己对增删改查的流程还不够熟练所以就自己写了DAO。
在写之前本来觉得挺简单一个事,谁知道越写越复杂,越写发现自己技术的欠缺。所以这个图书管理系统,还存在许多问题,贴出来大家共同学习进步,也真心希望有大佬能提出一些宝贵的建议,在此提前感谢!
设计思路
此处内容属于补充内容,是我在写完整个管理系统后,对于整个项目逻辑的梳理,在实际的代码实现上,可能并没有达到这个设计思路所要求的标准,主要原因在于自己经验和技术的欠缺。
这个设计思路只是一个笼统的概述,不涉及到细节层面,并且这只是我个人的一些设计思路。作为一个Java入门学习者,写出来的目的不是为了授业,而只是为了总结与交流。
本来想写一个针对于细节的总结博客,但是时间实在有限,Java学习之路前途漫漫,还有很多未知的技术等着学习,所以不能止步于眼下的风景,而迷失了学习的目标。相信随着未来学习的深入,对于项目的设计会有更加清晰的认知。
整个项目的设计思路,我总结了3个方面,分别为:数据处理方面(后端),用户交互方面(前端),接口对接方面(前后端交互)。
数据处理方面
- 分析管理系统所需要的信息,然后捋清楚信息之间的关系,并以此创建数据表。
- 分析系统所针对的用户群体,以及用户群体间的关系,并以此创建数据表。
- 分析针对于不同用户所需要提供的功能。
- 将所有功能进行整合,把每一个功能对应到所要操作的数据表中,并统计出每一张表所要提供的CRUD操作、操作需求的参数、以及操作所返回的结果。(CRUD操作的设计要尽可能通用)。
- 根据数据表创建相应的实体类。
- 根据所需提供的操作,编写针对于不同表的CRUD方法以及sql语句,并提供接口。
用户交互方面
- 分析系统所需要的页面,以及每一个页面所需要提供的功能。
- 一般需要提供登录和注册页面,登录页面需要校验用户输入的用户名和密码,将用户名和密码信息传递至后端数据库中查找此条纪录,找到根据用户的身份登录到不同的页面,找不到则提示错误。(对于用户名和密码校验,后端也应该提供接口用于查询)
- 根据用户的身份跳转到相应的页面,该页面中提供了针对于该用户群体所需要的操作。
- 对于用户选择的不同操作。创建不同的接口与后端对应的接口进行交互。
- 如果是添加操作,应该校验所用户输入的信息,对于一些特殊字段,应该先将信息传递至数据库中进行查询校验,并返回结果来决定后续操作是否可以正常进行。当操作完毕后,提示用户操作的结果。
- 如果是删除操作,将用户输入的信息传递至数据库中进行delete操作,如果纪录存在,则会删除成功并返回操作所影响的纪录数。如果纪录不存在,则不会删除数据库任何纪录,并返回0。根据返回值来提示用户操作的结果。
- 如果是修改操作,需要用户输入一个关键字段信息(一般为主键),并在数据库中查询是否存在。
1)不存在则返回null,并提示重新输入。
2)存在则返回承载了该条纪录的对象。并提示用户输入修改信息。(对此操作应该提供给用户一个不修改字段的权力,即如果用户不想修改该字段信息,那么可以提示输入回车表示默认不修改)。
3)当用户输入完毕后,将用户输入的信息重新封装在返回的对象中,并传递至数据库执行update操作。 - 如果是查询操作,针对于不同用户的查询权限,提供相应的功能,并创建不同的接口对接到后端对应的查询接口。对于查询的需求很多,可能涉及到单表查询,多表联查,条件查询,查询关键字段等等,对于不同的查询所需求的参数不同,并且返回的结果也不相同,对于这些查询需求,后端都应该提供相应的接口支持。这一块个人感觉是最难的地方,以自己目前的经验和技术很难设计出通用的查询接口。因此在具体实现时,出现了大量冗余的代码。
接口对接方面
- 针对于不同的功能需要提供不同的接口进行对接,完成前后端的交互。
- 前端需要将用户输入的信息通过接口传递至数据库中。
- 后端需要根据用户的请求对数据进行相应的CRUD操作。
接口对接相关的知识自己还没有学习过,所以在写的时候属于盲人抹黑的状态,需要什么接口就临时写一个,导致了代码在非常冗余,没用很好的运用到自己学习的面向对象特性。
数据库的搭建
刚开始准备写的时候,只想建3张表,一张user表,一张admin表和一张book表,但是后面开始写的时候发现3张表貌似不够用,所以最终我定义了5张表。分别为:
tb_booktype
图书类型表
表结构如下:
该表存放了图书的类型,作为下面的图书信息表的父表。
tid
类型编号,主键自增type
图书类型,唯一键。
tb_bookinfo
图书信息表
表结构如下:
该表用于存放图书详细信息,包括:
bid
图书编号,主键自增,主要用于查询。name
图书名称。author
作者publish
出版社
name
、 author
和 publish
三个字段我做了联合唯一约束,目的防止插入相同的书籍纪录。price
价格num
图书数量tid
图书类型编号,作为外键关联到tb_booktype
表
tb_borrowinfo
图书借阅信息表
表结构如下:
该表用于保存图书借阅的信息,该表关联了tb_bookinfo
表,以及下面介绍的tb_user
表。
bor_id
图书借阅编号,主键自增。book_id
图书编号,作为外键关联 tb_bookinfo
表。user_id
用户编号,作为外键管理tb_user
表。bordate
借阅日期。retdate
应归还日期。在Java中默认为1个月之后。comment
备注。
tb_status
用户身份表
表结构如下:
该表用于纪录管理系统中用户的身份信息。在这个管理系统中,我没有分别定义管理员表和用户表而是用这个status表来区分不同的用户身份。
sid
身份编号,主键自增,设置1为管理员,2为客户。status
用户身份,唯一键,该管理系统中只设置了2个身份。
tb_user
用户信息表
表结构如下:
该表用于存放用户的个人信息,sid
作为外键与tb_status
连接,其它字段不太重要,不在此介绍。
功能
在这个管理系统中,用户分为管理员和客户,根据用户的身份不同给予了不同的功能。
管理员的功能
客户的功能
功能演示视频
视频地址:https://www.bilibili.com/video/BV18h411n75U/
CSDN我没有资格上传。。。
项目结构
对于MVC设计模式只是听过,但具体每一个模块是做什么的不太了解,所以就照葫芦画瓢仿造了一个。
在该管理系统中,我分成了5个package存放不同功能的类和接口。
dao模块
dao模块中是关于数据库增删改查的操作。
baseDAO
baseDAO中定义的是通用的增删改查操作,其它针对于一张表的xxxxDAOImpl类都继承了该类。
package com.goodgoodstudy.dao;
import com.goodgoodstudy.Util.JDBCUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 定义为抽象类表示该类不能被实例化
public abstract class BaseDAO<T> {
// BaseDAO的子类(XxxDAOImpl)在创建对象时,会调用父类(即BaseDAO)的构造器,并加载父类的结构,代码块就会给Clazz初始化
private Class<T> clazz;
{
// 获取参数化类型实例, this既代表了实例化的子类
ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass();
// 调用getActualTypeArguments() 【获取泛型类型】
Type[] types = paramType.getActualTypeArguments();
// 只有一个泛型参数,因此types[0]就是子具体要操作的那个类。
clazz = (Class<T>) types[0];
}
/**
* 通用的增删改操作
*
* @param conn 数据库连接
* @param sql sql语句
* @param param 填充占位符的参数
* @return
*/
public int update(Connection conn, String sql, Object... param) throws SQLException {
// 获取数据库操作对象
PreparedStatement ps = null;
int updateCount = 0;
try {
ps = conn.prepareStatement(sql);
// 填充占位符
for (int i = 0; i < param.length; i++) {
ps.setObject(i + 1, param[i]); // 与数据库相关的索引都是从1开始
}
// 执行操作
updateCount = ps.executeUpdate(); // 接收操作所影响的纪录数量
} catch (SQLException e) {
throw new SQLException(e);
} finally {
JDBCUtils.closeResource(null, ps, null); // 关闭资源,考虑事务,因此不在方法内关闭外部获取的数据库连接
}
return updateCount;
}
/**
* 通用的查询操作(获取一条纪录)
*
* @param conn
* @param sql
* @param param
* @return
*/
public T getInstance(Connection conn, String sql, Object... param) {
PreparedStatement ps = null;
ResultSet rs = null;
T t = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
// 执行查询操作, 并获取结果集
rs = ps.executeQuery();
// 获取结果集元数据
ResultSetMetaData md = rs.getMetaData();
// 通过元数据获取查询纪录的列数
int colCount = md.getColumnCount();
// 判断结果集下一条是否有数据,
if (rs.next()) {
t = clazz.getDeclaredConstructor().newInstance(); // 利用clazz动态创建相应的对象,用了接收结果集的数据
for (int i = 0; i < colCount; i++) {
Object colVal = rs.getObject(i + 1); // 获取每一列的数据
String colLabel = md.getColumnLabel(i + 1); // 获取每一列的别名
Field field = clazz.getDeclaredField(colLabel); // 通过别名创建对应属性的Field对象
field.setAccessible(true); // 访问私有属性,需要设置为true
field.set(t, colVal); // 给t对象的field属性赋值为colVal
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
JDBCUtils.closeResource(null, ps, rs);
}
return t; // 返回承载了纪录的对象
}
/**
* 通用的查询操作(获取多条纪录)
*
* @param conn
* @param sql
* @param param
* @return
*/
public List<T> getForList(Connection conn, String sql, Object... param) {
PreparedStatement ps = null;
ResultSet rs = null;
List<T> list = new ArrayList<>();
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int colCount = md.getColumnCount();
// 获取多条纪录,将if 改为 while
while (rs.next()) {
T t = clazz.getDeclaredConstructor().newInstance();
for (int i = 0; i < colCount; i++) {
Object colVal = rs.getObject(i + 1);
String colLabel = md.getColumnLabel(i + 1);
Field field = clazz.getDeclaredField(colLabel);
field.setAccessible(true);
field.set(t, colVal);
}
list.add(t); // 将接收纪录的对象添加近list中
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
JDBCUtils.closeResource(null, ps, rs);
}
return list; // 返回list
}
/**
* 用于多表联查返回一条记录
*
* @param conn
* @param sql
* @param param
* @return
*/
public Map<String, Object> getInstanceMap(Connection conn, String sql, Object... param) {
PreparedStatement ps = null;
ResultSet rs = null;
Map<String, Object> row = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int colCount = md.getColumnCount();
if (rs.next()) {
// 创建Map接收查询到的一行纪录
row = new HashMap<>();
for (int i = 0; i < colCount; i++) {
row.put(md.getColumnLabel(i + 1), rs.getObject(i + 1));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
JDBCUtils.closeResource(null, ps, rs);
}
return row; // 返回row
}
/**
* 用于多表联查返回多条纪录,将查询的每条纪录以键值对的形式封装在Map中,再存入List集合中并返回
*
* @param conn
* @param sql
* @param param
* @return
*/
public List<Map<String, Object>> getForMapList(Connection conn, String sql, Object... param) {
PreparedStatement ps = null;
ResultSet rs = null;
List<Map<String, Object>> list = new ArrayList<>();
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
rs = ps.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int colCount = md.getColumnCount();
while (rs.next()) {
// 创建Map接收每一行的纪录
Map<String, Object> row = new HashMap<>();
for (int i = 0; i < colCount; i++) {
row.put(md.getColumnLabel(i + 1), rs.getObject(i + 1));
}
list.add(row); // 将map添加进list
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
JDBCUtils.closeResource(null, ps, rs);
}
return list; // 返回list
}
/**
* 用于查询特殊值的通用方法,返回一个特殊值
* 例如:查询总数,最大值等
*
* @param conn
* @param sql
* @param params
* @return
*/
public Object getValue(Connection conn, String sql, Object... params) {
PreparedStatement ps = null;
Object value = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
value = null;
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
rs = ps.executeQuery();
if (rs.next()) {
value = rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return value;
}
}
BookInfoDAO 与 BookInfoDAOImpl
BookInfoDAO
接口
BookInfoDAO
作为接口规定了针对于tb_bookinfo
表的CRUD操作。
package com.goodgoodstudy.dao;
import com.goodgoodstudy.entity.BookInfo;
import java.sql.图书管理系统( JSP + JDBC + Servlet )实现-01: 流程分析和数据库建表阶段