1.数据库t_book表,主键id为bigint类型,type_id为bigint类型(图书所属类别的id)
2.录入一些数据,注意type_id字段,一定要是t_type表存在的id
3.图书实体类Book.java
package com.soft1841.book.entity;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
/**
* 图书实体类
*/
public class Book {
private final SimpleLongProperty id = new SimpleLongProperty();
private final SimpleLongProperty typeId = new SimpleLongProperty();
private final SimpleStringProperty name = new SimpleStringProperty("");
private final SimpleStringProperty author = new SimpleStringProperty("");
private final SimpleDoubleProperty price = new SimpleDoubleProperty();
private final SimpleStringProperty cover = new SimpleStringProperty("");
private final SimpleStringProperty summary = new SimpleStringProperty("");
private final SimpleIntegerProperty stock = new SimpleIntegerProperty();
public Book() {
}
public Book(Long id, Long typeId, String name, String author, Double price, String cover, String summary,Integer stock) {
setId(id);
setTypeId(typeId);
setName(name);
setAuthor(author);
setPrice(price);
setCover(cover);
setSummary(summary);
setStock(stock);
}
public long getId() {
return id.get();
}
public SimpleLongProperty idProperty() {
return id;
}
public void setId(long id) {
this.id.set(id);
}
public long getTypeId() {
return typeId.get();
}
public SimpleLongProperty typeIdProperty() {
return typeId;
}
public void setTypeId(long typeId) {
this.typeId.set(typeId);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public String getAuthor() {
return author.get();
}
public SimpleStringProperty authorProperty() {
return author;
}
public void setAuthor(String author) {
this.author.set(author);
}
public double getPrice() {
return price.get();
}
public SimpleDoubleProperty priceProperty() {
return price;
}
public void setPrice(double price) {
this.price.set(price);
}
public String getCover() {
return cover.get();
}
public SimpleStringProperty coverProperty() {
return cover;
}
public void setCover(String cover) {
this.cover.set(cover);
}
public String getSummary() {
return summary.get();
}
public SimpleStringProperty summaryProperty() {
return summary;
}
public void setSummary(String summary) {
this.summary.set(summary);
}
public int getStock() {
return stock.get();
}
public SimpleIntegerProperty stockProperty() {
return stock;
}
public void setStock(int stock) {
this.stock.set(stock);
}
}
4.BookDAO接口
package com.soft1841.book.dao;
import cn.hutool.db.Entity;
import com.soft1841.book.entity.Book;
import java.sql.SQLException;
import java.util.List;
/**
* 图书DAO
*/
public interface BookDAO {
/**
* 新增图书,返回自增主键
*
* @param book
* @return
* @throws SQLException
*/
Long insertBook(Book book) throws SQLException;
/**
* 根据id删除图书
*
* @param id
* @return
*/
int deleteBookById(long id) throws SQLException;
/**
* 更新图书信息
*
* @param book
* @return
*/
int updateBook(Book book) throws SQLException;
/**
* 查询所有图书
*
* @return
*/
List selectAllBooks() throws SQLException;
/**
* 根据id查询图书信息
*
* @param id
* @return
*/
Entity getBookById(long id) throws SQLException;
/**
* 根据书名关键词模糊查询图书
* @param keywords
* @return
* @throws SQLException
*/
List selectBooksLike(String keywords) throws SQLException;
/**
* 根据图书类别查询图书
* @param typeId
* @return
* @throws SQLException
*/
List selectBooksByTypeId(long typeId) throws SQLException;
/**
* 根据图书类别统计图书数量
* @param typeId
* @return
* @throws SQLException
*/
int countByType(long typeId) throws SQLException;
}
5.BookDAOImpl实现类
package com.soft1841.book.dao.impl;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.sql.Condition;
import com.soft1841.book.dao.BookDAO;
import com.soft1841.book.entity.Book;
import java.sql.SQLException;
import java.util.List;
public class BookDAOImpl implements BookDAO {
@Override
public Long insertBook(Book book) throws SQLException {
return Db.use().insertForGeneratedKey(
Entity.create("t_book")
.set("type_id", book.getTypeId())
.set("name", book.getName())
.set("author", book.getAuthor())
.set("price", book.getPrice())
.set("cover", book.getCover())
.set("summary", book.getSummary())
.set("stock",book.getStock())
);
}
@Override
public int deleteBookById(long id) throws SQLException {
return Db.use().del(
Entity.create("t_book").set("id", id)
);
}
@Override
public int updateBook(Book book) throws SQLException {
//只修改了图书的价格和库存
return Db.use().update(
Entity.create().set("price", book.getPrice())
.set("stock",book.getStock()),
Entity.create("t_book").set("id", book.getId())
);
}
@Override
public List selectAllBooks() throws SQLException {
return Db.use().query("SELECT * FROM t_book ");
}
@Override
public Entity getBookById(long id) throws SQLException {
return Db.use().queryOne("SELECT * FROM t_book WHERE id = ? ", id);
}
@Override
public List selectBooksLike(String keywords) throws SQLException {
return Db.use().findLike("t_book", "name", keywords, Condition.LikeType.Contains);
}
@Override
public List selectBooksByTypeId(long typeId) throws SQLException {
return Db.use().query("SELECT * FROM t_book WHERE type_id = ? ", typeId);
}
@Override
public int countByType(long typeId) throws SQLException {
return Db.use().queryNumber("SELECT COUNT(*) FROM t_book WHERE type_id = ? ", typeId).intValue();
}
}
6.DAOFactory工厂类中增加方法,获得BookDAO的实现类对象
public static BookDAO getBookDAOInstance() {
return new BookDAOImpl();
}
7.单元测试
package com.soft1841.book.dao;
import cn.hutool.db.Entity;
import com.soft1841.book.entity.Book;
import com.soft1841.book.utils.DAOFactory;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
public class BookDAOTest {
private BookDAO bookDAO = DAOFactory.getBookDAOInstance();
@Test
public void insertBook() throws SQLException {
Book book = new Book();
book.setTypeId(1);
book.setName("测试书籍");
book.setAuthor("匿名");
System.out.println(bookDAO.insertBook(book));
}
@Test
public void deleteBookById() throws SQLException {
bookDAO.deleteBookById(40);
}
@Test
public void updateBook() throws SQLException {
Book book = new Book();
book.setId(40);
book.setPrice(11.1);
book.setStock(99);
bookDAO.updateBook(book);
}
@Test
public void selectAllBooks() throws SQLException {
List bookList = bookDAO.selectAllBooks();
bookList.forEach(entity -> System.out.println(entity.getStr("name")));
}
@Test
public void getBookById() throws SQLException {
Entity entity = bookDAO.getBookById(1);
System.out.println(entity);
}
@Test
public void selectBooksLike() throws SQLException {
List bookList = bookDAO.selectBooksLike("少");
bookList.forEach(entity -> System.out.println(entity.getStr("name")));
}
@Test
public void selectBooksByTypeId() throws SQLException {
List bookList = bookDAO.selectBooksByTypeId(1);
bookList.forEach(entity -> System.out.println(entity.getStr("name")));
}
@Test
public void countByType() throws SQLException {
int n = bookDAO.countByType(1);
System.out.println(n);
}
}
8.book.fxml布局文件
9.add_book.fxml布局文件
10.BookController控制器
package com.soft1841.book.controller;
import cn.hutool.db.Entity;
import com.soft1841.book.dao.BookDAO;
import com.soft1841.book.dao.TypeDAO;
import com.soft1841.book.entity.Book;
import com.soft1841.book.entity.Type;
import com.soft1841.book.utils.ComponentUtil;
import com.soft1841.book.utils.DAOFactory;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.net.URL;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
public class BookController implements Initializable {
//布局文件中的表格视图对象,用来显示数据库中读取的所有图书信息
@FXML
private TableView bookTable;
//布局文件中的下拉框组件对象,用来显示数据库中读取的所有图书类别
@FXML
private ComboBox typeComboBox;
//布局文件中的输入文本框对象,用来输入搜索关键词
@FXML
private TextField keywordsField;
//图书模型数据集合,可以实时相应数据变化,无需刷新
private ObservableList bookData = FXCollections.observableArrayList();
//图书类型模型数据集合
private ObservableList typeData = FXCollections.observableArrayList();
//图书DAO对象,从DAO工厂通过静态方法获得
private BookDAO bookDAO = DAOFactory.getBookDAOInstance();
//图书类型DAO对象
private TypeDAO typeDAO = DAOFactory.getTypeDAOInstance();
//图书实体集合,存放数据库图书表各种查询的结果
private List bookList = null;
//类别实体集合,存放数据库类别表查询结果
private List typeList = null;
//表格中的编辑列
private TableColumn editCol = new TableColumn<>("操作");
//表格中的删除列
private TableColumn delCol = new TableColumn<>("操作");
//初始化方法,通过调用对图书表格和列表下拉框的两个封装方法,实现数据初始化
@Override
public void initialize(URL location, ResourceBundle resources) {
initTable();
initComBox();
}
//表格初始化方法
private void initTable() {
//水平方向不显示滚动条,表格的列宽会均匀分布
bookTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
//1.调用底层查询所有图书的方法,
try {
bookList = bookDAO.selectAllBooks();
} catch (SQLException e) {
e.printStackTrace();
}
//将实体集合作为参数,调用显示数据的方法,可以在界面的表格中显示图书模型集合的值
showBookData(bookList);
//2.编辑列的相关设置
editCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()));
editCol.setCellFactory(param -> new TableCell() {
//通过ComponentUtil工具类的静态方法,传入按钮文字和样式,获得一个按钮对象
private final Button editButton = ComponentUtil.getButton("编辑", "blue-theme");
@Override
protected void updateItem(Book book, boolean empty) {
super.updateItem(book, empty);
if (book == null) {
setGraphic(null);
return;
}
setGraphic(editButton);
//点击编辑按钮,弹出窗口,输入需要修改的图书价格
editButton.setOnAction(event -> {
TextInputDialog dialog = new TextInputDialog("请输入价格");
dialog.setTitle("图书修改界面");
dialog.setHeaderText("书名:" + book.getName());
dialog.setContentText("请输入新的价格:");
Optional result = dialog.showAndWait();
//确认输入了内容,避免NPE
if (result.isPresent()) {
//获取输入的新价格并转化成Double数据
String priceString = result.get();
book.setPrice(Double.parseDouble(priceString));
try {
//更新图书信息
bookDAO.updateBook(book);
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
});
//将编辑列加入图书表格
bookTable.getColumns().add(editCol);
//3.删除列的相关设置
delCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()));
delCol.setCellFactory(param -> new TableCell() {
private final Button deleteButton = ComponentUtil.getButton("删除", "warning-theme");
@Override
protected void updateItem(Book book, boolean empty) {
super.updateItem(book, empty);
if (book == null) {
setGraphic(null);
return;
}
setGraphic(deleteButton);
//点击删除按钮,需要将这一行从表格移除,同时从底层数据库真正删除
deleteButton.setOnAction(event -> {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("确认对话框");
alert.setHeaderText("请确认");
alert.setContentText("确定要删除这行记录吗?");
Optional result = alert.showAndWait();
//点击了确认按钮,执行删除操作,同时移除一行模型数据
if (result.get() == ButtonType.OK){
bookData.remove(book);
try {
bookDAO.deleteBookById(book.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
});
//将除列加入图书表格
bookTable.getColumns().add(delCol);
//4.图书表格双击事件,双击弹出显示图书详情的界面
bookTable.setRowFactory(tv -> {
TableRow row = new TableRow<>();
row.setOnMouseClicked(event -> {
//判断鼠标双击了一行
if (event.getClickCount() == 2 && (!row.isEmpty())) {
//获得该行的图书ID属性
long id = row.getItem().getId();
Book book = new Book();
try {
//通过ID到数据库查询到该图书对象的完整信息,因为表格中没有显示全,如
//图书封面、摘要等,但是在详情界面需要这些数据,如果不查出完整信息,会有空值异常
Entity entity = bookDAO.getBookById(id);
book.setId(entity.getLong("id"));
book.setTypeId(entity.getLong("type_id"));
book.setName(entity.getStr("name"));
book.setAuthor(entity.getStr("author"));
book.setPrice(entity.getDouble("price"));
book.setCover(entity.getStr("cover"));
book.setSummary(entity.getStr("summary"));
book.setStock(entity.getInt("stock"));
} catch (SQLException e) {
e.printStackTrace();
}
// System.out.println(book.getName() + book.getPrice() + book.getCover() + book.getSummary());
//创建一个新的图书详情界面窗口
Stage bookInfoStage = new Stage();
bookInfoStage.setTitle("图书详情界面");
//用VBox显示具体图书信息
VBox vBox = new VBox();
vBox.setSpacing(10);
vBox.setAlignment(Pos.CENTER);
vBox.setPrefSize(600, 400);
vBox.setPadding(new Insets(10, 10, 10, 10));
Label nameLabel = new Label("书名:" + book.getName());
nameLabel.getStyleClass().add("font-title");
Label authorLabel = new Label("作者:" + book.getAuthor());
Label priceLabel = new Label("价格:" + book.getPrice());
Label stockLabel = new Label("库存:"+book.getStock());
ImageView bookImgView = new ImageView(new Image(book.getCover()));
bookImgView.setFitHeight(150);
bookImgView.setFitWidth(120);
Label summaryLabel = new Label(book.getSummary());
summaryLabel.setPrefWidth(400);
summaryLabel.setWrapText(true);
summaryLabel.getStyleClass().add("box");
vBox.getChildren().addAll(nameLabel, authorLabel, priceLabel,stockLabel,bookImgView, summaryLabel);
Scene scene = new Scene(vBox, 640, 480);
//因为是一个新的窗口,需要重新读入一下样式表,这个界面就可以使用style.css样式表中的样式了
scene.getStylesheets().add("/css/style.css");
bookInfoStage.setScene(scene);
bookInfoStage.show();
}
});
return row;
});
}
//下拉框初始化方法
private void initComBox() {
//1.到数据库查询所有的类别
try {
typeList = typeDAO.selectAllTypes();
} catch (SQLException e) {
e.printStackTrace();
}
//2.遍历typeList集合,将其加入typeData模型数据集合
for (Entity entity : typeList) {
Type type = new Type();
type.setId(entity.getLong("id"));
type.setTypeName(entity.getStr("type_name"));
typeData.add(type);
}
typeComboBox.setItems(typeData);
//4.下拉框选择事件监听,根据选择不同的类别,图书表格中会过滤出该类别的图书
typeComboBox.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) -> {
// System.out.println(newValue.getId() + "," + newValue.getTypeName());
//移除掉之前的数据
bookTable.getItems().removeAll(bookData);
try {
//根据选中的类别查询该类别所有图书
bookList = bookDAO.selectBooksByTypeId(newValue.getId());
} catch (SQLException e) {
e.printStackTrace();
}
//重新显示数据
showBookData(bookList);
}
);
}
//显示图书表格数据的方法
private void showBookData(List bookList) {
for (Entity entity : bookList) {
Book book = new Book();
book.setId(entity.getInt("id"));
book.setName(entity.getStr("name"));
book.setAuthor(entity.getStr("author"));
book.setPrice(entity.getDouble("price"));
book.setStock(entity.getInt("stock"));
bookData.add(book);
}
bookTable.setItems(bookData);
}
//弹出新增图书界面方法
public void newBookStage() throws Exception {
Stage addBookStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/add_book.fxml"));
AnchorPane root = fxmlLoader.load();
Scene scene = new Scene(root);
scene.getStylesheets().add("/css/style.css");
AddBookController addBookController = fxmlLoader.getController();
addBookController.setBookData(bookData);
addBookStage.setTitle("新增图书界面");
//界面大小不可变
addBookStage.setResizable(false);
addBookStage.setScene(scene);
addBookStage.show();
}
//根据关键词搜索方法
public void search() {
bookTable.getItems().removeAll(bookData);
//获得输入的查询关键字
String keywords = keywordsField.getText().trim();
try {
bookList = bookDAO.selectBooksLike(keywords);
} catch (SQLException e) {
e.printStackTrace();
}
showBookData(bookList);
}
}
11.AddBookController控制器
package com.soft1841.book.controller;
import cn.hutool.db.Entity;
import com.soft1841.book.dao.BookDAO;
import com.soft1841.book.dao.TypeDAO;
import com.soft1841.book.entity.Book;
import com.soft1841.book.entity.Type;
import com.soft1841.book.utils.DAOFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.net.URL;
import java.sql.SQLException;
import java.util.List;
import java.util.ResourceBundle;
public class AddBookController implements Initializable {
private ObservableList bookData = FXCollections.observableArrayList();
public ObservableList getBookData() {
return bookData;
}
public void setBookData(ObservableList bookData) {
this.bookData = bookData;
}
@FXML
private ComboBox bookType;
@FXML
private TextField bookName, bookAuthor, bookPrice, bookCover,bookStock;
@FXML
private TextArea bookSummary;
private ObservableList typeData = FXCollections.observableArrayList();
private BookDAO bookDAO = DAOFactory.getBookDAOInstance();
private TypeDAO typeDAO = DAOFactory.getTypeDAOInstance();
private List entityList = null;
private Long typeId;
@Override
public void initialize(URL location, ResourceBundle resources) {
try {
entityList = typeDAO.selectAllTypes();
} catch (SQLException e) {
e.printStackTrace();
}
for (Entity entity : entityList) {
Type type = new Type();
type.setId(entity.getLong("id"));
type.setTypeName(entity.getStr("type_name"));
typeData.add(type);
}
bookType.setItems(typeData);
bookType.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) -> {
typeId = newValue.getId();
}
);
}
public void addBook() throws Exception {
String name = bookName.getText();
String author = bookAuthor.getText();
String price = bookPrice.getText();
String stock = bookStock.getText();
String cover = bookCover.getText();
String summary = bookSummary.getText();
System.out.println(stock);
Book book = new Book();
book.setTypeId(typeId);
book.setName(name);
book.setAuthor(author);
book.setPrice(Double.parseDouble(price));
book.setStock(Integer.parseInt(stock));
book.setCover(cover);
book.setSummary(summary);
long id = bookDAO.insertBook(book);
book.setId(id);
this.getBookData().add(book);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("提示信息");
alert.setHeaderText("新增图书成功!");
alert.showAndWait();
Stage stage = (Stage) bookName.getScene().getWindow();
stage.close();
}
}
12.运行效果:
模型和视图绑定后,无需刷新界面,新增、修改、删除都可以无刷新实时显示。
-
图书信息显示
-
根据类别过滤
-
根据关键词搜索
-
新增图书
-
编辑价格
-
删除