420-管家婆项目详细介绍
管家婆个人财务管理系统 - 项目技术文档
目录
1. 项目概述
1.1 项目简介
管家婆个人财务管理系统是一个基于Java Swing开发的桌面应用程序,主要用于帮助用户管理个人或家庭的收支情况。系统提供了直观的图形界面,用户可以方便地记录收入和支出,查看财务统计,实现个人财务的数字化管理。
1.2 项目功能
- 收支记录管理:记录每笔收入和支出,包括金额、分类、账户、时间等信息
- 分类管理:自定义收入和支出的分类,如工资、奖金、餐饮、交通等
- 数据查询:按时间段、收支类型、分类等条件组合查询财务记录
- 统计分析:自动计算总收入、总支出,支持图表展示
- 数据持久化:所有数据保存在MySQL数据库中,确保数据安全
1.3 使用场景
- 个人日常收支记录
- 家庭财务管理
- 学生生活费管理
- 小型工作室财务记录
2. 技术架构说明
2.1 技术栈
- 编程语言:Java 8
- 用户界面:Java Swing(Java自带的桌面应用开发框架)
- 数据库:MySQL 8.0
- 架构模式:MVC(Model-View-Controller,模型-视图-控制器)
- 数据库操作:Apache Commons DBUtils(简化数据库操作的工具库)
- 图表组件:JFreeChart(用于生成图表的Java库)
2.2 MVC架构解释
MVC是一种软件设计模式,把程序分成三个部分:
-
Model(模型):负责数据处理和业务逻辑
domain
包:定义数据结构(如账务记录、分类信息)dao
包:负责数据库操作(增删改查)services
包:处理业务逻辑
-
View(视图):负责用户界面显示
view
包:定义所有的窗口和对话框
-
Controller(控制器):连接模型和视图,处理用户操作
controller
包:处理用户点击按钮、选择菜单等操作
为什么使用MVC?
- 分工明确:每个部分职责清楚,便于团队开发
- 代码复用:同一个模型可以对应多个视图
- 易于维护:修改界面不影响业务逻辑,修改业务逻辑不影响界面
3. 项目结构详解
3.1 整体目录结构
gjp-hl/
├── src/com/itheima/gjp/ # 源代码根目录
│ ├── app/ # 应用程序入口
│ │ ├── MainApp.java # 主程序启动类
│ │ └── TestDB.java # 数据库连接测试类
│ ├── controller/ # 控制器层(MVC中的C)
│ │ ├── MainFrameContrller.java # 主窗口控制器
│ │ ├── LedgerMngController.java # 账务管理控制器
│ │ ├── AddLedgerController.java # 添加账务控制器
│ │ ├── EditLedgerController.java # 编辑账务控制器
│ │ ├── SortMngController.java # 分类管理控制器
│ │ └── ... # 其他控制器
│ ├── dao/ # 数据访问层
│ │ ├── LedgerDao.java # 账务数据访问对象
│ │ └── SortDao.java # 分类数据访问对象
│ ├── domain/ # 实体类(MVC中的M)
│ │ ├── Ledger.java # 账务实体类
│ │ ├── Sort.java # 分类实体类
│ │ └── QueryForm.java # 查询条件实体类
│ ├── services/ # 业务逻辑层(MVC中的M)
│ │ ├── LedgerServices.java # 账务业务逻辑
│ │ └── SortService.java # 分类业务逻辑
│ ├── tools/ # 工具类
│ │ ├── JDBCUtils.java # 数据库连接工具
│ │ ├── DateUtils.java # 日期处理工具
│ │ └── ... # 其他工具类
│ └── view/ # 视图层(MVC中的V)
│ ├── AbstractMainFrame.java # 主窗口抽象类
│ ├── AbstractLedgerMngDialog.java # 账务管理对话框抽象类
│ └── ... # 其他视图类
├── lib/ # 第三方依赖库
│ ├── mysql-connector-java-8.0.28.jar # MySQL数据库驱动
│ ├── commons-dbutils-1.4.jar # 数据库操作工具
│ ├── jfreechart-1.0.9.jar # 图表生成库
│ └── ... # 其他依赖库
├── bin/ # 编译后的字节码文件
└── 配置文件...
3.2 核心包说明
app包 - 应用入口
- 包含程序的main方法,是程序启动的地方
domain包 - 实体类
- 定义系统中的核心数据结构
- 相当于现实世界中的"对象",如一笔账务、一个分类
dao包 - 数据访问对象
- DAO = Data Access Object(数据访问对象)
- 专门负责与数据库交互,执行增删改查操作
- 把复杂的SQL语句封装成简单的Java方法
services包 - 业务逻辑
- 处理具体的业务规则和逻辑
- 在controller和dao之间起桥梁作用
controller包 - 控制器
- 处理用户的操作(点击按钮、选择菜单等)
- 调用services层处理业务,然后更新界面
view包 - 用户界面
- 定义所有的窗口、对话框、按钮等界面元素
- 用户看到和操作的都是这一层的内容
4. 核心功能模块
4.1 账务管理模块
功能描述: 管理所有的收入和支出记录
主要文件:
Ledger.java
:账务实体类,定义账务记录的数据结构LedgerDao.java
:账务数据访问类,负责账务数据的数据库操作LedgerServices.java
:账务业务逻辑类LedgerMngController.java
:账务管理控制器
核心功能:
- 添加账务记录
- 编辑账务记录
- 删除账务记录
- 查询账务记录
- 统计收支总额
4.2 分类管理模块
功能描述: 管理收入和支出的分类
主要文件:
Sort.java
:分类实体类SortDao.java
:分类数据访问类SortService.java
:分类业务逻辑类SortMngController.java
:分类管理控制器
核心功能:
- 添加分类
- 编辑分类
- 删除分类
- 查看所有分类
4.3 数据库连接模块
功能描述: 提供数据库连接和操作功能
主要文件:
JDBCUtils.java
:数据库连接工具类- 使用连接池技术提高数据库访问效率
4.4 用户界面模块
功能描述: 提供友好的图形用户界面
主要特点:
- 主窗口:显示系统主菜单
- 账务管理对话框:管理收支记录
- 分类管理对话框:管理分类信息
- 添加/编辑对话框:录入和修改数据
5. 程序运行逻辑
5.1 程序启动流程
1. 用户双击程序或运行命令
↓
2. 执行MainApp.java的main方法
↓
3. 创建MainFrameContrller对象(主窗口控制器)
↓
4. MainFrameContrller继承AbstractMainFrame(主窗口界面)
↓
5. 显示主窗口,等待用户操作
5.2 用户操作流程
以添加账务记录为例:
1. 用户点击"账务管理"按钮
↓
2. MainFrameContrller.ledgerMng()方法被调用
↓
3. 创建LedgerMngController对象,显示账务管理对话框
↓
4. 用户点击"添加"按钮
↓
5. LedgerMngController.addLedger()方法被调用
↓
6. 创建AddLedgerController对象,显示添加账务对话框
↓
7. 用户填写信息并点击"确定"
↓
8. AddLedgerController.confirm()方法被调用
↓
9. 数据验证 → 创建Ledger对象 → 调用LedgerServices.addLedger()
↓
10. LedgerServices调用LedgerDao.addLedger()
↓
11. LedgerDao执行SQL语句,将数据保存到数据库
↓
12. 提示用户"添加成功",关闭对话框
5.3 数据流转过程
用户界面(View) → 控制器(Controller) → 业务逻辑(Services) → 数据访问(Dao) → 数据库
↑ ↓
←←←←←←←←←←←←←←← 返回结果 ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
5.4 UI界面
6. 数据库设计
6.1 数据库结构
数据库名: gjp
主要数据表:
-
gjp_sort(分类表)
sid
:分类ID(主键,自动递增)sname
:分类名称(如:工资、餐饮)parent
:父级分类(收入 或 支出)sdesc
:分类描述
-
gjp_ledger(账务表)
lid
:账务ID(主键,自动递增)parent
:收支类型(收入 或 支出)money
:金额sid
:分类ID(外键,关联gjp_sort表)account
:账户名称createtime
:创建时间ldesc
:账务描述
6.2 表关系说明
gjp_sort (分类表) 1 → ∞ gjp_ledger (账务表)
↑ ↓
一个分类 多笔账务记录
可以对应 每笔记录都属于
多笔账务记录 一个特定分类
外键关系: gjp_ledger.sid → gjp_sort.sid
- 这意味着每笔账务记录都必须属于一个已存在的分类
- 删除分类前必须先删除该分类下的所有账务记录
7. 关键代码解析
7.1 程序入口代码
文件: src/com/itheima/gjp/app/MainApp.java
package com.itheima.gjp.app;
import com.itheima.gjp.controller.MainFrameContrller;
/*
* 整个项目的起始入口
*/
public class MainApp {
public static void main(String[] args) {
// 开启主窗体,创建他的子类对象
new MainFrameContrller().setVisible(true);
}
}
代码解释:
- 这是程序的入口点,当用户运行程序时首先执行这里
- 创建主窗口控制器对象,并设置为可见
setVisible(true)
让窗口显示出来
7.2 数据库连接工具类
文件: src/com/itheima/gjp/tools/JDBCUtils.java
package com.itheima.gjp.tools;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class JDBCUtils {
// 数据库连接配置
public static final String DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/gjp?useSSL=false&serverTimezone=UTC";
public static final String USERNAME = "root";
public static final String PASSWORD = "123456";
// 连接池配置参数
private static final int MAX_IDLE = 3; // 最大空闲连接数
private static final long MAX_WAIT = 5000; // 最大等待时间
private static final int MAX_ACTIVE = 5; // 最大活跃连接数
private static final int INITIAL_SIZE = 10; // 初始连接数
// 创建数据源(连接池)
private static BasicDataSource dataSource = new BasicDataSource();
// 静态代码块,程序启动时自动执行,配置连接池
static {
dataSource.setDriverClassName(DRIVER_CLASS_NAME);
dataSource.setUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaxActive(MAX_IDLE);
dataSource.setMaxWait(MAX_WAIT);
dataSource.setMaxActive(MAX_ACTIVE);
dataSource.setInitialSize(INITIAL_SIZE);
}
// 获取数据源的方法,其他类调用这个方法来获取数据库连接
public static DataSource getDataSource() {
return dataSource;
}
}
关键概念解释:
-
数据库连接池:预先创建一些数据库连接保存在"池"中,需要时取出使用,用完归还。这样比每次都创建新连接要快很多。
-
静态代码块:用
static {}
包围的代码,在类被加载时自动执行一次,常用于初始化操作。
7.3 实体类代码
文件: src/com/itheima/gjp/domain/Ledger.java
package com.itheima.gjp.domain;
public class Ledger {
private int lid; // 账务ID
private String parent; // 收支类型(收入/支出)
private double money; // 金额
private int sid; // 分类ID
private String account; // 账户
private String createtime; // 创建时间
private String ldesc; // 描述
private String sname; // 分类名称(从分类表关联查询得到)
// 无参构造方法
public Ledger() {}
// 有参构造方法
public Ledger(int lid, String parent, double money, int sid,
String account, String createtime, String ldesc, String sname) {
this.lid = lid;
this.parent = parent;
this.money = money;
this.sid = sid;
this.account = account;
this.createtime = createtime;
this.ldesc = ldesc;
this.sname = sname;
}
// Getter和Setter方法(用于获取和设置属性值)
public int getLid() { return lid; }
public void setLid(int lid) { this.lid = lid; }
public String getParent() { return parent; }
public void setParent(String parent) { this.parent = parent; }
public double getMoney() { return money; }
public void setMoney(double money) { this.money = money; }
// ... 其他getter和setter方法
// toString方法,用于打印对象信息(调试时很有用)
@Override
public String toString() {
return "Ledger [lid=" + lid + ", parent=" + parent + ", money=" + money
+ ", sid=" + sid + ", account=" + account + ", createtime="
+ createtime + ", ldesc=" + ldesc + ", sname=" + sname + "]";
}
}
代码解释:
- 实体类:对应数据库表的Java类,一个对象代表表中的一行记录
- 构造方法:用于创建对象的方法
- Getter/Setter方法:Java的标准做法,用于访问和修改私有属性
- toString方法:返回对象的字符串表示,便于调试
7.4 数据访问类代码
文件: src/com/itheima/gjp/dao/LedgerDao.java
(部分代码)
package com.itheima.gjp.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import com.itheima.gjp.domain.Ledger;
import com.itheima.gjp.tools.JDBCUtils;
public class LedgerDao {
// 创建QueryRunner对象,用于执行SQL语句
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
/*
* 添加账务记录的方法
* 参数:Ledger对象,包含要添加的账务信息
*/
public void addLedger(Ledger ledger) {
try {
// 准备SQL插入语句
String sql = "INSERT INTO gjp_ledger (parent,money,sid,account,createtime,ldesc)" +
"values(?,?,?,?,?,?)";
// 准备参数数组,对应SQL语句中的?号占位符
Object[] params = {
ledger.getParent(), // 收支类型
ledger.getMoney(), // 金额
ledger.getSid(), // 分类ID
ledger.getAccount(), // 账户
ledger.getCreatetime(), // 时间
ledger.getLdesc() // 描述
};
// 执行SQL语句
qr.update(sql, params);
} catch(SQLException e) {
// 如果出现SQL异常,转换为运行时异常抛出
throw new RuntimeException(e);
}
}
/*
* 查询账务记录的方法
* 参数:QueryForm对象,包含查询条件
* 返回:List<Ledger>集合,包含查询结果
*/
public List<Ledger> queryLedgerByQueryForm(QueryForm form) {
// 动态构建SQL语句
List<String> params = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
// 基本查询条件(时间范围)
builder.append("SELECT * FROM gjp_ledger WHERE createtime between ? and ?");
params.add(form.getBegin()); // 开始时间
params.add(form.getEnd()); // 结束时间
// 根据用户选择的收支类型添加条件
if(form.getParent().equals("收入") || form.getParent().equals("支出")) {
builder.append(" and parent = ?");
params.add(form.getParent());
}
// 根据用户选择的分类添加条件
if(!form.getSname().equals("-请选择-")) {
// 根据分类名称查找分类ID
int sid = sortDao.getSidBySname(form.getSname());
builder.append(" and sid=?");
params.add(sid + "");
}
try {
// 执行查询,BeanListHandler会自动将结果集转换为Ledger对象列表
List<Ledger> list = qr.query(builder.toString(),
new BeanListHandler<Ledger>(Ledger.class),
params.toArray());
return list;
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
}
关键概念解释:
- QueryRunner:Apache Commons DBUtils提供的工具类,简化了数据库操作
- 占位符(?):SQL语句中的?代表参数,可以防止SQL注入攻击
- BeanListHandler:自动将数据库查询结果转换为Java对象列表
- 动态SQL:根据不同条件拼接不同的SQL语句
7.5 控制器代码
文件: src/com/itheima/gjp/controller/AddLedgerController.java
(部分代码)
package com.itheima.gjp.controller;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import com.itheima.gjp.domain.Ledger;
import com.itheima.gjp.services.LedgerServices;
import com.itheima.gjp.view.AbstractOperationLedgerDialog;
public class AddLedgerController extends AbstractOperationLedgerDialog {
private LedgerServices ledgerService = new LedgerServices();
public AddLedgerController(JDialog dialog) {
super(dialog);
titleLabel.setText("添加账务");
super.setTitle("添加账务");
}
/*
* 用户点击"确定"按钮时调用的方法
*/
@Override
public void confirm() {
// 1. 获取用户输入的数据
String parent = parentBox.getSelectedItem().toString(); // 收支类型
String sname = sortBox.getSelectedItem().toString(); // 分类名称
String account = accountTxt.getText().trim(); // 账户
String moneyStr = moneyTxt.getText().trim(); // 金额(字符串)
String createtime = createtimeTxt.getText(); // 时间
String ldesc = ldescTxt.getText(); // 描述
// 2. 数据验证
if(parent.equals("-请选择-")) {
JOptionPane.showMessageDialog(this, "请选择收支类型");
return;
}
if(sname.equals("-请选择-")) {
JOptionPane.showMessageDialog(this, "请选择分类");
return;
}
if(account == null || account.equals("")) {
JOptionPane.showMessageDialog(this, "请填写账户");
return;
}
// 验证金额是否为有效数字
double money = 0.0;
try {
money = Double.parseDouble(moneyStr);
if(money <= 0) {
JOptionPane.showMessageDialog(this, "金额必须大于0");
return;
}
} catch(NumberFormatException e) {
JOptionPane.showMessageDialog(this, "请输入有效的金额");
return;
}
// 3. 根据分类名称获取分类ID
int sid = sortService.getSidBySname(sname);
// 4. 创建Ledger对象,封装用户输入的数据
Ledger ledger = new Ledger(0, parent, money, sid, account, createtime, ldesc, sname);
// 5. 调用业务逻辑层,保存数据
ledgerService.addLedger(ledger);
// 6. 提示用户操作成功
JOptionPane.showMessageDialog(this, "添加账务成功", "操作成功", JOptionPane.PLAIN_MESSAGE);
// 7. 关闭当前对话框
this.dispose();
}
/*
* 收支类型改变时调用的方法(实现下拉框联动)
*/
@Override
public void changeParent() {
String parent = parentBox.getSelectedItem().toString();
if(parent.equals("-请选择-")) {
// 如果收支类型是"请选择",分类也显示"请选择"
sortBox.setModel(new DefaultComboBoxModel(new String[] {"-请选择-"}));
}
if(parent.equals("收入") || parent.equals("支出")) {
// 根据收支类型,从数据库查询对应的分类列表
List<Object> list = sortService.querySortNameByParent(parent);
list.add(0, "-请选择-"); // 在列表开头添加"请选择"选项
sortBox.setModel(new DefaultComboBoxModel(list.toArray()));
}
}
}
代码解释:
- 数据验证:检查用户输入是否合法,如必填项是否为空、金额是否为有效数字等
- 异常处理:使用try-catch处理可能出现的数字格式异常
- 下拉框联动:当收支类型改变时,分类下拉框的内容也相应改变
- 对话框:使用JOptionPane显示提示信息和错误信息
8. 项目特色与亮点
8.1 技术特色
-
标准MVC架构
- 代码结构清晰,各层职责明确
- 便于维护和扩展
- 符合企业级开发规范
-
数据库连接池技术
- 提高数据库访问效率
- 合理管理数据库连接资源
- 支持高并发访问
-
动态SQL构建
- 支持多条件组合查询
- 灵活的查询功能
- 安全的参数化查询,防止SQL注入
-
面向对象设计
- 使用继承和多态特性
- 抽象类和接口的合理运用
- 代码复用性强
8.2 功能特色
-
用户友好的界面
- 直观的图形界面
- 合理的布局设计
- 清晰的操作流程
-
灵活的分类管理
- 支持自定义收支分类
- 收入和支出分别管理
- 分类与账务记录的关联
-
强大的查询功能
- 时间段查询
- 收支类型筛选
- 分类筛选
- 多条件组合查询
-
实时统计功能
- 自动计算总收入和总支出
- 支持图表展示
- 数据可视化
8.3 代码质量
-
良好的注释
- 详细的方法注释
- 清晰的业务逻辑说明
- 便于理解和维护
-
异常处理
- 完善的错误处理机制
- 用户友好的错误提示
- 程序稳定性好
-
代码规范
- 统一的命名规范
- 合理的包结构
- 清晰的类设计
8.4 学习价值
-
Java基础知识应用
- 面向对象编程
- 集合框架使用
- 异常处理机制
- 文件I/O操作
-
数据库应用
- SQL语句编写
- JDBC编程
- 数据库设计
- 事务处理
-
GUI编程
- Swing组件使用
- 事件处理机制
- 布局管理
- 用户交互设计
-
软件工程实践
- MVC架构设计
- 分层开发
- 代码组织
- 项目管理
总结
管家婆个人财务管理系统是一个功能完整、结构清晰的Java桌面应用程序。它不仅实现了个人财务管理的基本功能,还展示了现代软件开发的最佳实践。通过学习这个项目,可以深入理解Java编程、数据库应用、GUI开发和软件架构设计等多个方面的知识。
项目的成功运行证明了其技术方案的可行性和代码实现的正确性。无论是作为学习Java编程的实践项目,还是作为个人财务管理的实用工具,都具有很好的价值和意义。