通过JDBC实现一个航班系统
一、任务要求
1、创建数据库表 airinfo,添加测试数据不少于 4 条。要求主键自增
2、创建实体类 AirInfo,根据业务提供需要的构造方法和setter和getter方法。
package bean;
public class AirInfo {
private int no;
private String airId;
private String destination;
private String flyDate;
@Override
public String toString() {
return no + "\t\t" + airId +"\t\t\t"+ destination +" \t\t"+ flyDate;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getAirId() {
return airId;
}
public void setAirId(String airId) {
this.airId = airId;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getFlyDate() {
return flyDate;
}
public void setFlyDate(String flyDarete) {
this.flyDate = flyDarete;
}
}
3、创建 BaseDao 类,实现数据库连接和关闭功能。
(1)配置.properties文件
将数据库连接需要重复用到的信息配置为属性文件,命名为db.properties。如果后续要更改数据库或者链接地址更换的话,直接在这个文件里该数据就行,十分方便。
driver = com.mysql.cj.jdbc.Driver
uname = root
upass = 123456
url = jdbc:mysql://localhost:3306/yhp?serverTimezone=UTC
(2)建立Druid连接池,获取配置文件,实现数据库的连接
package util;
import bean.AirInfo;
import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
public class BaseDao {
private Connection connection = null;
private PreparedStatement pps = null;
private ResultSet result = null;
private int count = 0;//接收受影响行数
private static String userName;
private static String passWord;
private static String url;//连接数据库的地址
private static String driverName;
//引入Druid(德鲁伊)连接池
private static DruidDataSource dds = new DruidDataSource();
//初始化
static {
// 1.加载属性文件
ResourceBundle bundle = ResourceBundle.getBundle("db");
driverName = bundle.getString("driver");
url = bundle.getString("url");
userName = bundle.getString("uname");
passWord = bundle.getString("upass");
// 2.设置参数
dds.setUsername(userName);
dds.setPassword(passWord);
dds.setUrl(url);
dds.setDriverClassName(driverName);
}
/**
* 建立连接,设为protected是为了方便让子类继承
*
* @return
*/
protected Connection getConnection() {
try {
connection = dds.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;
}
/**
* 根据传入的sql语句建立预状态通道pps
*
* @param sql 传入的sql语句
* @param list 存放填充占位符的数据
* @return
*/
protected PreparedStatement getPps(String sql, List list) {
try {
pps = getConnection().prepareStatement(sql);
if (list != null && list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
pps.setObject(i + 1, list.get(i));
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return pps;
}
/**
* 启用pps,实现增删改的功能
*
* @param sql
* @param list
* @return
*/
protected int update(String sql, List list) {
pps = getPps(sql, list);
try {
count = pps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return count;
}
/**
* 启用pps,实现查询的功能
*
* @param sql
* @param list
* @return
*/
protected ResultSet query(String sql, List list) {
pps = getPps(sql, list);
try {
result = pps.executeQuery();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return result;
}
/**
* 执行查询结果集的处理
*
* @param sql
* @param list
* @return
*/
public List<AirInfo> dealSelect(String sql, List list) {
List<AirInfo> airs = new ArrayList<>();//用于存放查询出的信息表
try {
ResultSet result = query(sql, list);//若没有占位符,则直接传null就可以
while (result.next()) {
//为当前对象的各属性赋值
AirInfo air = new AirInfo();
air.setNo(result.getInt("no"));
air.setAirId(result.getString("airId"));
air.setDestination(result.getString("destination"));
air.setFlyDate(result.getString("flyDate"));
//将赋值完毕的对象存入airs集合中
airs.add(air);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
closeAll();
}
return airs;
}
protected void closeAll() {
try {
if (connection != null) {
connection.close();
}
if (pps != null) {
pps.close();
}
if (result != null) {
result.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
4、创建 Dao 接口 AirInfoDao,定义查询所有航班,按日期和目的地查询航班,删除航班,更新航班的方法。
package dao;
import bean.AirInfo;
import java.util.List;
public interface AirInfoDao {
// 1.列出所有航班,
public List<AirInfo> getAll();
// 2.按起飞时间查询,
public List<AirInfo> getByDate(String date);
// 3.按目的地查询,
public List<AirInfo> getByDes(String des);
// 4.删除航班(根据编号),
public int delete(int no);
// 5.更新航班,
public int updateByNo(AirInfo air);
// // 6.离开系统
// public void away();
public List<AirInfo> getByNo(int no);
}
5、创建DAO实现类AirInfoDaoImpl,继承BaseDao类,实现AirInfoDao接口,使用 JDBC 完成相应数据库操作。
package dao.impl;
import bean.AirInfo;
import dao.AirInfoDao;
import util.BaseDao;
import java.util.ArrayList;
import java.util.List;
public class AirInfoDaoImpl extends BaseDao implements AirInfoDao {
//定义变量
private String sql = null;//用于传达各种指令
private List list = new ArrayList();//用于存放填充占位符的数据
int count;//记录受改变的行数
/**
* 1.列出所有航班信息,
*
* @return
*/
@Override
public List<AirInfo> getAll() {
sql = "select * from airInfo";//查询语句(所有航班)
return dealSelect(sql, list);//处理查询,返回结果集
}
/**
* 2.按起飞时间查询,
*
* @param date
* @return
*/
@Override
public List<AirInfo> getByDate(String date) {
sql = "select * from airInfo where flyDate = ?";//查询语句(同一起飞日期)
list.add(date);//传入等待检查的日期,准备填充占位符
return dealSelect(sql, list);//处理查询,返回结果集
}
/**
* 3.按目的地查询,
*
* @param des
* @return
*/
@Override
public List<AirInfo> getByDes(String des) {
sql = "select * from airInfo where destination = ?";//查询语句(同一目的地)
list.add(des);//传入等待检查的日期,准备填充占位符
return dealSelect(sql, list);//处理查询,返回结果集
}
//4.删除航班(根据编号),
@Override
public int delete(int no) {
sql = "delete from airInfo where no = ?";
list.add(no);
count = update(sql, list);
return count;
}
@Override
public int updateByNo(AirInfo air) {
sql = "update airInfo set airId = ?,destination = ?,flyDate = ? where no = ?";
list.add(air.getAirId());
list.add(air.getDestination());
list.add(air.getFlyDate());
list.add(air.getNo());
count = update(sql,list);
return count;
}
// @Override
// public void away() {
//
// }
@Override
public List<AirInfo> getByNo(int no) {
sql = "select * from airInfo where no = ?";
list.add(no);
return dealSelect(sql, list);
}
}
6、创建 Main 类,完成在控制台显示留言信息和用户添加留言操作,启动
(1)创建菜单类Menu,实现从控制台到Dao的交流
package test;
import bean.AirInfo;
import dao.AirInfoDao;
import dao.impl.AirInfoDaoImpl;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Menu {
//定义变量
private AirInfoDao dao = new AirInfoDaoImpl();//用于执行具体的数据库操作
private List<AirInfo> airs = new ArrayList<>();//用于接收和返回的信息表
private AirInfo air = new AirInfo();//用于迭代的单个信息
private Scanner input = new Scanner(System.in);//用于接收用户的输入
/**
* 构造器,用于接收用户传入的指令并进行跳转或提示
*
* @param in
*/
public Menu(int in) {
switch (in) {
case 1:
case_1();
break;
case 2:
case_2();
break;
case 3:
case_3();
break;
case 4:
case_4();
break;
case 5:
case_5();
break;
case 6:
break;
}
}
// 1.列出所有航班,
public void case_1() {
System.out.println("------------------所有航班信息如下------------------");
airs = dao.getAll();
printAir();
}
// 2.按起飞时间查询,
public void case_2() {
System.out.println("请输入您要查询的起飞时间(如:2021-3-10):");
String date = input.next();//用户输入日期
airs = dao.getByDate(date);//根据日期查询所有符合条件的航班
System.out.println("----------于" + date + "起飞的航班信息如下--------------");
printAir();//将查询到的航班信息打印出来
}
// 3.按目的地查询,
public void case_3() {
System.out.println("请输入您要查询的目的地(如:成都):");
String des = input.next();//用户输入目的地
airs = dao.getByDes(des);//根据目的地查询所有符合条件的航班
System.out.println("----------飞往" + des + "的航班信息如下--------------");
printAir();
}
// 4.删除航班(根据编号),
public void case_4() {
System.out.println("请输入您要删除的航班编号:");
int no = input.nextInt();
airs = dao.getByNo(no);
System.out.println("----------编号为" + no + "的航班信息如下--------------");
printAir();
System.out.println("是否确定删除(输入1或0):");
if (input.nextInt() == 1) {
int isDel = dao.delete(no);
if (isDel > 0) {
System.out.println("删除成功");
} else {
System.out.println("删除失败,请检查!");
}
}
}
// 5.更新航班,
public void case_5() {
//接收用户输入的新信息
System.out.println("请输入需要更新的航班编号:");
air.setNo(input.nextInt());
System.out.println("请输入新的航班号");
air.setAirId(input.next());
System.out.println("请输入新的目的地");
air.setDestination(input.next());
System.out.println("请输入新的起飞日期");
air.setFlyDate(input.next());
//用新信息覆盖旧信息
int i = dao.updateByNo(air);
if (i > 0) {
System.out.println("更新成功");
} else {
System.out.println("更新失败");
}
}
// // 6.离开系统
// public void away() {
//
//
// }
/**
* 打印航班信息
*/
public void printAir() {
if (airs != null && airs.size() > 0) {
System.out.println("编号\t航班号\t\t目的地\t\t起飞日期");
for (int i = 0; i < airs.size(); i++) {
air = airs.get(i);
System.out.println(air);
}
}
}
}
(2)创建Main类,实现与Menu的互动
package test;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println("--------------欢迎使用航班信息管理系统--------------");
while (true) {
System.out.println("请选择操作(1.列出所有航班,2.按起飞时间查询,3.按目的地查询,4.删除航班,5.更新航班,6离开系统):");
Scanner input = new Scanner(System.in);
int in = input.nextInt();
new Menu(in);
}
}
}
二、运行结果示例
三、改进与反思
1、问题
在进行其他选项的测试时都达到了目的,但是在进行删除操作时系统报了个越界的错
出错的代码在这个位置(下图打断点的那一行)
一开始我觉得问题是不是出在参数设置上了,可是我就是专门写成i+1的呀怎么可能会出错?后来我试了好多遍就是不对,回去看了教材和视频也证明这行代码确实没毛病。那么问题出在了哪里?
于是我断点调试了好久才发现在进入delete函数之后,list的元素竟然多了一个!这时我才恍然大悟:问题出在下面打出断点的位置!
查询航班信息的时候我们调用了getByNo方法已经往list里面填了一个编号了,在delete里面只要再把list拿出来用一次就行了,不用再次添加了。
2、反思
(1)所以这个问题其实出在二次赋值,导致list中有两个重复的no,但是需要填充的占位符只有一个,才会出现所谓的越界提示。
(2)经过测试,原先错误的代码也可以将目的航班删除,但这种写法太不严谨,远达不到可交付的级别。
(3)不能机械地认为每一个函数都应该传参,都应该按同一套流程走,如果是这样又何必写那么多不一样的函数呢,直接抽象出一个新的函数,要用的时候直接调不是更省事吗?写代码要动脑子!!!
3、代码对比
最后贴一下改正前后的对比吧(这些代码原来的所在已经标注清楚,错误的地方已通过注释说明)
//接口AirInfoDao中第15行的改动:将参数删去
public int delete();
//实现类AirInfoDaoImpl中第55、57行的改动:删去参数,删去第二次在list中插入元素的语句
@Override
public int delete() {//这里的参数用不上,也不用传了
sql = "delete from airInfo where no = ?";
//因为在删除只前已经查询过一遍了(list里面已经有值了),所以无须二次赋值,直接传就可以
//list.add(no);
count = update(sql, list);
return count;
}
// 菜单类Menu中第79行的改动:调用delete不再传参
public void case_4() {
System.out.println("请输入您要删除的航班编号:");
int no = input.nextInt();
airs = dao.getByNo(no);//这个地方list已经赋值了,通过list就能查到对应的航班
System.out.println("----------编号为" + no + "的航班信息如下--------------");
printAir();
System.out.println("是否确定删除(输入1或0):");
if (input.nextInt() == 1) {
int isDel = dao.delete();//这个地方直接借用上面查到的数据就行,不用再传参数
if (isDel > 0) {
System.out.println("删除成功");
} else {
System.out.println("删除失败,请检查!");
}
}
}