前段时间学习完了MySQL和JDBC,想着自己做个东西实战一下,于是写了一个烂大街的图书管理系统。。。但好歹是自己一个人日夜兼程,硬着头皮做出来的还像样的东西,总结一手吧。看的人欢迎吐槽。我会从以下几个方面进行总结。(总结的是我目前认为的以及怎么做的)
管理信息系统的建立与应用可以划分成总体规划、系统开发和系统运行三个阶段,其中系统开发阶段还可进一步分为系统分析、系统设计和系统实施等工作环节。上述各个阶段排列成一个严格的线性开发序列,在每个工作阶段均产生完整的技术文档作为下一阶段工作的指导和依据,每一阶段都应对文档进行评审,确信该阶段工作已完成并达到要求后才能进入下一阶段,同时在以后的工作中不能轻易改变前面经过评审的成果。
国外曾有人对一些软件项目开发各阶段的工作量进行了统计,结果表明,在开发过程中各工作阶段所占全部工作量的比重分别达到系统分析:系统设计:编程:模块调试:系统调试=20%:15%:20%:25%:20%。由此可见,程序编写在开发工作中只占很小比例,而调试工作却占整个开发工作量的一半左右
在做这个图书管理系统之前,我大概看了几篇相关的论文,我的大致的步骤如下:
MySQL8.0.21、IDEA开发工具、JDK11.0、JDBC、SpringJDBC-Template、DBCP开源数据连接池
图形界面使用Swing组件
对每个工具有认识、懂得如何使用。(swing不太懂就扑街了)
需要考虑:①系统的处理对象;②系统的功能、具体的业务;③对具体的业务有流程图设计;④将业务需求进行模块划分
处理对象:
系统功能、业务
从处理对象的角度去分析
流程图设计
我没有设计,按照自己的思路写的。
功能模块划分
具体功能还需要细分、设计实现
大写强调 ;
设计数据库字段用英文 !
我们要设计数据库要考虑
books表
书编号(主键)、书名、价格、剩余量、出版商、作者、出版日期、类型
user表
字段:id主键、用户名、密码、是否管理员(这里可以添加权限,我没有设计,0非管理员1管理员)
reader表
id关联user表的id,名字、可借书的天数、可借书本数、性别、已借书数、人生格言(默认值)
borrow表
借书者的id、所借书id、借书日期、是否归还、截止还书日期
创建的触发器
1、当书的余量为0,读者想要借书无法借
2、当读者成功借书、则余量-1且borrow表添加对应的借书记录
3、当读者还书时、则余量+1、且borrow表删除对应的记录
4、当添加图书时,检查出版日期不能超过今天
创建的表结构的MySQL语句汇合:
创建book表——>创建user表——>创建reader表——>创建borrow表
1、Books表
创建:
CREATE TABLE `books` (
id int NOT NULL,
name varchar(100) NOT NULL,
price float NOT NULL DEFAULT '0',
surples int NOT NULL DEFAULT '0',
publisher varchar(200) DEFAULT NULL,
author varchar(50) DEFAULT NULL,
publish timestamp NULL DEFAULT NULL,
type varchar(50) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
触发器:
create trigger before_booksInsert before insert on books for each row
begin
if new.publish >=now() then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'publish Time is after nowadays!' ;
end if;
end
-----------
2、borrow表
创建表:
CREATE TABLE `borrow` (
reader_id int NOT NULL,
book_id int NOT NULL,
borrow_date timestamp NULL DEFAULT NULL,
wether_return varchar(2) DEFAULT NULL,
return_date timestamp NULL DEFAULT NULL,
PRIMARY KEY (reader_id,book_id),
KEY book_id (book_id),
CONSTRAINT `borrow_ibfk_1` FOREIGN KEY (reader_id) REFERENCES `reader` (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `borrow_ibfk_2` FOREIGN KEY (book_id) REFERENCES `books` (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `borrow_chk_1` CHECK (( wether_return in (_utf8mb4'yes',_utf8mb4'no')))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
触发器:
create trigger before_borrowupdate before update on borrow for each row
begin
if old.borrow_date is not null then
set new.borrow_date=old.borrow_date;
end if;
end
触发器:
create trigger after_borrowinsert after insert on borrow for each row
begin
update books set surples=surples-1 where id=new.book_id;
end
触发器:
create trigger before_borrowinsert before insert on borrow for each row
begin
declare c int;
set c = (select surples from books where id=new.book_id);
if c=0 then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'There is no books';
end if;
end
---------
3、user表
创建表:
CREATE TABLE `user` (
id int NOT NULL,
name varchar(45) NOT NULL,
password varchar(45) NOT NULL,
isAdmin smallint DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
4、reader
创建表;
CREATE TABLE `reader` (
`id` int NOT NULL,
`name` varchar(20) DEFAULT NULL,
`borrow_day` int NOT NULL DEFAULT '90',
`borrow_num` int NOT NULL DEFAULT '30',
`sex` varchar(6) DEFAULT NULL,
`life_motto` varchar(45) DEFAULT '读万卷书,行万里路!',
PRIMARY KEY (`id`),
CONSTRAINT `reader_chk_1` CHECK ((`sex` in (_utf8mb4'man',_utf8mb4'woman')))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
这里可以添加了一个外键:reader id 关联了user的id不然需要在设计程序的时候注意
这个过程中遇到的错误:
MySQL 为日期增加一个时间间隔:date_add()
select date_add(now(), interval 1 day); - 加1天
select date_add(now(), interval 1 hour); -加1小时
select date_add(now(), interval 1 minute); - 加1分钟
select date_add(now(), interval 1 second); -加1秒
select date_add(now(), interval 1 microsecond);-加1毫秒
select date_add(now(), interval 1 week);-加1周
select date_add(now(), interval 1 month);-加1月
select date_add(now(), interval 1 quarter);-加1季
select date_add(now(), interval 1 year);-加1年
MySQL adddate(), addtime()函数,可以用date_add() 来替代。
2. MySQL 为日期减去一个时间间隔:date_sub()
MySQL date_sub() 日期时间函数 和date_add() 用法一致。
MySQL 中subdate(),subtime()函数,建议,用date_sub()来替代。
我们需要将与数据库的交互进行封装
在java里面创建表映射对象,我们对对象的操作就可以操作对应的表数据了。
如books表的映射
package DatabasesOperation.DAO_Design.ORM;
import java.util.Date;
/**
* ORM映射books表
*/
public class ORM_Books {
private int id;//书的编号
private String name;//书名
private float price;//价格
private int surples;//剩余量
private String publisher;//出版商
private String author;//作者
private Date publish;//出版日期
private String type;//类型
public static String[] columnNames = {"编号", "书名", "作者", "价格/元", "出版社", "出版日期", "类型", "余量"};//book的数据
public ORM_Books() {//空参构造器
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public int getSurples() {
return surples;
}
public void setSurples(int surples) {
this.surples = surples;
}
public String getPublisher() {
return publisher;
}
public void setPublisher(String publisher) {
this.publisher = publisher;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getPublish() {
return publish;
}
public void setPublish(Date publish) {
this.publish = publish;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Books[id:" + getId() + " name:" + getName() +
" author:" + getAuthor() + " surples:" + getSurples()
+ "publisher:" + getPublisher() + " publish:" + getPublish() + " price:" +
getPrice() + "type" + getType() + "]";
}
/**
* 只要传入的string数组的属性与books的属性一一对应,我们就可以添加图书
*
* @param res 传入的属性数组
*/
private void setBooks(String[] res) {
String s;
int i = 0;
while (i < res.length) {
s = res[i];
switch (i) {
case 0:
int id = Integer.parseInt(s);
this.setId(id);
break;
case 1:
this.setName(s);
break;
case 2:
float price = Float.parseFloat(s);
this.setPrice(price);
break;
case 3:
int surples = Integer.parseInt(s);
this.setSurples(surples);
break;
case 4:
this.setPublisher(s);
break;
case 5:
this.setAuthor(s);
break;
case 6:
java.util.Date publish = java.sql.Date.valueOf(s);
this.setPublish(publish);
break;
case 7:
this.setType(s);
break;
default:
break;
}
i++;
}
}
public static String[] booksToArray(ORM_Books books){//将一本书各个字段提取出来,变成一维数组
return new String[]{
Integer.toString(books.getId()),
books.getName(),
books.getAuthor(),
Float.toString(books.getPrice()),
books.getPublisher(),
books.getPublish().toString(),
books.getType(),
Integer.toString(books.getSurples())
};
}
}
在Java中开源的数据库连接池有以下几种 :
1、C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate [2] 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
2、Proxool:是一个Java SQL Driver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。
3、Jakarta DBCP:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。
4、DDConnectionBroker:是一个简单、轻量级的数据库连接池。
5、DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。
我们使用是DBCP
利用该数据库连接池我们创建封装好的JDBC实例:
相关的配置我们写到一个文件里面:
然后我们创建工具类用于获取数据库连接池的datasource
package DatabasesOperation.JDBCUtils;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 创建一个工具类,用于创建jdbc加载驱动,释放资源等,优化Test_Driver代码
*/
public final class JDBCUtils {
private static DataSource datasource;
public static Connection getConnect() throws SQLException {//异常应该抛出
return datasource.getConnection();
}
static {
try {
InputStream inputs = JDBCUtils.class.getClassLoader().getResourceAsStream("DBCP_Config.properties");
Properties properties = new Properties();
assert inputs != null;
properties.load(inputs);
datasource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void free(Connection conn, Statement st, ResultSet res) {
try {
if (res != null) //原则1:晚点连接早点释放。原则2:先创建的后释放
res.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (st != null)
st.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null)
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static DataSource getDatasource() {
return datasource;
}
}
先利用SpringJDBCTemplate返回被封装的JDBC,类型是带命名参数的JDBCTemplate
package DatabasesOperation.DAOImplSpring;
import DatabasesOperation.JDBCUtils.JDBCUtils;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
/**
* 使用SpringTemplate实现各个表ORM对象的CRUD;
* 这个类主要返回一个注册了数据源的命名参数Spring-template 即:nJdbc;
*/
public class DAOImplSpringTemplate {
private static NamedParameterJdbcTemplate nJdbc;
public static NamedParameterJdbcTemplate getJDBC() {
if (nJdbc == null)//多线程安全问题
synchronized (DAOImplSpringTemplate.class) {
if (nJdbc == null)
nJdbc = new NamedParameterJdbcTemplate(JDBCUtils.getDatasource());
}
return nJdbc;
}
}
创建对应表的DAOImpl,如books的DAOImpl实现CRUD操作:
package DatabasesOperation.DAO_Design.DAOImpl;
import DatabasesOperation.DAOImplSpring.DAOImplSpringTemplate;
import DatabasesOperation.DAO_Design.ORM.ORM_Books;
import DatabasesOperation.JDBCUtils.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import java.io.*;
import java.sql.*;
import java.util.*;
/**
* 实现CRUD_BOOKS
*/
public class DAOBooks {
private NamedParameterJdbcTemplate nJDBC;
private static String[] book = new String[]{//book的映射,不好维护,应该使用文件读取更新操作。
"id",
"name",
"price",
"surples",
"publisher",
"author",
"publish",
"type"
};
public DAOBooks() {
nJDBC = DAOImplSpringTemplate.getJDBC();
}
public void addBooks(ORM_Books book) {//添加书
if (!isExistId(book.getId())) {//id不存在才允许插入
String sql =
"insert into books(id,name,price,surples,publisher,author,publish,type) Values(:id,:name,:price,:surples,:publisher,:author,:publish,:type)";
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(book);
nJDBC.update(sql, parameterSource);
} else System.out.println("id=" + book.getId() + "的书已经存在!插入无法完成!");//可以利用弹窗
}
/**
* 最终的方法:将文件里面的books信息插入到表中。
*
* @param file 含有books内容的文件
*/
public void addBooksByFile(File file) throws IOException {
//文件一行代表一本书,属性之间使用#分割,且用对应的字段名问号值
//如price:400#name:大英###surples:3。。
//通过文件,将文件里面的关于书的信息导入。属于批量导入
BufferedReader buff = null;
try {
buff = new BufferedReader(new FileReader(file));
String s;
List<ORM_Books> books = new ArrayList<>();
while ((s = buff.readLine()) != null) {
String[] res = s.split("#+");
ORM_Books books1 = setBooks(res);
if (!isExistId(books1.getId()))//不存在才添加
{
books.add(setBooks(res));
}
}
SqlParameterSource[] params = new BeanPropertySqlParameterSource[books.size()];
for (int i = 0; i < books.size(); i++) {
params[i] = new BeanPropertySqlParameterSource(books.get(i));
}
addBatchBooks(params);
} finally {
if (buff != null)
try {
buff.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 批量操作,将传入的参数源多个,批量插入到表中。
*
* @param batchArgs 要插入的参数,book数组
*/
private void addBatchBooks(SqlParameterSource[] batchArgs) {
String sql =
"insert into books(id,name,price,surples,publisher,author,publish,type) Values(:id,:name,:price,:surples,:publisher,:author,:publish,:type)";
nJDBC.batchUpdate(sql, batchArgs);
}
/**
* 根据读出来的字符集构造book类返回
*
* @param res 文件里面读出来的字符集,一次代表一个book
* @return book
*/
private ORM_Books setBooks(String[] res) {
ORM_Books books;
if (res.length <= book.length)//必须与books里的字段长度一致
{
books = new ORM_Books();
String s;
int i = 0;
while (i < res.length && res[i].contains(book[i])) {
while (res[i].equals(""))//去除空行
i = i + 1;
s = res[i].substring(book[i].length() + 1);//将值取出来
s = s.replace(" ", "");//去除空格
switch (i) {
case 0:
int id = Integer.parseInt(s);
books.setId(id);
break;
case 1:
books.setName(s);
break;
case 2:
float price = Float.parseFloat(s);
books.setPrice(price);
break;
case 3:
int surples = Integer.parseInt(s);
books.setSurples(surples);
break;
case 4:
books.setPublisher(s);
break;
case 5:
books.setAuthor(s);
break;
case 6:
java.util.Date publish = java.sql.Date.valueOf(s);
books.setPublish(publish);
break;
case 7:
books.setType(s);
default:
break;
}
i++;
}
return books;
} else throw new RuntimeException("文件的格式不对!");
}
public void updateBooks(ORM_Books book) {//根据id找到该书,将其更新
String sql =
"update books set name=:name,price=:price,surples=:surples,publisher=:publisher,author=:author,publish=:publish,type=:type where id=:id";
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(book);
nJDBC.update(sql, parameterSource);
}
public void updateBooksBatch(List<ORM_Books> books){
String sql =
"update books set name=:name,price=:price,surples=:surples,publisher=:publisher,author=:author,publish=:publish,type=:type where id=:id";
SqlParameterSource[] parameterSource = new SqlParameterSource[books.size()];
int i=0;
for(ORM_Books books1:books){
parameterSource[i] = new BeanPropertySqlParameterSource(books1);
i++;
}
nJDBC.batchUpdate(sql, parameterSource);
}
public List<Map<String, Object>> findBooks(String keyword) {//根据关键字查看对应的书
String sql = "select * from books where name Like :keyword";
Map<String, String> params = new HashMap<>();
params.put("keyword", "%" + keyword + "%");
return nJDBC.queryForList(sql, params);
}
public static List<ORM_Books> displayBooks(String keyword) {//展示书本,返回list
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<ORM_Books> list = new ArrayList<>();
try {
conn = JDBCUtils.getConnect();//从连接池里面拿连接,不浪费资源
String sql;
if(keyword==null){
sql = "select * from books";
ps = conn.prepareStatement(sql);
}else {
sql = "select * from books where name like ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"%"+keyword+"%");
}
rs = ps.executeQuery();
ORM_Books books;
while (rs.next()) {
books = new ORM_Books();
books.setId(rs.getInt("id"));
books.setName(rs.getString("name"));
books.setSurples(rs.getInt("surples"));
books.setPrice(rs.getFloat("price"));
books.setAuthor(rs.getString("author"));
books.setPublisher(rs.getString("publisher"));
books.setPublish(rs.getDate("publish"));
books.setType(rs.getString("type"));
list.add(books);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.free(conn, ps, rs);
}
return list;
}
public ORM_Books findBooksByName(String book_name) {//根据书的名字查看对应的书
String sql = "select * from books where name=:name";
Map<String, String> params = new HashMap<>();
params.put("name", book_name);
/*Incorrect column count: expected 1, actual 8
同时不仅是jdbcTemplate.queryForList不能这么使用,
queryForObject同样也不能这么使用,而是应该添加new RowMapper接口才能返回自定义的实体类对象。
*/
List<ORM_Books> books = nJDBC.query(sql, params, new BeanPropertyRowMapper<>(ORM_Books.class));
if (books.size() == 0)//不能判断null,因为及时返回的元素个数为0,这个list也不是null
throw new RuntimeException("不存在这样的书名!");
return books.remove(0);//返回第一个
}
private boolean isExistId(int id) {//判断id是否存在
String sql = "select id from books where id=:id";
Map<String, Integer> params = new HashMap<>();
params.put("id", id);
List<ORM_Books> list = nJDBC.query(sql, params, new BeanPropertyRowMapper<>(ORM_Books.class));
return list.size() != 0;
}
public void deleteBooks(int id) {//根据书的id来删除书本
String sql = "delete from books where id=:id";
Map<String, Integer> params = new HashMap<>();
params.put("id", id);
nJDBC.update(sql, params);
}
public static int maxId(){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
int id = 0;
try{
String sql = "select max(id) from books";
conn = JDBCUtils.getConnect();
st = conn.createStatement();
rs = st.executeQuery(sql);
if(rs.next()){
id = rs.getInt("max(id)");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.free(conn,st,rs);
}
return id;
}
}
package DatabasesOperation.Test;
import DatabasesOperation.DAO_Design.DAOImpl.DAOBooks;
import DatabasesOperation.DAO_Design.DAOImpl.DAOUser;
import DatabasesOperation.DAO_Design.ORM.ORM_Books;
import GUIModule.PublishMethodGet.Constant_Size;
import GUIModule.PublishMethodGet.RepaintJPanel;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 测试类,用于测试每个DAO实现类的CRUD是否可以实现。
* 已经通过测试:books表, user表、Reader表、borrow表
*/
public class TestDAOimpl {
/*public static void main(String[] args) throws IOException {
DAOBooks daoBooks = new DAOBooks();
daoBooks.addBooksByFile(new File("src/main/resources/books.txt"));
}*/
}
管理员的登陆界面也是这个,但是会添加额外功能
以读者身份登陆后显示个人信息,头像动态生成,man则是男头像,woman则是女头像
点击借书按钮,如果没有借书记录则不进入还书界面
点击借书按钮,进入馆藏资源借书,然后返回:已借书数动态更新
点击还书按钮:
输入id还书或者全部归还
在登陆界面登陆提示管理员以后,点击是,会进入管理界面,弹出问候语
这里会有很多操作,功能实现等。采用了菜单栏和菜单、菜单选项、监听对应的菜单,触发不同的功能。
一些还没写的功能就用:
2、如何创建一个Map数组以及其实例化
Map<String,Integer>[] maps =new Map[res.length];
for(int k=0;k<res.length;k++){
//同时对应的书的数量加1
maps[k] = new HashMap<String, Integer>();
(maps[k]).put("id1",res[k]);
}
nJDBC.batchUpdate(sql2,maps);
jComboBox_sex = new JComboBox<>(new String[]{"woman", "man"});//文本选择框,只能选择woman或者man
String sex = Objects.requireNonNull(jComboBox_sex.getSelectedItem()).toString();
千万不要信哪些人说的添加Jlabel,治标不治本的。设置透明毫无美感。可以自己手写一个类来实现,利用了重新绘制的函数。
package GUIModule.PublishMethodGet;
import javax.swing.*;
import java.awt.*;
/**
* 能够设置背景图片,不是通过设置label实现的
*/
public class RepaintJPanel extends JPanel {
//设置主页背景图片的JPanel类
ImageIcon icon;
Image img;
public RepaintJPanel(String path) {
// /img/HomeImg.jpg 是存放在你正在编写的项目的bin文件夹下的img文件夹下的一个图片
icon = new ImageIcon(path);
img = icon.getImage();
}
/*private RepaintJPanel(ImageIcon icon) {
this.icon = icon;
}*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
//下面这行是为了背景图片可以跟随窗口自行调整大小,可以自己设置成固定大小
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
}
}
思路:将数据库的数据读取出来,然后得到了二维数据数据bookDate,给表创建单元格编辑器,在编辑器里可以设置对值的检验。然后在监听对应的表数据,将对应的修改值存起来,选择的行利用主键唯一性存起来。然后利用一个按钮来确认删除.就将存的数据提取处理删除。
表格编辑器
package GUIModule.PublishMethodGet;
import javax.swing.*;
/**
* 自动义单元格编辑器,不允许空格
*/
public class MyCellEditor extends DefaultCellEditor {
private JFrame jFrame;
public MyCellEditor(JTextField textField, JFrame jFrame) {
super(textField);
this.jFrame = jFrame;
}
@Override
public boolean stopCellEditing() {
// 获取当前单元格的编辑器组件
// Component comp = getComponent();
// 获取当前单元格编辑器输入的值
Object obj = getCellEditorValue();
// 如果当前单元格编辑器输入的值不是数字,则返回 false(表示数据非法,)
if (obj == null || obj.toString().equals("")) {
JOptionPane.showMessageDialog(jFrame, "不允许输入空值", "警告!", JOptionPane.ERROR_MESSAGE);
return false;//不允许设置,无法保存,不调用super.stopCellEdition
}
// 合法数据交给父类处理
return super.stopCellEditing();
}
}
给表格添加该编辑器
// 创建单元格编辑器,使用文本框作为编辑组件
MyCellEditor cellEditor = new MyCellEditor
(new JTextField("", jTable_book.getColumnModel().getColumn(2).getWidth()), mainJFrame);
// 遍历表格中所有数字列,并设置列单元格的编辑器
for (int i = 1; i < ORM_Books.columnNames.length; i++) {
// 根据 列名 获取 表格列
TableColumn tableColumn = jTable_book.getColumn(ORM_Books.columnNames[i]);
// 设置 表格列 的 单元格编辑器
tableColumn.setCellEditor(cellEditor);
}
对表格更新操作进行监听,然后将数据保存到bookData,但是未保存到数据库。在确认按钮上添加监控,点击,则进行对应事件的处理。
///表格监听,编辑表格得到的数据将保存到实际的数据库,保存前会发出提示
jTable_book.getModel().addTableModelListener(e -> {
int type = e.getType();
if (type == TableModelEvent.UPDATE)//更新操作触发
{
int row_first = jTable_book.getEditingRow(); //获取改变的行,默认每改变一个格子则弹出一次窗口
int col = jTable_book.getEditingColumn();//获取改变的列
String newInformation = jTable_book.getValueAt(row_first, col).toString();//获取新行的值,视觉行
String id = jTable_book.getValueAt(row_first, 0).toString();//获取id
alter_row.add(id);//获取id值
for (int i = 0; i < bookData.length; i++) {
if (bookData[i][0].equals(id)) {
bookData[i][col] = newInformation;//先缓存到数据bookdata里面
break;
}
}
}
});
jButton_confirm.addMouseListener(new MouseListenerNew(jButton_confirm) {
@Override
public void mouseClicked(MouseEvent e) {
while (jTable_book.isEditing()) {//注意这个操作,当在编辑行时,如果没有保存数据,点击button,没有下面的代码二无法实现更新
jTable_book.getCellEditor(jTable_book.getEditingRow(), jTable_book.getEditingColumn()).stopCellEditing();
}
//确认更新操作,会提示真的更新所有修改到数据库?
int value = JOptionPane.showConfirmDialog(jFrame, "所有的修改会保存到对应的数据库,是否继续?", "提示", JOptionPane.YES_NO_OPTION);
if (value == JOptionPane.YES_OPTION) {
DAOBooks daoBooks = new DAOBooks();
List<ORM_Books> list = new ArrayList<>();
JOptionPane.showMessageDialog(jFrame, "修改中...,请稍等", "提示", JOptionPane.INFORMATION_MESSAGE);
for (int i = 0; i < alter_row.size(); i++) {
String s = alter_row.remove(i);//获取被修改的书的id
// System.out.println("移除的id____"+s);
for (String[] bookDatum : bookData) {
if (bookDatum[0].equals(s)) {//找到该id
ORM_Books books = new ORM_Books();
books.setId(Integer.parseInt(s));//设置id,根据id来修改的
books.setName(bookDatum[1]);
books.setAuthor(bookDatum[2]);
books.setPrice(Float.parseFloat(bookDatum[3].trim()));
books.setPublisher(bookDatum[4]);
books.setPublish(Date.valueOf(bookDatum[5]));
books.setType(bookDatum[6]);
books.setSurples(Integer.parseInt(bookDatum[7].trim()));
//alter_row.remove(i);//修改了,则需要在记录数组中移除
list.add(books);
}
}
}
if (list.size() == 1)//一个的时候不需要
daoBooks.updateBooks(list.get(0));
else daoBooks.updateBooksBatch(list);//批量更新
JOptionPane.showMessageDialog(jFrame, "修改成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
}
}
});
删除多选的行操作,因为你移除了一次行,对应的行的索引会变化,你要像我这么做:
jButton_delete.addMouseListener(new MouseListenerNew(jButton_delete) {
@Override
public void mouseClicked(MouseEvent e) {
while (jTable_book.isEditing()) {//注意这个操作,当在编辑行时,如果没有保存数据,点击button,没有下面的代码二无法实现更新
jTable_book.getCellEditor(jTable_book.getEditingRow(), jTable_book.getEditingColumn()).stopCellEditing();
}
int numLength = jTable_book.getSelectedRows().length;//获取选择的行数
if (numLength > 0) {
int value = JOptionPane.showConfirmDialog(jFrame, "真的要删除选择图书?",
"提示", JOptionPane.YES_NO_OPTION);
if (value == JOptionPane.YES_OPTION) {
DAOBooks daoBooks = new DAOBooks();
for (int i = 0; i < numLength; i++) {
int row = jTable_book.getSelectedRow();//因为一行一行的删除会改变行索引,导致我们无法
//通过直接导入选择的所有行进行 一行行删除实现
int id = Integer.parseInt(jTable_book.getValueAt(row, 0).toString());
daoBooks.deleteBooks(id);
model.removeRow(row);//移除行后行号变了,所以对应的res[i]又变了。
}
}
} else JOptionPane.showMessageDialog(jFrame, "你还没有选择要删除的行数据!", "警告", JOptionPane.ERROR_MESSAGE);
}
});
全部删除;
while(model.getRowCount()>0){//批量删除
model.removeRow(0);//一行一行的删除
}
表格的排序和过滤:
TableModel没有移除行model.removeRow(0);//一行一行的删除
所以我们要将TableModel定义为DefaultTableModel
TableModel model = new DefaultTableModel(rows, columns);
JTable table = new JTable(model);
RowSorter sorter = new TableRowSorter(model);
table.setRowSorter(sorter);
先添加菜单栏,再添加菜单,必须设置名字才能看见。。。
然后在对应的菜单添加多个菜单选项或者子菜单
JMenuBar jMenuBar = new JMenuBar();//创建菜单栏
jMenuBar.setForeground(Color.BLUE);
//第一个菜单——用户管理(用户列表,修改用户、删除用户、禁止用户借书、生成用户罚单)
JMenu jMenu = new JMenu("用户管理");
//第二个菜单——图书管理(更新图书信息、删除图书(设置余量为0)、 导入图书,批量导入图书)
JMenu jMenu1 = new JMenu("图书管理");
//第三个菜单——系统管理(修改用户名和密码,数据库系统重启,数据库系统退出,数据库备份,数据库日志记录,暂停用户登陆、清空数据库)
JMenu jMenu2 = new JMenu("系统管理");
//第四个菜单——
JMenu jMenu3 = new JMenu("退出管理");
//设置菜单1
userItem1 = new JMenuItem("用户列表");
userItem2 = new JMenuItem("修改用户");
userItem3 = new JMenuItem("删除用户");
userItem4 = new JMenuItem("禁止用户借书");
userItem5 = new JMenuItem("生成用户罚单");
jMenu.add(userItem1);
jMenu.add(userItem2);
jMenu.add(userItem3);
jMenu.add(userItem4);
jMenu.add(userItem5);
利用了传入参数的办法,判断是否null等、或者按照需要将对应的窗体设置为:
mainJFrame.setDefaultCloseOperation(jFrame.EXIT_ON_CLOSE);//退出所有界面,相当于退出虚拟机
//DISPOSE_ON_CLOSE退出当前窗体
public void setDefaultCloseOperation(int operation) {
if (operation != DO_NOTHING_ON_CLOSE &&
operation != HIDE_ON_CLOSE &&
operation != DISPOSE_ON_CLOSE &&
operation != EXIT_ON_CLOSE) {
throw new IllegalArgumentException("defaultCloseOperation must be"
+ " one of: DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE,"
+ " DISPOSE_ON_CLOSE, or EXIT_ON_CLOSE");
}
一个很实在的讲解:
运用 BoxLayout 进行 Swing 控件布局
项目做了6天,也是自己的第一个项目,完全独立的做,遇到了很多的难题,但是解决了以后现在我想不起啦 。。。总的来说还行。
贴上总代码行数:
最大的问题:怎么样让代码,不那么冗杂!!!DAO三层架构设计不知道我的到底算不算是
github:
https://github.com/linj21a/BooksMangementSystem
结后语
说明文档什么的就不会写了。。。。博客匆忙总结一把了。还有好多东西要学,大家加油!不懂就要问,我也不懂。大家开始吐槽吧!!!