最近写了一个基于控制台的网上银行项目,总结了一下实现过程中遇到的一些小知识点,帮助记忆。
一、基础框架。
项目分为三个板块,客户版块,账号板块,日志板块,一个客户可以拥有多个账户,并对它们进行存取转账操作,在sql数据库中建了三个表:
1.customer客户表,主键cid,
2.account账号表,主键aid,外键acid,对应客户表中的aid
3.record日志表,主键rid,外键Raidfroom,对应账号表中的aid。
二、应用小技巧
1.customer表中有一个注册日期cregdate,该项可以在进行注册时获取当前时间,实现方法如下所示,同样要实现一个记录上次登录时间的功能,则可以写一个修改上次登录时间的方法,方法里面将获取当前的时间值填入该项,在退出该系统时调用这个方法,实现修改。
/ 注册日期
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
ps1.setString(3, sdf.format(new Date()));
**注意:**customer表中对cregdate数据类型的定义为 datetime,所对应的时间格式类似 2019-12-16 00:00:00 所以在定义日期时要按照这个格式来定义,即:YYYY-MM-dd HH:mm:ss 如果数据类型为date时则只用 YYYY-MM-dd 定义即可。
2.在完成一个sql语句的预编译与赋值之后,如果我们想获取运行结果的值,则我们可以调用 .executeQuery()方法对结果进行查询,使用结果 .getString(1//列编号)对一个新定义对象的对应属性进行赋值,注意在这个过程之前,我们要使用 .next()方法对结果进行判断是否有值,如果不进行判断就会出现这样的报错:
java.sql.SQLException: Before start of result set
3.在书写一个方法时,如果我们想对这个方法传入参数,则我们可以在写这个方法时在方法名后面的括号中定义传入参数的类型,即形参,后序在调用这个方法时就可以直接在方法名的后面按顺序写入具体的数据,即实参,如果这个方法实现了某个接口,则接口定义时也要定义对应的形参。
对上述三个知识点的一个注册账号的小实例:
// 注册账号
public int Register(String name,String psw){//形参
int result = 0;
//调用数据库连接
Connection conn = DBHelper.getConn();
//书写sql语句,查询cname这一列
String sql ="select cname from customer";
PreparedStatement ps = null;
try {
//预编译
ps = conn.prepareStatement(sql);
//获取结果
ResultSet rs = ps.executeQuery();
while (rs.next()) {//判断rs中是否有值,如果有则返回ture
//定义一个新的customer对象
CustomerInfo ci = new CustomerInfo();
//把rs中的第一列的值赋值给ci对象的cname属性中
ci.setCname(rs.getString(1));
// 判断表中是否已存在该客户
if (ci.getCname().equals(name)) {
System.out.println("你输入的客户名称已存在,请重新输入");
break;
} else {
//给customer表添加一条记录,因为cid项在数据库中设置为自增,所以可以写为null,数据库自动填充该值
String sql1 = "insert into customer values(null,?,?,?,?,?,?,?)";
Scanner input = new Scanner(System.in);
PreparedStatement ps1 = null;
ps1 = conn.prepareStatement(sql1);
ps1.setString(1, name);
ps1.setString(2, psw);
// 注册日期
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
ps1.setString(3, sdf.format(new Date()));
// 上次登录时间
ps1.setString(4, sdf.format(new Date()));
// 客户地址
System.out.println("请输入你的地址:");
String adress = input.next();
ps1.setString(5, adress);
// 电子邮箱
System.out.println("请输入你的电子邮件:");
ps1.setString(6, input.next());
// 电话号码
System.out.println("请输入你的电话号码:");
ps1.setString(7, input.next());
//.executeUpdate()查看修改的数据行数,即一行。
result = ps1.executeUpdate();
System.out.println("注册成功");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
}
4.我们在获取控制台输入的选项时可以使用 switch–case方法来进行接收,也可以使用if–else语句。一般我们会设置一个获取不存在的错误选项来处理异常情况,,比如switch–case方法中有defult,if–else中有else,要想实现重新输入功能则需要建立while或do while循环,实例如下:
System.out.println("请输入你的选择:");
Scanner input = new Scanner(System.in);
int choice = input.nextInt();
boolean con = true;
do {
if (choice==1) {
record.FindRecord(aid);
break;
}else if (choice==2){
MainMenu(cnm);
}else{
System.out.println("没有这个选项,请重试");
con = false;
break;
}
} while (!con);//循环条件
5.在实现转账功能时,如果转账出现异常,则需要将双方的钱数复原,这里就需要运用事务处理的知识,实例如下:
//转账
@Override
//转账双方的卡号
public int Transfer(int aid1, int aid2) {
int result = 0;
int result1 = 0;
//手续费
double charge = 0;
Scanner scanner = new Scanner(System.in);
AccountImpl accout = new AccountImpl();
System.out.println("请输入转账的金额:");
double money = scanner.nextDouble();
//查询客户等级
String level = accout.SelectLevel(aid1);
if (level.equals("一般用户")){
charge = money*0.002;
}
RecordImpl record = new RecordImpl();
//转账金额不能为负数
if (money > 0) {
Connection conn = DBHelper.getConn();
//直接在sql语句中对余额进行修改
String sql = "update account set abalance = abalance-? where aid=?";
try {
//关闭自动提交
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement(sql);
//赋值
ps.setDouble(1, (money+charge));
ps.setInt(2, aid1);
result = ps.executeUpdate();
String sql1 = "update account set abalance = abalance+? where aid=?";
PreparedStatement ps1 = conn.prepareStatement(sql1);
ps1.setDouble(1, money);
ps1.setInt(2, aid2);
result1 = ps1.executeUpdate();
//如果两个修改都成功
if (result > 0 && result1 > 0) {
//手动提交
conn.commit();//转账
System.out.println("转账成功");
System.out.println("收取手续费"+charge+"元");
} else {
System.out.println("转账失败");
//将之前修改的数据复原
conn.rollback();//回滚
}
} catch (SQLException e) {
e.printStackTrace();
}
}else {
System.out.println("转账金额不能为负数");
}
return result;
}
6.在查看日志时,想要打印日志表中所有与对应卡号相等的日志记录,一般思路是将获取到的账号直接赋值给Raidfroom,然后打印出得到的结果,这是一个取巧的办法,所以告诉你一个正规一点的方法,即右连接,如下所示:
我们将获得所有与账号表存在主外键关系的日志表数据。
@Override
// 客户根据账号查看对应账号交易记录
public RecordInfo FindRecord(int aid) {
RecordInfo ri =null;
Connection conn = DBHelper.getConn();
// join右边打印所有数据,左边显示与之为主外键关系的行的所有值,如果右表的某行在左表中没有匹配的值,则左表数据显示为空。
String sql = "select *from record r right join account a on r.Raidfroom=a.aid";
try {
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
ri = new RecordInfo();
// 循环输出
while (rs.next()){
ri.setRaidfroom(rs.getInt(3));
//如果该数据属于这个卡号
if (ri.getRaidfroom()==aid) {
ri.setRid(rs.getInt(1));
ri.setRtransdate(rs.getString(2));
ri.setRaidfroom(rs.getInt(3));
ri.setRaidto(rs.getInt(4));
ri.setRtranstype(rs.getInt(5));
ri.setRtranssummary(rs.getString(6));
ri.setRabalance(rs.getDouble(7));
System.out.println("记录编号\t\t"+"生成时间\t\t"+"操作账号\t\t"+"对应账号\t"+"交易类型(1.存款 2.取款 3.转账)\t"+"摘要\t\t"+"余额");
System.out.println(ri.getRid()+"\t\t\t"+ri.getRtransdate()+"\t"+ri.getRaidfroom()+"\t\t\t\t"+ri.getRaidto()+"\t\t\t\t"+ri.getRtranstype()+"\t\t\t\t\t\t\t"+ri.getRtranssummary()+"\t\t"+ri.getRabalance());
System.out.println("----------------------------------------------------------------------------------------------------------------------------------------------------");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return ri;
}