图书管理系统总结——数据库操纵(三):数据库安全性
最后一次记录,说一说数据库的安全性。
一、一致性控制
为了保证事务的一致性,可以采用显示事务语句以及在数据库中添加完整性约束。比如在还书时候,要同时修改用户的用户表中的剩余借书本数,借阅记录中的已还选项以及图书表中剩余本数。这个时候可以有两个策略:
1、采用触发器,在MySQL中定义触发器:(仅举个例子)
DELIMITER $$ CREATE /*[DEFINER = { user | CURRENT_USER }]*/ TRIGGER `db_book`.`Borrow` AFTER INSERT ON `db_book`.`t_borrow` FOR EACH ROW BEGIN UPDATE t_user SET borroNumRem=borrowNumRem-1 WHERE id=(SELECT userID FROM inserted);--修改用户表 UPDATE t_book SET numer=number-1 WHERE id=(SELECT bookId FROM inserted);--修改图书表 END$$ DELIMITER ;
2、在应用软件中做三个表修改
但是这样感觉出错了的话也不太好知道,最终采用在应用软件中分别对三个表进行操作。这样计算定期未还扣钱事件好操作一点,在应用程序里改参数还是比较习惯的。
二、并发控制
首先关于一个用户不能两次登录。如果是JAVA EE可以用web端的session:http://www.cnblogs.com/loveweiwei/p/4139668.html
但是这个由于不是网络,我也不知道怎么办,由于时间关系没空细致调研,只能用最低劣的方法,在user表中加一个属性表示是否登录。然后登录的函数中加一个行级锁,保证一个人抢先登录时候后面的人登录不进去这是就用到之前《数据库操纵(一)》中所定义的有关事务的函数:
/** * 登录验证 * @param con * @param user * @return * @throws Exception */ public User login(Connection con,User user)throws Exception { User resultUser=null; DbUtil dbUtil=new DbUtil(); PreparedStatement pstmt = null; ResultSet rs = null; String sql="select * from t_user where id=? and password=? and IsLogin=? for update";//加悲观锁,不允许同时登陆 try { DbUtil.beginTransaction(con); //开始事务 pstmt=con.prepareStatement(sql); //对问号设置 pstmt.setString(1, user.getId()); pstmt.setString(2, user.getPassword()); pstmt.setByte(3, (byte)0); rs=pstmt.executeQuery(); if(rs.next()) {//如果查到了,则实例化 resultUser=new User(); resultUser.setId(rs.getString("id"));//getInt("id") resultUser.setUserName(rs.getString("userName")); resultUser.setPassword(rs.getString("password")); resultUser.setBorrowNumRem(rs.getInt("borrowNumRem")); resultUser.setBalance(rs.getFloat("balance")); resultUser.setIsLogin((byte)1);//置成已登陆 int num=modifyIsLoginField(con,user.getId(),(byte)1); //提交事务 DbUtil.commitTransaction(con); } }catch(Exception e){ e.printStackTrace(); //回滚事务 DbUtil.rollbackTransaction (con); throw new RuntimeException(); }finally{ DbUtil.close(rs); DbUtil.close(pstmt); DbUtil.resetTransaction(con); dbUtil.closeCon(con); } return resultUser; }
同样,在缴纳用户费用时候也用了封锁机制。
/** * 用户缴纳罚款 * @param tableName * @return */ public static int Recharge(Connection con,User user)throws Exception { //使用数据库的悲观锁for update String sql = "select balance from t_user where id=? for update"; //加上for update为数据库加上行级排他锁,防止修改金额出错 int num; //update()返回条数 DbUtil dbUtil=new DbUtil(); PreparedStatement pstmt = null; ResultSet rs = null; float value = 0; try{ //设置自动提交为false DbUtil.beginTransaction(con); pstmt = con.prepareStatement(sql); pstmt.setString(1, user.getId()); rs = pstmt.executeQuery(); rs.next(); //指向第一条记录 value = rs.getFloat("balance"); value=value+user.getBalance();//要求传进来的user为待增加的balance ! user.setBalance(value);//改变balance num=modifyValueField(con,user.getId(),value); //提交事务 DbUtil.commitTransaction(con); }catch(Exception e){ e.printStackTrace(); //回滚事务 DbUtil.rollbackTransaction (con); throw new RuntimeException(); }finally{ DbUtil.close(rs); DbUtil.close(pstmt); DbUtil.resetTransaction(con); dbUtil.closeCon(con); } return num; }
由于时间关系,只对这两个重要事件加了锁。