数据库(二)

目录

数据库(二)

9.4

JDBC

Java DataBase Connectivity

JDBC,是Java程序用于连接不同类型数据库的一套规范

实际是,用Java定义的一套连接数据库时的接口的集合,不同的数据库对其进行了实现

核心接口
  • Connection
    • 用于设置要连接的数据库的信息
      • 数据库地址
      • 用户名
      • 密码
  • PreparedStatement
    • 预处理结果,用于执行SQL语句
  • Result
    • 结果集,用于保存执行了查询后的结果集

JDBC具体使用

导入驱动包并添加依赖关系

项目中新建一个目录,可以命名为lib,将要连接的驱动包保存在其中

驱动包jar

将驱动包保存其中后,右键点击add as libray,添加依赖关系

查询

核心方法

  • Class.forName(“class文件权限定名”)

    • 加载某个class文件
    • Class.forName(“com.mysql.cj.jdbc.Driver”)
  • Connection conn = DriverManager.getConnection(url, username, password)

    • 连接指定数据,返回连接成功的对象Connection
    • url = “jdbc:mysql://localhost:3306/数据库?serverTimezone=Asia/Shanghai”;
  • PreparedStatement pst = conn.prepareStatement(sql)

    • 预处理sql语句,返回处理后的对象
  • ResultSet res = pst.executeQuery()

  • 执行查询

  • res.next() 返回boolean类型值

    • 表示下一行是否有数据
  • res.get数据类型(字段名或字段顺序)

  • 读取查询到的某个字段的值

    • res.getInt(“id”);
    • res.getString(2);
//1.加载连接数据库的驱动文件
Class.forName("com.mysql.cj.jdbc.Driver");
//2.定义连接信息(地址、账号、密码)
String url = "jdbc:mysql://localhost:3306/db_game?serverTimezone=Asia/Shanghai";
String username = "root";
String password = "root";
//连接指定数据库,返回值为连接对象
Connection conn = DriverManager.getConnection(url, username, password);
//3.使用上一步返回的连接对象conn执行指定的sql语句,得到sql预处理对象
String sql = "select * from hero";
PreparedStatement pst = conn.prepareStatement(sql);
//4.使用上一步返回的预处理对象pst执行查询的方法,得到查询后的结果集
ResultSet res = pst.executeQuery();
//5.循环读取查询到的结果集
while (res.next()) {
    //取数据。get数据类型(字段名) 或 get数据类型(字段顺序) 用于获取读取到的一条记录中指定字段的值
    int id=res.getInt("id");
    String name = res.getString(2);
    String sex = res.getString(3);
    double price = res.getDouble(4);
    Date createDate = res.getDate(5);
    String position = res.getString(6);
    //将读取出的数据赋值给一个Hero对象的属性
    Hero hero = new Hero(id, name, sex, position, price, createDate);
    System.out.println(hero);
}
//6.释放资源
res.close();
pst.close();
conn.close();
更新(增加、删除、修改)
  • Class.forName(“class文件权限定名”)

    • 加载某个class文件
    • Class.forName(“com.mysql.cj.jdbc.Driver”)
  • Connection conn = DriverManager.getConnection(url, username, password)

    • 连接指定数据,返回连接成功的对象Connection
    • url = “jdbc:mysql://localhost:3306/数据库?serverTimezone=Asia/Shanghai”;
  • PreparedStatement pst = conn.prepareStatement(sql)

    • 预处理sql语句,返回处理后的对象
  • int i = pst.executeUpdate()

    • 执行更新,返回受影响的行数
  • pst.set数据类型(问号顺序,值)

  • 给sql中指定的问号赋值

//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.连接数据库
String url="jdbc:mysql://localhost:3306/db_game?severTimezone=Asia/Shanghai";
Connection conn = DriverManager.getConnection(url, "root", "root");
//3.定义sql对其预处理。使用?表示sql中的参数
String sql="insert into hero values(null,?,default,5000,now(),null)";
PreparedStatement pst = conn.prepareStatement(sql);
//4.使用预处理对象pst对?赋值 调用 set数据类型(index,param) 方法给第index个?赋值param
pst.setString(1,"xxx");
//5.调用数据更新的方法,返回值是受影响的行数
int i = pst.executeUpdate();
if(i!=0){
    System.out.println("操作成功");
}else{
    System.out.println("操作失败");
}
//6.释放资源
sc.close();
pst.close();
conn.close();

应用

用户表

用户编号 用户名 密码

注册,保证不重复
1.查询当前要注册的信息是否存在

查询:select * from 用户表 where 用户名=‘admin’

2.若存在(能查询出数据)无法注册;若不存在(不能查询出数据)才能注册

注册即添加:insert into 用户表 values(‘admin’,‘123123’)

登录 admin 123123

如果能通过给定的用户名和密码查询到数据,说明输入正确

如果查询结果为空,说明用户名或密码有误

select * from 用户表 where 用户名=‘admin’ and 密码=‘123123’

SQL注入

利用sql语句拼接后,导致原本sql失去本意的漏洞

-- 使用姓名和手机登录 如果能查询出结果则进入系统
-- 如果有一个条件不满足,查询结果为空
select * from employee where emp_name='aw' and emp_phone='123'
-- 王茂鑫 18523473566
select * from employee where emp_name='王茂鑫' and emp_phone='18523473566'

-- "' or 1=1 -- "
select * from employee where emp_name='' or 1=1 -- ' and emp_phone=''

’ or 1=1 – 若将这个字符串作为用户名输入时,会导致sql语句异常,查询出全部数据

package login;
import java.sql.*;
/*
* 注册登录流程
* 注册:
* 1.查询当前要注册的用户名是否存在
* 存在则无法注册
* 2.不存在,执行添加
* 登录:
* 使用用户名和密码查询,如果查询结果为空,说明用户名或密码错误
* */
public class Test {
    public static void main(String[] args) throws SQLException, ClassNotFoundException
    {
        //register("xxxx", "sdfsdf");
        login("' or 1=1 -- ","sdfsdf");
    }
    /*
* 注册的方法
* */
    public static void register(String username, String password) throws
        ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_game?
            serverTimezone=Asia/Shanghai";
            Connection conn = DriverManager.getConnection(url, "root", "root");
        //核心sql1:查询用户名是否存在
        String sql = "select * from userinfo where username = ?";
        PreparedStatement pst = conn.prepareStatement(sql);
        pst.setString(1, username);
        ResultSet res = pst.executeQuery();
        //如果查询到数据,提前结束
        if (res.next()) { //true==true true false==true false
            System.out.println("该用户已存在");
            return;
        }
        //核心sql2:添加用户
        String sql2 = "insert into userinfo values(null,?,?)";
        pst = conn.prepareStatement(sql2);
        pst.setString(1, username);
        pst.setString(2, password);
        if (pst.executeUpdate() > 0) {
            System.out.println("注册成功");
        }
        res.close();
        pst.close();
        conn.close();
    }
    public static void login(String username, String password) throws SQLException,
    ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/db_game?
            serverTimezone=Asia/Shanghai";
            Connection conn = DriverManager.getConnection(url, "root", "root");
        //登录核心sql:根据用户名密码查询
        String sql = "select * from userinfo where username =? and password=?";
        PreparedStatement pst = conn.prepareStatement(sql);
        /*pst.setString(1, username);
pst.setString(2, password);*/
        ResultSet res = pst.executeQuery();
        if (res.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("用户名或密码错误");
        }
        res.close();
        pst.close();
        conn.close();
    }
}

简化JDBC

DBUtil

定义数据库工具类

package com.hqyj.jdbc_plus.util;

import java.sql.*;

public class DBUtil {

    /*
     * 数据库工具类
     * 用于提取JDBC连接数据库时的公共代码
     * //公共代码:
     * //1.加载驱动
     * //2.获取连接
     * //3.释放资源
     * */

    //静态代码块,在类加载时就执行,只执行一次
    static {
        try{
            //加载数据库驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("加载驱动异常"+e);
        }
    }

    //定义常量保存数据库的地址、用户名、密码
    private static final String URL="jdbc:mysql://localhost:3306/db_emp?serverTimezone=Asia/Shanghai";
    private static final String NAME="root";
    private static final String PWD="root";

    //获取连接数据库对象
    public static Connection getConnection(){
        Connection conn =null;
        try {
            conn=DriverManager.getConnection(URL, NAME, PWD);
        } catch (SQLException e) {
            System.out.println("获取连接异常"+e);
        }
        return conn;
    }

    //释放资源
    public static void release(Connection conn, PreparedStatement pst, ResultSet res){
        try {
            //判断非空,防止空指针异常
            if(res!=null){
                res.close();
            }
            if (pst!=null){
                pst.close();
            }
            if(conn!=null){
                conn.close();
            }
        }catch (SQLException e){
            System.out.println("关闭异常"+e);
        }
    }
}

存储数据表字段

数据表字段类似与类中的属性

package com.hqyj.jdbc_plus.entity;

import java.util.Date;
/*
* 用于保存Employee表中数据的实体类
* entity包表示实体包
* 属性表示表中的字段
* 全参与无参构造方法
* get()/set()方法
* toString()方法
* */
public class Employee {
    private int empNo;
    private String empName;
    private String empPhone;
    private Date joinDate;
    private String dept;
    private double salary;
    private String email;

    //无参构造
    public Employee() {
    }
    //全参构造
    public Employee(int empNo, String empName, String empPhone, Date joinDate, String dept, double salary, String email) {
        this.empNo = empNo;
        this.empName = empName;
        this.empPhone = empPhone;
        this.joinDate = joinDate;
        this.dept = dept;
        this.salary = salary;
        this.email = email;
    }

    @Override
    public String toString() {
        return "Employee{" +
            "empNo=" + empNo +
            ", empName='" + empName + '\'' +
            ", empPhone='" + empPhone + '\'' +
            ", joinDate=" + joinDate +
            ", dept='" + dept + '\'' +
            ", salary=" + salary +
            ", email='" + email + '\'' +
            '}';
    }

    public int getEmpNo() {
        return empNo;
    }

    public void setEmpNo(int empNo) {
        this.empNo = empNo;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpPhone() {
        return empPhone;
    }

    public void setEmpPhone(String empPhone) {
        this.empPhone = empPhone;
    }

    public Date getJoinDate() {
        return joinDate;
    }

    public void setJoinDate(Date joinDate) {
        this.joinDate = joinDate;
    }

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

数据库访问层
package com.hqyj.jdbc_plus.dao;

import com.hqyj.jdbc_plus.entity.Employee;
import com.hqyj.jdbc_plus.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/*
* dao层表示数据库访问层
* 该层中的类都是用于操作数据库
* 类名通常命名为:实体类Dao
* */
public class EmployeeDao {
    //定义操作数据库时所需要的接口
    Connection conn;
    PreparedStatement pst;
    ResultSet res;
    //查询所有,返回所有Employee对象集合
    public List<Employee> queryAll(){
        //创建集合用于最终的返回值
        List<Employee> list = new ArrayList<>();
        try {
            //加载驱动、连接
            conn= DBUtil.getConnection();
            //构造sql语句,对其预处理
            pst=conn.prepareStatement("select * from employee");
            res=pst.executeQuery();
            while (res.next()){
                int id=res.getInt(1);
                String name=res.getString(2);
                String phone=res.getString(3);
                Date date=res.getDate(4);
                String dept=res.getString(5);
                double salary=res.getDouble(6);
                String email=res.getString(7);
                //读取到的数据保存在一个对象里
                Employee emp=new Employee(id,name,phone,date,dept,salary,email);
                //将读取到的数据对象保存到集合中
                list.add(emp);
            }
        }catch (SQLException e){
            System.out.println("查询所有异常"+e);
        }finally {
            //释放资源
            DBUtil.release(conn,pst,res);
        }
        return list;
    }
    //根据编号查询员工
    public Employee queryId(int id){
        Employee emp=null;
        //加载与连接
        try {
            conn=DBUtil.getConnection();
            pst=conn.prepareStatement("select * from employee where emp_no=?");
            pst.setInt(1,id);
            res=pst.executeQuery();
            if(res.next()){
                emp=new Employee(res.getInt(1),res.getString(2),res.getString(3),res.getDate(4),res.getString(5),res.getDouble(6),res.getString(7));
            }
        }catch (SQLException e){
            System.out.println("查询id异常"+e);
        }finally {
            DBUtil.release(conn,pst,res);
        }
        return emp;
    }



    //添加新员工
    public boolean insertEmp(int id,String name,String phone,Date date,String dept, double salary,String email){
        conn=DBUtil.getConnection();
        try {
            pst=conn.prepareStatement("insert into employee values (?,?,?,?,?,?,?)");
            if (id==-1){
                pst.setInt(1,0);
            }else {
                pst.setInt(1,id);
            }
            pst.setString(2,name);
            pst.setString(3,phone);
            pst.setDate(4,new java.sql.Date(date.getTime()));
            pst.setString(5,dept);
            pst.setDouble(6,salary);
            pst.setString(7,email);
            return pst.executeUpdate()>0;
        }catch (SQLException e){
            System.out.println("添加员工异常"+e);
        }finally {
            DBUtil.release(conn,pst,res);
        }
        return false;
    }
    //修改员工信息
    public boolean updateSalary(int empNo,double salary){
        try {
            conn=DBUtil.getConnection();
            pst=conn.prepareStatement("update employee set salary=? where emp_no=?");
            pst.setDouble(1,salary);
            pst.setDouble(2,empNo);
            int i=pst.executeUpdate();
            return pst.executeUpdate()>0;
        }catch (SQLException e){
            System.out.println("修改薪资异常"+e);
        }finally {
            DBUtil.release(conn,pst,res);
        }
        return false;
    }
    //删除员工
    public boolean delectEmp(int id){
        try {
            conn=DBUtil.getConnection();
            if(id==-1){
                pst=conn.prepareStatement("truncate table employee");
                pst.executeUpdate();
                return true;
            }else {
                pst=conn.prepareStatement("delete from employee where emp_no=? ");
                pst.setInt(1,id);
                return pst.executeUpdate()>0;
            }
        }catch (SQLException e){
            System.out.println("删除员工异常"+e);
        }
        finally {
            DBUtil.release(conn,pst,res);
        }
        return false;
    }
}

用法
package com.hqyj.jdbc_plus.service;

import com.hqyj.jdbc_plus.dao.EmployeeDao;
import com.hqyj.jdbc_plus.entity.Employee;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        EmployeeDao employeeDao = new EmployeeDao();
        Scanner sc = new Scanner(System.in);
        while (true){
            System.out.println("1.查询所有员工");
            System.out.println("2.根据编号查询员工");
            System.out.println("3.添加员工");
            System.out.println("4.修改员工");
            System.out.println("5.删除员工");
            System.out.println("6.退出员工");
            System.out.println("请输入操作编号");
            switch (sc.nextInt()){
                case 1:
                    List<Employee> list=employeeDao.queryAll();
                    list.forEach((i)->{
                        System.out.println(i.getEmpNo()+"\t"+i.getEmpName()+"\t"+i.getEmpPhone()+"\t"+i.getDept()+"\t"+i.getJoinDate()+"\t"+i.getSalary()+"\t"+i.getEmail());
                    });
                    break;
                case 2:
                    System.out.println("请输入要查询的id");
                    int id=sc.nextInt();
                    Employee emp=employeeDao.queryId(id);
                    if(emp!=null){
                        System.out.println(emp.getEmpNo()+"\t"+emp.getEmpName()+"\t"+emp.getEmpPhone()+"\t"+emp.getDept()+"\t"+emp.getJoinDate()+"\t"+emp.getSalary()+"\t"+emp.getEmail());
                    }
                    else {
                        System.out.println("该"+id+"员工未查询到");
                    }
                    break;
                case 3:
                    System.out.println("请输入要添加的员工编号,自动自增输入-1或输入指定id");
                    int id3=sc.nextInt();
                    System.out.println("请输入要添加的员工名");
                    String name=sc.next();
                    System.out.println("请输入要添加的员工电话号码");
                    String phone=sc.next();
                    System.out.println("请输入要添加的员工入职日期,自动生成当前日期null或自定义日期");
                    Date date;
                    String d=sc.next();
                    if(d.equals("null")){
                        date=new Date();
                    }else {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        System.out.println("输入自定义日期,格式yyyy-MM-dd");
                        while (true){
                            try {
                                date=sdf.parse(d);
                                break;
                            } catch (ParseException e) {
                                System.out.println("输入日期格式不规范"+e+"请重输入");
                                d=sc.next();
                            }
                        }
                    }

                    System.out.println("请输入要添加的员工部门");
                    String dept=sc.next();
                    System.out.println("请输入要添加的员工薪资");
                    double salary=sc.nextDouble();
                    System.out.println("请输入要添加的员工邮箱");
                    String email=sc.next();

                    if(employeeDao.insertEmp(id3,name,phone,date,dept,salary,email)){
                        System.out.println("添加员工成功");
                    }else {
                        System.out.println("操作失败");
                    }

                    break;
                case 4:
                    System.out.println("修改员工");
                    System.out.println("请输入要修改的id");
                    int id4=sc.nextInt();
                    System.out.println("请输入工资");
                    double salary4=sc.nextDouble();
                    if (employeeDao.updateSalary(id4,salary4)){
                        System.out.println("更新成功");
                    }
                    else {
                        System.out.println("该"+id4+"员工不存在,更新失败");
                    }
                    break;
                case 5:
                    System.out.println("请选择删除员工(全部输入-1;指定输入删除的id)");
                    int id5=sc.nextInt();
                    if (employeeDao.delectEmp(id5)){
                        System.out.println("删除成功");
                    }
                    else {
                        System.out.println("该"+id5+"员工不存在,删除失败");
                    }

                    break;
                case 6:
                    System.out.println("程序结束");
                    System.exit(0);
                    break;
                default:
                    System.out.println("输入操作序号无法识别");
            }
        }

    }
}

IDEA的一些便于操作的选择

忽略大小写,均字符提示

忽略大小写,均字符提示

自动导包与删包

自动导包与删包

安装翻译插件

翻译插件

选择微软翻译

9.5

连接查询

表1 inner/left/right join 表2 on 表1.字段=表2.字段

笛卡尔积、交叉连接

将两张表中的数据两两组合,得到的结果就是交叉连接的结果,也称为笛卡尔积

集合A:{a,b};集合B:{1,2,3};

集合A x 集合B = {a1,a2,a3,b1,b2,b3}

  • select * from 表1**,**表2;
  • select * from 表1 cross join 表2;
  • select * from 表1 inner join 表2;
-- 交叉连接
SELECT * FROM book_type,book_info;

SELECT * from book_type CROSS JOIN book_info;

SELECT * from book_type INNER JOIN book_info;

以上三种方式都能将两张表中的数据互相组合,其中有很多无效数据

内连接

在交叉连接的基础上,筛选出相关联的数据

  • select * from 表1**,**表2 where 表1.字段 = 表2.字段;
  • select * from 表1 cross join 表2 on 表1.字段 = 表2.字段;
  • select * from 表1 inner join 表2 on 表1.字段 = 表2.字段;
-- 查询所有图书详情和类型名
select * from 图书详情表 t1,图书类型表 t2 where t1.类型编号=t2.类型编号;
select * from 图书详情表 t1 inner join 图书类型表 t2 on t1.类型编号=t2.类型编号;


-- 内连接
SELECT * FROM book_type,book_info WHERE book_type.type_id=book_info.type_id;

-- 为表重命名

SELECT * FROM book_type bt,book_info bi WHERE bt.type_id=bi.type_id;

-- 使用cross join、inner join 进行内连接使用on 
SELECT * FROM book_type bt CROSS JOIN book_info bi on bt.type_id=bi.type_id;

SELECT * from book_type bt INNER JOIN book_info bi on bt.type_id=bi.type_id ;
外连接
左连接 left join
-- 左连接
SELECT * from book_type bt left join book_info bi on bt.type_id=bi.type_id;

-- 查询没有图书的类型
SELECT type_name from book_type bt left JOIN book_info bi on bt.type_id=bi.type_id  where book_id is null;
右连接 right join
-- 右连接
SELECT * from book_info bi RIGHT JOIN book_type bt on bt.type_id=bi.type_id;

-- 查询每个类型下的图书平均价格和类型名,显示没有图书的类型
SELECT type_name,AVG(book_price) avg FROM book_info bi RIGHT JOIN book_type bt on bt.type_id=bi.type_id GROUP BY bt.type_id;

表的复用

可以在一个查询中,将一张表使用多次。一定要将表重命名

表复用

-- 表复用

-- 查询书名相同、作者不同的图书;显示图书的编号、名称、作者
SELECT t1.book_id,t1.book_name,t1.book_author from book_info t1,book_info t2 where t1.book_name=t2.book_name and t1.book_author!=t2.book_author;

-- 查询书名相同、作者不同的图书;显示图书的编号、名称、作者、类型名
SELECT t1.book_id,t1.book_name,t1.book_author,type_name from book_info t1,book_info t2,book_type bt where t1.book_name=t2.book_name and t1.book_author!=t2.book_author and t1.type_id=bt.type_id;

嵌套查询

将查询出的结果继续使用在另一个查询语句中

-- 查询价格最低的图书

-- 1.查询最低价格 2.将查询的结果看做整体作为查询的条件
select * from book_info where book_price = (select min(book_price) from book_info)

-- 按类型分组,查询每组中价格大于平均价格的图书
-- 1.查询每组的平均价格 2.类型编号相同,价格大于均价
select * from book_info bi,(select type_id , avg(book_price) avg from book_info group by type_id) temp where book_price> avg and bi. type_id=temp.type_id

数据库设计

绘图软件

实体关系模型

实体Entity:一张表就是一个实体

关系Relationship:实体与实体之间的关系

实体关系模型,也称为ER模型

用图形表示ER模型时,这个图称为ER图

ER图
  • 矩形表示实体
  • 椭圆形表示实体的属性
  • 菱形表示实体之间的关系
  • 直线连接各个图形
一对一

实体A与实体B之间唯一对应

如一个国家有一个领导人;一个人对应一个配偶

一对一

在数据库中创建表的过程
方式一:使用一张表实现

国家实体的某个属性和领导人实体的某个属性需要添加唯一约束

一张表实现

方式二:使用两张表实现(推荐)

各自保存实体的属性,选择其中一张表添加额外的一列,关联另一张表的主键,且这一列还要添加唯一约束

领导表

create table leader(
    id int not null primary key auto_increment,
    name varchar(50) not null,
    birthday date ,
    sex char(1) not null
)

国家表

create table country(
    id int not null primary key auto_increment,
    name varchar(50) not null,
    poplulation bigint not null,
    area double not null,
    leader_id int not null unique,
    foreign key leader_id references leader(id)
)

方式三:使用三张表实现

两张表用于保存两个实体的信息,第三张表用于表示对应关系

使用三张表实现

一对多/多对一

一对多:一个实体A对应多个实体B,一个实体B不能对应多个实体A

如:一个人可以有多辆车,一辆车不能被多个人拥有

多对一:多个实体B对应一个实体A,多个实体A不能对应一个实体B

如:多个学员对应一个训练他们的教官,多个教官不能对应一个学员

一对多/多对一

在数据库中创建表的过程

主表

create table coach(
    id int not null primary key auto_increment,
    name varchar(20) not null,
    level varchar(20) not null
)

创建从表

create table student(
    id int not null primary key auto_increment,
    phone varchar(20) not null,
    name varchar(20) not null,
    coach_id int not null,
    foreign key coach_id references coach(id)
)
多对多

一个实体A可以对应多个实体B,一个实体B也可以对应多个实体A

如:一个学生可以学习多门课程,一门课程可以对应多个学习它的学生

如:一个医生对应多个病人,一个病人可以看多个医生

多对多

在数据库中创建表的过程

创建学生表

create table student(
    id int not null primary key auto_increment,
    name varchar(20) not null,
    phone varchar(20)
)

课程表

create table course(
    id varchar(10) not null primary key ,
    name varchar(20) not null,
    credit int not null
)

成绩表

create table score(
    id int not null primary key auto_increment
    s_id int not null,
    c_id varchar(20) not null,
    cj int not null,
    foreign key s_id references student(id),
    foreign key c_id references course(id)
)

总结

一对一:创建各自的实体表

  • 在任意一张表中添加另一张表的主键字段,将其设置为唯一

一对多/多对一:先创建主表(一),再创建从表(多)

  • 在从表中添加主表的主键字段,外键可选添加

多对多:创建各自的实体表,再创建第三张表:“关系表”

  • 在关系表中添加两个实体表中的主键字段,外键可选添加
练习

医院信息管理系统

  • 科室表

    create table dept(
        id int not null primary key auto_increment,
        name varchar(20) not null,
        phone varchar(20) not null
    )
    
  • 医生表

    CREATE TABLE doctor(
        id int not null PREPARE key auto_increment,
        name VARCHAR(20) not null,
        phone VARCHAR(20) not null,
        dept_id int not null,
        FOREIGN KEY(dept_id) REFERENCES dept(id);
    )
    
  • 病人表

    CREATE TABLE patient(
        id int not null PRIMARY key auto_increment,
        name VARCHAR(20) not null,
        phone VARCHAR(20) not null,
        age int not null,
        address VARCHAR(20)
    )
    
  • 诊断记录表

    CREATE TABLE diagnosis(
        id int not null PRIMARY key auto_increment,
        dr_id int not null,
        p_id int not null,
        diagnosis_time dateime not null,
        diagnosis_result text not null,
        foreign key dr_id REFERENCES doctor(id),
        foreign key p_id references patient(id)
    )
    

数据库设计规范

数据库设计的规范,简称为范式NF

范式分为第一范式1NF,第二范式2NF,第三范式3NF,BC范式BCNF,第四范式4NF,第五范式5NF共六种

这六种范式的级别越高(第一到第五越来越高),表示数据库设计的结构越规范

每一个高等级的范式都包含了低等级的范式

通常设计数据库时,只需满足3NF即可

如:有该原始表

  • 黄色背景称为联合主键
  • 由学号和科目一起区分每一条记录
  • 暂时这张表不满足任何范式

原始表

当前表如果要做增删改查的操作,会涉及到的问题

  • 如果要加入一个新的系别,就要添加学生信息、系主任
  • 如果要删除"刘定宇",他所占的系别也会被删除
  • 如果将"王海"的系别改为会计,相应的系主任也要修改
  • 当前表中有大量冗余数据
第一范式1NF

数据表中的每一列都是不可分割的原子项

关系型数据库中,起码要满足1NF,才能创建表

上表中的"联系方式"列,可以分为"手机"和"QQ"两列,不满足原子性,不满足1NF

根据1NF修改:

根据1NF修改

第二范式2NF

在满足1NF的基础上,消除部分依赖

  • 对于联合主键而言,每一个非主属性字段都需要完全依赖于主属性,而不是只依赖其中的一部分
  • 在上图中,无法用学号当主键
  • 学号和科目组合为联合主键
  • 联合主键才能得到分数
  • 除了学号和科目外,其他字段都是非主属性字段
    • 分数完全依赖于联合主键
    • 其他字段部分依赖于联合主键,所以对其进行拆分

消除部分依赖

第三范式3NF

在满足2NF的基础上,消除传递依赖

在上图中,系主任是通过学号–>系别–>系主任获取,系主任传递依赖于学号,消除这个传递依赖

**在满足2NF的基础上,消除传递依赖**

最终根据实体关系模型进行优化,体现对应关系

最终优化

名词解释
  • 主键/主码/主属性

    • 用于区分每条记录的一个字段
    • 通常选择能唯一确定且几乎不会更改的字段作为主键
    • 如果没有,自定义一个id列作为主键
  • 联合主键

    • 若一个字段不能唯一区分每条记录,且需要多个字段一起才能区分,这些字段组成了联合主键
  • 完全依赖

    • 如果能通过A和B得到C,A和B都不能单独得到C时,称为C完全依赖于A和B

      如:(学号+科目)联合主键–>分数,分数完全依赖于联合主键,其他字段完全依赖于学号

  • 部分依赖

    • 通过A和B可以得到C,单独通过A或B也能得到C,称为C部分依赖于A和B

      如:(学号+科目)联合主键–>姓名,其实只通过学号也能得到姓名,称为姓名部分依赖于联合主键

  • 传递依赖

    • 如果通过A得到B,通过B得到C,称为C传递依赖于A

      如:学号–>系–>系主任。其实只通过系也能得到系主任,称为系主任传递依赖于学号

连接查询练习

-- 创建学生信息库 studb;
drop database if EXISTS studb;
create database studb;
use studb;

-- 学生表student
drop table if EXISTS student;
create table student(
    stu_id int not null primary key auto_increment comment '学号',
    stu_name varchar(20) not null comment '姓名',
    stu_sex char(1) comment '性别'
);


-- 课程表course
drop table if EXISTS course;
create table course(
    c_id varchar(20) not null primary key comment '课程号',
    c_name varchar(20) not null comment '课程名',
    c_redit int not null comment '学分'
);



-- 成绩表score
drop table if EXISTS score;
create table score(
    s_no int not null primary key auto_increment comment '成绩编号',
    stu_id int not null comment '学号',
    c_id varchar(20) not null comment '课程号',
    cj int not null comment '成绩',
    foreign key(stu_id) REFERENCES student(stu_id),
    FOREIGN key(c_id) REFERENCES course(c_id)
);




insert into student values(1001,'张晓红','女');
insert into student values(null,'张伟','男');
insert into student values(null,'肖怀伟','男');
insert into student values(null,'卢宇鹏','男');
insert into student values(null,'白思琪','女');
insert into student values(null,'黄鹏','男');
insert into student values(null,'吕思源','女');

insert into course values('c9001','高等数学',8);
insert into course values('c9002','大学英语',8);
insert into course values('c9003','思修',8);
insert into course values('c9004','大学体育',4);


insert into score values(null,'1001','c9003','88');
insert into score values(null,'1001','c9001','79');
insert into score values(null,'1002','c9001','84');
insert into score values(null,'1002','c9003','68');
insert into score values(null,'1003','c9002','78');
insert into score values(null,'1003','c9003','90');
insert into score values(null,'1003','c9004','69');
insert into score values(null,'1004','c9003','55');
insert into score values(null,'1004','c9004','54');
insert into score values(null,'1005','c9003','68');
insert into score values(null,'1005','c9004','74');
insert into score values(null,'1005','c9002','72');

select * from course;
select * from student;
select * from score;



-- 查询每位学生的学号、姓名、所学课程名,考试成绩,对字段重命名
SELECT st.stu_id 学号,stu_name 姓名,c_name 所学课程名,cj 考试成绩 from student st,score sc,course cs where st.stu_id=sc.stu_id and sc.c_id=cs.c_id;

-- 查询所有女生的学号、姓名、学习的课程名,成绩
SELECT st.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from (SELECT * from student where stu_sex='女') st,score sc,course cs where st.stu_id=sc.stu_id and sc.c_id=cs.c_id;

SELECT st.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from (SELECT * from student where stu_sex='女') st INNER JOIN score sc on st.stu_id=sc.stu_id INNER JOIN course cs on sc.c_id=cs.c_id;

SELECT st.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from student st INNER JOIN score sc on st.stu_id=sc.stu_id INNER JOIN course cs on sc.c_id=cs.c_id  where stu_sex='女';

-- 查询参加了’高等数学‘考试的学生学号、姓名、成绩
SELECT st.stu_id 学号,stu_name 姓名,cj 考试成绩 from student st,score sc,(SELECT * from course where c_name='高等数学') cs where st.stu_id=sc.stu_id and sc.c_id=cs.c_id;


SELECT st.stu_id 学号,stu_name 姓名,cj 考试成绩 from student st INNER JOIN score sc on st.stu_id=sc.stu_id INNER JOIN (SELECT * from course where c_name='高等数学') cs on sc.c_id=cs.c_id;

-- 查询没有参加考试的学生
SELECT st.stu_id 学号,stu_name 姓名 from student st LEFT JOIN score sc on st.stu_id=sc.stu_id where cj is null;

-- 查询每门课程的平均成绩、总成绩、最高分、最低分
SELECT c_name 课程名,avg(cj) 平均成绩,sum(cj) 总成绩,max(cj) 最高分, min(cj) 最低分 from score sc right JOIN course cs on sc.c_id=cs.c_id GROUP BY cs.c_id;

-- 查询每个学生的学号、姓名、总分、平均分
select st.stu_id 学号,stu_name 姓名,sum(cj) 总分,avg(cj) 平均分 from student st LEFT JOIN score sc on st.stu_id=sc.stu_id GROUP BY st.stu_id;

-- 查询姓“张”的同学的学号、姓名、考试课程、成绩
SELECT st.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from student st INNER JOIN score sc on st.stu_id=sc.stu_id INNER JOIN course cs on sc.c_id=cs.c_id  where stu_name LIKE '张%';

-- 查询没有及格(60分以下)的学生及其课程名、成绩
SELECT stu_name 姓名,c_name 课程名,cj 成绩 from student st INNER JOIN score sc on st.stu_id=sc.stu_id INNER JOIN course cs on sc.c_id=cs.c_id  where cj<60;

-- 查询男生和女生的平均成绩
select stu_sex 性别,avg(cj) 平均分 from student st LEFT JOIN score sc on st.stu_id=sc.stu_id GROUP BY st.stu_sex;

-- 查询总分大于160分的同学的学号、姓名、总分
select st.stu_id 学号,stu_name 姓名,sum(cj) 总分 from student st INNER JOIN score sc on st.stu_id=sc.stu_id GROUP BY st.stu_id HAVING sum(cj)>160;

-- 查询平均分大于80的课程名、平均分
SELECT c_name 课程名,avg(cj) 平均分 from score sc INNER JOIN course cs on sc.c_id=cs.c_id GROUP BY cs.c_id HAVING avg(cj)>80;

-- 查询参加了3门考试的同学的学号、姓名、课程名、成绩,按成绩降序

SELECT st1.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from (SELECT sc.stu_id,stu_name from student st,score sc where st.stu_id=sc.stu_id GROUP BY st.stu_id HAVING count(sc.c_id)=3) st1 INNER JOIN score sc1 on st1.stu_id=sc1.stu_id INNER JOIN course cs1 on sc1.c_id=cs1.c_id ORDER BY st1.stu_id,cj desc; 

SELECT st.stu_id 学号,stu_name 姓名,c_name 课程名,cj 成绩 from student st,score sc,course cs where st.stu_id=sc.stu_id and sc.c_id=cs.c_id and st.stu_id in (SELECT stu_id from score GROUP BY stu_id HAVING count(s_no)=3) ORDER BY st.stu_id,cj desc ;



-- 添加教师表teacher
create table teacher(
    t_id varchar(20) not null primary key,
    t_name varchar(20) not null
);

-- 添加授课表
create  table teach(
    id int not null primary key auto_increment,
    t_id varchar(20) not null,
    c_id varchar(20) not null
);


-- INSERT
insert into teacher values('t001','吴彦祖');
insert into teacher values('t002','易烊千玺');
insert into teacher values('t003','刘德华');
insert into teacher values('t005','李宇春');
insert into teacher values('t004','邓超');


insert into teach() values(0,'t001','c9001');
insert into teach() values(0,'t001','c9002');
insert into teach() values(0,'t001','c9004');
insert into teach() values(0,'t002','c9003');
insert into teach() values(0,'t002','c9001');
insert into teach() values(0,'t003','c9002');
insert into teach() values(0,'t003','c9003');
insert into teach() values(0,'t003','c9004');
insert into teach() values(0,'t005','c9001');
insert into teach() values(0,'t005','c9002');


SELECT * from teacher;
SELECT * from teach;
select * from course;
select * from student;
select * from score;


-- 查询每个教师的姓名及其所教课程
SELECT t_name 教师姓名,c_name 所教课程 from teacher tr 
INNER JOIN teach th on tr.t_id=th.t_id 
INNER JOIN course cs on th.c_id=cs.c_id;


SELECT t_name 教师姓名,c_name 所教课程 from teacher tr 
LEFT JOIN teach th on tr.t_id=th.t_id
LEFT JOIN course cs on th.c_id=cs.c_id;


-- 查询每个教师所教课程数量
SELECT t_name 教师姓名,count(c_id) 所教课程数量 from teacher tr 
INNER JOIN teach th on tr.t_id=th.t_id 
GROUP BY tr.t_id;

SELECT t_name 教师姓名,count(c_id) 所教课程数量 from teacher tr 
left JOIN teach th on tr.t_id=th.t_id 
GROUP BY tr.t_id;

-- 查询没有上课的教师姓名
SELECT t_name 教师姓名 from teacher tr 
LEFT JOIN teach th on tr.t_id=th.t_id 
WHERE c_id is null;

-- 查询授课3门的教师的姓名及其所授课程
SELECT t_name 教师姓名,c_name 所教课程 from teacher tr 
INNER JOIN teach th on tr.t_id=th.t_id 
INNER JOIN course cs on th.c_id=cs.c_id 
where tr.t_id in(SELECT t_id from teach th GROUP BY t_id HAVING count(t_id)=3);


SELECT t_name 教师姓名,c_name 所教课程 from teacher tr ,(SELECT t_id from teach th GROUP BY t_id HAVING count(t_id)=3) tmp,teach th,course cs where tr.t_id=tmp.t_id and tmp.t_id=th.t_id and th.c_id=cs.c_id;

-- 查询每个教师所带学生数量,不显示没有学生的教师
SELECT t_name 教师,count(DISTINCT sc.stu_id) 所带学生数量 from teacher tr INNER JOIN teach th on tr.t_id=th.t_id 
INNER JOIN score sc on th.c_id=sc.c_id 
GROUP BY th.t_id;


SELECT count(DISTINCT sc.stu_id) 所带学生数量 from teach th 
INNER JOIN score sc on th.c_id=sc.c_id 
GROUP BY th.t_id;


-- 查询每个教师所教课程的平均分
SELECT t_name 教师,c_name 课程名, avg 平均分 from (SELECT c_id,avg(cj) avg from score GROUP BY c_id) s1 
INNER JOIN course cs on cs.c_id=s1.c_id 
INNER JOIN teach th on th.c_id=cs.c_id INNER JOIN teacher tr on tr.t_id=th.t_id ORDER BY tr.t_id,avg DESC;


SELECT t_name 教师,c_name 课程名, avg 平均分 
from (SELECT c_id,avg(cj) avg from score GROUP BY c_id) s1 
RIGHT JOIN course cs on cs.c_id=s1.c_id  
RIGHT JOIN teach th on th.c_id=cs.c_id 
RIGHT JOIN teacher tr on tr.t_id=th.t_id 
ORDER BY tr.t_id,avg DESC;


-- 对一个老师所教的所有课程求平均值
SELECT t_name 教师,avg(cj) 平均分 from  score sc 
RIGHT JOIN teach th on th.c_id=sc.c_id 
RIGHT JOIN teacher tr on tr.t_id=th.t_id 
GROUP BY tr.t_id;



-- 查询每个同学的姓名、课程名、每门成绩、按成绩降序
SELECT stu_name 姓名,c_name 课程名,cj 成绩 from student st 
INNER JOIN score sc on st.stu_id=sc.stu_id 
INNER JOIN course cs on sc.c_id=cs.c_id 
ORDER BY st.stu_id,cj DESC; 


-- -- 查询每个同学的姓名、课程名、每门成绩、要包含没有考试的学生,按成绩降序
SELECT stu_name 姓名,c_name 课程名,cj 成绩 from student st 
LEFT JOIN score sc on st.stu_id=sc.stu_id 
LEFT JOIN course cs on sc.c_id=cs.c_id 
ORDER BY st.stu_id,cj DESC; 

-- 查询平均分最高的课程名及其授课教师

SELECT cs.c_name 课程名称,tr.t_name 授课教师 from 
(SELECT c_id,avg(cj) avg from score GROUP BY c_id) s1 
INNER JOIN course cs on cs.c_id=s1.c_id 
INNER JOIN teach th on th.c_id=cs.c_id 
INNER JOIN teacher tr on tr.t_id=th.t_id 
where avg=(SELECT max(avg) from (SELECT avg(cj) avg from score GROUP BY c_id) s);

-- 视图
-- 将某个查询得到结果临时保存为一张表
 create view myview as SELECT c_id,avg(cj) avg from score GROUP BY c_id
 
 SELECT cs.c_name 课程名称,tr.t_name 授课教师 from myview s1 
 INNER JOIN course cs on cs.c_id=s1.c_id 
 INNER JOIN teach th on th.c_id=cs.c_id 
 INNER JOIN teacher tr on tr.t_id=th.t_id 
 where avg=(SELECT max(avg) from myview);

总体应用-美术馆票务管理系统数据库设计

组织结构图

美术馆票务管理系统

E-R图

E-R图

数据库具体设计
  • 展厅表venues

    -- 展馆表
    create table venues(
    venues_id int not null primary key auto_increment comment '展馆编号',
    venues_name varchar(20) not null comment '展馆名称',
    venues_price double not null comment '展馆门票价格'
    )
    
  • 作品展表arts

    -- 作品展表
    create table arts(
    arts_id int not null primary key auto_increment comment '作品编号',
    arts_topic varchar(50) not null comment '作品主题',
    arts_author varchar(50) not null comment '作品作者',
    arts_notes text comment '作品描述',
    arts_poster varchar(50) comment '作品海报'
    )
    
  • 展览时间表show_time

    -- 展映时刻表
    create table show_time(
    id int not null primary key auto_increment comment '编号',
    exhibition_time datetime not null comment '展映时间',
    total_tickets int not null comment '总票数',
    remaining_tickets int not null comment '余票',
    venues_id int not null comment '展馆编号',
    arts_id int not null comment '作品编号'
    )
    
  • 用户表userinfo

    -- 用户信息表
    create table userinfo(
    user_id int not null primary key auto_increment comment '用户编号',
    user_phone varchar(50) not null comment '登录电话',
    user_password varchar(50) not null comment '登录密码',
    user_nickname varchar(50) comment '昵称'
    )
    
  • 订单表orders

    -- 订单表
    create table orders(
    order_id int not null primary key auto_increment comment '订单编号',
    show_time_id int not null comment '展映时间编号',
    user_id int not null comment '用户编号',
    tickets_num int comment '买票数量',
    cost double comment '费用',
    order_time datetime comment '下单时间',
    order_state int default 0 comment '订单状态 0未使用 1已使用 2申请退票 3退票成功'
    )
    
实际功能对应的SQL语句
-- 假设管理员已经登录
-- 添加一些展馆信息
insert into venues values
(null,'A馆',100),
(null,'B馆',80),
(null,'C馆',80),
(null,'D馆',60)
-- 有作品展想要在B馆进行展览
-- 添加一条作品展的信息
INSERT INTO arts values(9001,'xxx摄影展','xxx','记录美好生活','sdfsdf.jpg');
-- B馆进行展览
-- 添加一条展映记录
insert into show_time values(null,'2023-09-08 14:00:00',500,500,2,9001);
-- 查询当前美术馆中可以观看的展览
-- 查询展览的名称、内容、余票、价格、地点、时间
select
arts_topic,arts_notes,arts_poster,remaining_tickets,venues_price,venues_name,exhibition
_time
select *
from arts a,venues v,show_time s where a.arts_id=s.arts_id and v.venues_id=s.venues_id
and exhibition_time>now()
-- 用户登录系统
-- 注册 13895522678
select * from userinfo where user_phone = '13895522678';
insert into userinfo values(null,'13895522678','123123',concat('游
客',right('13895522678',4)))
-- 登录
select * from userinfo where user_phone='13895522678' and user_password='123123'
-- 订票
insert into orders values(null,952701,1,2,160,now(),default);
update show_time set remaining_tickets=remaining_tickets-2;
-- 查询订单
-- 查询作品展的信息、场馆信息、订单信息
select * from orders o,show_time s,arts a,venues v
where o.show_time_id=s.id and s.arts_id=a.arts_id and s.venues_id=v.venues_id

视图View

视图,可当做数据库中的一个临时表,保存一些较为复杂的查询后的结果

可直接通过视图查询数据,不需要再次编写重复的sql语句

视图还能隐藏一些查询细节定制查询数据

SQL语句用法
create view 视图名 as 查询的sql语句;创建视图
select * from 视图名;使用视图
drop view 视图名;删除视图
-- 查询平均分最高的课程名及其授课教师

-- 视图
-- 将某个查询得到结果临时保存为一张表
 create view myview as SELECT c_id,avg(cj) avg from score GROUP BY c_id

 SELECT cs.c_name 课程名称,tr.t_name 授课教师 from myview s1 
 INNER JOIN course cs on cs.c_id=s1.c_id 
 INNER JOIN teach th on th.c_id=cs.c_id 
 INNER JOIN teacher tr on tr.t_id=th.t_id 
 where avg=(SELECT max(avg) from myview);

表的行列转换

行转列

-- 查询每个同学的姓名、课程名、每门成绩、要包含没有考试的学生

CREATE VIEW stu_score 
as SELECT st.stu_id,stu_name,c_name,cj from student st 
LEFT JOIN score sc on st.stu_id=sc.stu_id
LEFT JOIN course cs on sc.c_id=cs.c_id ; 

-- 行列转换
-- 将stu_score表中数据,输出为"学号、姓名、课程1、课程2...格式"
-- 统计函数造为一行

select stu_id,stu_name,
sum(if(c_name='思修',cj,null)) as '思修',
avg(if(c_name='高等数学',cj,null)) as '高等数学',
max(if(c_name='大学英语',cj,0)) as '大学英语',
min(if(c_name='大学体育',cj,null)) as '大学体育'
from stu_score group by stu_id

-- 如果一门课程一个学生只能有一个成绩,sum()、avg()、max()、min()都可以使用
-- 如果要统计学生对于课程的考试次数,使用count()
-- if(表达式,表达式成立时的结果,表达式不成立时的结果)

事务

事务是由一组sql语句组成的最小执行单元,这些sql之间一般都互相依赖

如A给B转账

  • update 表 set money = money - 200 where id=A
  • update 表 set money = money + 200 where id=B

以上两句sql组成了一个转账的事务,一个事务要么全部执行,要么全部不执行

事务的特性ACID
  • Atomicity 原子性

    事务是最小的执行单元,要么全部执行,要么全部不执行

  • Consistency 一致性

    事务执行前后,必须让所有数据保持一致状态(数据总量守恒)

  • Isolation 隔离性

    多个事务并发执行时,应该互相隔离,互不影响

  • Durability 持久性

    事务一旦提交,对数据的改变是永久的

事务的使用

提交commit回滚rollback

mysql中的事务默认是,自动提交

如果关闭了事务自动提交,在执行某个事务过程中,若出了错误,可使用rollback进行回滚,让数据回到事务执行之前的状态

  • 查询事务自动提交开启状态:select @@autocommit
    • 1,表示开启了自动提交
    • 0,表示关闭了自动提交
  • 关闭事务自动提交:set @@autocommit=0
  • 手动开启事务:start transaction
手动提交/回滚事务
  1. 关闭事务自动提交:set @@autocommit=0
  2. 手动开启事务:start transaction
  3. 事务要执行的sql
  4. 没有commit提交之前,可以使用rollback回滚
  5. 如果没有问题,使用commit提交,一旦提交,无法rollback

一旦commit提交,无法rollback回滚

-- 查看事务自动提交状态
select @@autocommit
-- 设置设置不自动提交
set @@autocommit =0

-- 开启事务
start TRANSACTION;
-- 1006向1008转账200
-- 扣除1006的200
update employee set salary = salary - 200 where emp_no=1006;
-- 模拟出错
jkghkjh;
-- 1008增加200
update employee set salary = salary + 200 where emp_no=1008;

-- 先运行以上三条,若出错单独运行回滚语句,则返回数据回滚
-- 回滚
rollback;
-- 提交
commit;
事务并发可能出现的问题

在同一时刻同时执行多个事务时,称为事务并发

事务并发时有可能会出现以下问题:

问题描述
脏读事务A读取到了事务B未提交的数据
不可重复读事务A中如果要读取两次数据,在这期间,事务B对数据进行了修改并提交,导致事务A读取两次的情况不一致。
幻读事务A读取id为1-10之间的数据,假如只有2和5的数据,在读取期间,事务B添加了一条id为3的数据,导致事务A多读取到了事务B中的数据。
事务隔离级别
隔离级别能否出现脏读能否出现不可重复读能否出现幻读
Read Uncommitted RU未提交读
Read Committed 已提交读RC(Oracle默认)不会
Repeatable Read 可重复读RR(MySQL默认)不会不会
Serializable 可序列化不会不会不会
查看事务隔离级别
select @@transaction_isolation
设置事务隔离级别
set [session|global] transatcion isolation level [Read Uncommitted|Read Committed|Repeatable Read|Serializable]

触发器trigger

若在更新某张表之前before或之后after,自动执行另一组sql时,可以使用触发器实现

表A是客户表,表B是操作日志表

对表A进行更新的操作时,将这次的操作记录保存到表B中

慎用触发器

因为触发器对于表中的每一行都生效,数据多时,触发器也会执行很多次,效率不高

创建触发器
-- 语法
-- create trigger 触发器名
-- 触发时机(before/after) 触发操作(update/insert/delete) on 表 for each row
-- begin
-- 满足触发条件后执行的内容
-- end

create trigger mytrigger
after insert on userinfo for each row
begin
insert into log values(null,'执行了添加用户的操作',now());
end
使用触发器

触发器创建成功后,无需手动调用,在执行满足触发器的条件的操作后,自动执行触发器

-- 执行这句话时,会自动在log表中添加一条记录
insert into userinfo values(null,'a','b')
删除触发器
drop trigger 触发器名;

存储过程procedure

类似于Java中的方法,定义一组用于完成特定功能的sql语句

定义存储过程后,通过调用存储过程名,就可以执行定义时的内容,存储过程还可以有参数

调用存储过程
-- 调用无参数的存储过程
call 存储过程名();

-- 调用有参数的存储过程
call 存储过程名('实参');
定义存储过程
-- 参数类型分为输入型和输出型
create procedure 存储过程名([参数类型 参数名 参数数据类型]) 
begin
sql语句;
end
定义无参数的存储过程
create procedure 存储过程名()
begin
sql语句;
end

-- 定义无参数的存储过程,查询每本图书的所有信息
CREATE PROCEDURE myproc1 ()
BEGIN
SELECT
*
FROM
book_info bi
INNER JOIN book_type bt ON bi.type_id = bt.type_id;
END

-- 调用存储过程
call myproc1();
定义输入型参数的存储过程
create procedure 存储过程名(in 形参名 数据类型)
begin
sql语句;
end

-- 根据图书类型名,查询该类型下的所有图书
CREATE PROCEDURE myproc2 (IN lx VARCHAR ( 20 ))
BEGIN
SELECT
*
FROM
book_info bi,
book_type bt
WHERE
bi.type_id = bt.type_id
AND type_name = lx;
END

-- 调用
call myproc2('漫画');
定义输出型参数的存储过程

调用输出型参数,使用@变量,接收存储过程的调用结果

create procedure 存储过程名(out 形参名 数据类型)
begin
sql语句;
end
-- 根据作者查询其出版图书数量
drop PROCEDURE myproc3;
CREATE PROCEDURE myproc3 (OUT book_count INT,IN zz VARCHAR ( 20 ))
BEGIN
SELECT
-- 将查询到的结果使用into赋值给输出型参数book_count中
count( book_id ) INTO book_count
FROM
book_info
WHERE
book_author = zz;
END
-- 调用输出型参数,使用@变量,接受存储过程的调用结果
call myproc3(@x,'金庸');
select @x;
删除存储过程

drop procedure 存储过程名;

drop PROCEDURE myproc3;

MySQL编程

在定义的存储过程中,可以使用变量、流程控制语句等

变量
  • 定义变量

    declare 变量名 数据类型
    
  • 给变量赋值

    -- 方法一
    selectinto 变量
    -- 方法二
    select 字段 into 变量 from
  • 读取变量值

    select 变量
    
定义变量

declare 变量名 数据类型

create procedure 存储过程名()
begin
-- declare 变量名 数据类型;
declare num int;
declare name varchar(20);
end
给变量赋值
create procedure 存储过程名()
begin
-- declare 变量名 数据类型;
declare num int;
declare name varchar(20);
-- 给num和name赋值
-- select 值/字段 into 变量 [from 表];
select 123 into num;
select book_name into name from book_info where id=123;
end
读取变量的值
create procedure 存储过程名()
begin
-- declare 变量名 数据类型;
declare num int;
declare name varchar(20);
-- 给num和name赋值
-- select 值/字段 into 变量 [from 表];
select 123 into num;
select book_name into name from book_info where id=123;
-- select 变量名
select num;
select name;
end
练习
-- 创建存储过程,查询所有图书库存总数,保存到变量中
CREATE PROCEDURE myproc4 ()
BEGIN
-- 定义变量
DECLARE num INT;
-- 查询,将结果赋值给变量
SELECT
sum( book_num ) INTO num
FROM
book_info;
-- 读取变量的值
SELECT
num;
END
call myproc4()
条件语句
  • 单分支if语句

    if 条件 then 满足条件时执行的sql;
    end if;
    
  • 双分支if语句

    if 条件 then 满足条件时执行的sql;
    else 不满足条件时执行的sql;
    end if;
    
  • case语句

    case 变量
    whenthen
    变量为该值时执行的sql语句;
    whenthen
    变量为该值时执行的sql语句;
    else
    没有任何值满足时执行的sql语句;
    end case;
    
单分支if语句
if 条件
then
满足条件时执行的sqlend if;

-- 根据作者查询图书库存,如果不足100,输出'库存不足'
create PROCEDURE myproc5(in zz varchar(20))
begin

-- 定义变量保存最终的结果
declare num int;
-- 核心sql
select sum(book_num) into num from book_info where book_author=zz;
-- 判断
if num<100
then
select '库存不足';
end if;
end
call myproc5('鸟山明')
双分支if语句
if 条件
then
满足条件时执行的sqlelse
不满足条件时执行的sqlend if;
-- 根据图书类型查询图书数量,如果不足5,输出'不足5中',如果足够,输出详情
create PROCEDURE myproc6(in lx varchar(20))
begin
-- 定义变量保存图书数量
declare num int;
-- 执行查询
SELECT
count( book_id ) INTO num
FROM
book_info bi,
book_type bt
WHERE
bi.type_id = bt.type_id
AND type_name = lx;
-- 双分支判断
if num<5
then
select '不足5种';
else
SELECT
*
FROM
book_info bi,
book_type bt
WHERE
bi.type_id = bt.type_id
AND type_name = lx;
end if;
end

case语句
case 变量
whenthen
变量为该值时执行的sql语句;
whenthen
变量为该值时执行的sql语句;
else
没有任何值满足时执行的sql语句;
end case;
-- 输入类型编号,输出对应的类型名称,没有输出不存在
create procedure myproc7(in id int)
begin
declare type varchar(20) ;
select type_name into type from book_type where type_id=id;
case type
when '小说' then
select concat(id,'--',type);
when '漫画' then
select concat(id,'--',type);
when '杂志' then
select concat(id,'--',type);
when '传记' then
select concat(id,'--',type);
when '教材' then
select concat(id,'--',type);
else
select concat(id,'不存在');
end case;
end
call myproc7(4)
call myproc7(9)
循环语句
  • while循环

    while 循环条件 do
    循环体;
    end while;
    
  • repeat循环

    repeat
    循环体;
    unitl 条件 end repeat;
    
  • loop循环

    循环名:loop
    循环体;
    if 循环条件 then
    leave 循环名;
    end if;
    end loop 循环名;
    
while循环
while 循环条件 do
循环体;
end while;
-- 添加50本图书
create PROCEDURE myproc8()
begin
-- 定义循环变量
declare n int;
-- 循环变量定义初始值
select 1 into n;
-- 循环条件
while n<=50 do
-- 循环内容
insert into book_info values(null,concat('书名',n),'作者',now(),50,1,100);
-- 更新循环变量
set n = n+1;
end while;
end
call myproc8()
repeat循环
repeat
循环体;
unitl 条件 end repeat;
-- 添加50本图书
create PROCEDURE myproc9()
begin
-- 定义循环变量
declare n int;
-- 循环变量定义初始值
select 51 into n;
-- 循环条件
repeat
-- 循环内容
insert into book_info values(null,concat('书名',n),'作者',now(),50,1,100);
-- 更新循环变量
set n = n+1;
until n=100 end repeat;
end
call myproc9()
loop循环
循环名:loop
循环体;
if 循环条件 then
leave 循环名;
end if;
end loop 循环名;
-- 添加50本图书
create PROCEDURE myproc10()
begin
-- 定义循环变量
declare n int;
-- 循环变量定义初始值
select 100 into n;
-- 循环条件
test:loop
-- 循环内容
insert into book_info values(null,concat('书名',n),'作者',now(),50,1,100);
-- 更新循环变量
set n = n+1;
if n=151 then
leave test;
end if;
end loop test;
end
call myproc10()

综合练习-“简易商场系统”

image-20230909114357712

until工具包
//DBUntil
package com.shopping.jdbc_plus.util;

import java.sql.*;

public class DBUtil {
    /*
     * 数据库工具类
     * 用于提取JDBC连接数据库时的公共代码
     * //公共代码:
     * //1.加载驱动
     * //2.获取连接
     * //3.释放资源
     * */

    //加载驱动,一次即可,放入静态代码块
    //Class.forname("com.mysql.cj.jdbc.Driver")
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("加载驱动异常"+e);
        }
    }
    //获取连接数据库
    //url、username,password
//url="jdbc:mysql://localhost:3306/数据库名?serverTimezone=Asia/Shanghai"
    static final String URL="jdbc:mysql://localhost:3306/db_shopping?serverTimezone=Asia/Shanghai";
    static final String USERNAME="root";
    static final String PASSWORD="root";

    public static Connection getConnection(){
        Connection conn=null;
        try {
            conn= DriverManager.getConnection(URL,USERNAME,PASSWORD);
        } catch (SQLException e) {
            System.out.println("连接数据库异常"+e);
        }
        return conn;
    }
    //释放资源
    public static void release(Connection conn, PreparedStatement pst, ResultSet rst){
        try {
            if(conn!=null)
                conn.close();
            if(pst!=null)
                conn.close();
            if(rst!=null)
                conn.close();
        }catch (SQLException e) {
            System.out.println("释放资源异常"+e);
        }
    }

}
entity
AdminInfo
package com.shopping.jdbc_plus.entity;

public class AdminInfo {
    private int aId;
    private String aUsername;
    private String aPassword;

    public AdminInfo() {
    }

    public AdminInfo(int aId, String aUsername, String aPassword) {
        this.aId = aId;
        this.aUsername = aUsername;
        this.aPassword = aPassword;
    }

    @Override
    public String toString() {
        return "admin{" +
            "aId=" + aId +
            ", aUsername='" + aUsername + '\'' +
            ", aPassword='" + aPassword + '\'' +
            '}';
    }

    public int getaId() {
        return aId;
    }

    public void setaId(int aId) {
        this.aId = aId;
    }

    public String getaUsername() {
        return aUsername;
    }

    public void setaUsername(String aUsername) {
        this.aUsername = aUsername;
    }

    public String getaPassword() {
        return aPassword;
    }

    public void setaPassword(String aPassword) {
        this.aPassword = aPassword;
    }
}

ClientInfo
package com.shopping.jdbc_plus.entity;

public class ClientInfo {
    private int cId;
    private String cPhone;
    private String cPassword;
    private String cName;
    private double cAmount;
    private String cAddress;

    public ClientInfo() {
    }

    public ClientInfo(int cId, String cPhone, String cPassword, String cName, double cAmount, String cAddress) {
        this.cId = cId;
        this.cPhone = cPhone;
        this.cPassword = cPassword;
        this.cName = cName;
        this.cAmount = cAmount;
        this.cAddress = cAddress;
    }

    @Override
    public String toString() {
        return "client{" +
            "cId=" + cId +
            ", cPhone='" + cPhone + '\'' +
            ", cPassword='" + cPassword + '\'' +
            ", cName='" + cName + '\'' +
            ", cAmount=" + cAmount +
            ", cAddress='" + cAddress + '\'' +
            '}';
    }

    public int getcId() {
        return cId;
    }

    public void setcId(int cId) {
        this.cId = cId;
    }

    public String getcPhone() {
        return cPhone;
    }

    public void setcPhone(String cPhone) {
        this.cPhone = cPhone;
    }

    public String getcPassword() {
        return cPassword;
    }

    public void setcPassword(String cPassword) {
        this.cPassword = cPassword;
    }

    public String getcName() {
        return cName;
    }

    public void setcName(String cName) {
        this.cName = cName;
    }

    public double getcAmount() {
        return cAmount;
    }

    public void setcAmount(double cAmount) {
        this.cAmount = cAmount;
    }

    public String getcAddress() {
        return cAddress;
    }

    public void setcAddress(String cAddress) {
        this.cAddress = cAddress;
    }
}

ProductInfo
package com.shopping.jdbc_plus.entity;

public class ProductInfo {
    private int pId;
    private String pName;
    private double pPrice;
    private int pNum;

    public ProductInfo() {
    }

    public ProductInfo(int pId, String pName, double pPrice, int pNum) {
        this.pId = pId;
        this.pName = pName;
        this.pPrice = pPrice;
        this.pNum = pNum;
    }

    @Override
    public String toString() {
        return "client{" +
            "pId=" + pId +
            ", pName='" + pName + '\'' +
            ", pPrice=" + pPrice +
            ", pNum=" + pNum +
            '}';
    }

    public int getpId() {
        return pId;
    }

    public void setpId(int pId) {
        this.pId = pId;
    }

    public String getpName() {
        return pName;
    }

    public void setpName(String pName) {
        this.pName = pName;
    }

    public double getpPrice() {
        return pPrice;
    }

    public void setpPrice(double pPrice) {
        this.pPrice = pPrice;
    }

    public int getpNum() {
        return pNum;
    }

    public void setpNum(int pNum) {
        this.pNum = pNum;
    }
}
dao
AdminInfoDao
package com.shopping.jdbc_plus.dao;

import com.shopping.jdbc_plus.entity.AdminInfo;
import com.shopping.jdbc_plus.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/*
 * dao层表示数据库访问层
 * 该层中的类都是用于操作数据库
 * 类名通常命名为:实体类Dao
 * */
public class AdminInfoDao {
    //定义操作数据库时所需要的接口
    private Connection conn;
    private PreparedStatement pst;
    private ResultSet rst;
    //一个方法、加载驱动、连接、构造sql语句,对其预处理

    //验证登录
    public AdminInfo loginAdmin(AdminInfo ad){
        //加载驱动、连接数据
        conn= DBUtil.getConnection();
        //构造sql语句
        try {
            pst= conn.prepareStatement("select * from admin where a_username=? and a_password=?");
            pst.setString(1,ad.getaUsername());
            pst.setString(2,ad.getaPassword());
            rst=pst.executeQuery();
            if(rst.next()){
                ad.setaId(rst.getInt(1));
                return ad;
            }
        } catch (SQLException e) {
            System.out.println("管理员验证登录异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return null;
    }
}
ClientInfoDao
package com.shopping.jdbc_plus.dao;

import com.shopping.jdbc_plus.entity.ClientInfo;
import com.shopping.jdbc_plus.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ClientInfoDao {
    Connection conn;
    PreparedStatement pst;
    ResultSet rst;

    //用户查询
    public ClientInfo findByPhone(ClientInfo c){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("select * from client where c_phone=?");
            pst.setString(1,c.getcName());
            ResultSet rst= pst.executeQuery();
            if(rst.next()){
                c.setcId(rst.getInt(1));
                c.setcPhone(rst.getString(2));
                c.setcPassword(rst.getString(3));
                c.setcName(rst.getString(4));
                c.setcAmount(rst.getDouble(5));
                c.setcAddress(rst.getString(6));
                return c;
            }
        } catch (SQLException e) {
            System.out.println("用户查询异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return null;
    }
    //用户注册
    public boolean signClient(ClientInfo c){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("insert into client values (null,?,?,concat('用户',REPLACE(?,SUBSTR(?,4,4),'****')),default ,default)");
            pst.setString(1,c.getcPhone());
            pst.setString(2,c.getcPassword());
            pst.setString(3,c.getcPhone());
            pst.setString(4,c.getcPhone());
            return pst.executeUpdate()>0;
        } catch (SQLException e) {
            System.out.println("用户查询注册异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return false;
    }

    //用户登录
    public ClientInfo loginClient(ClientInfo c){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("select * from client where c_phone=? and c_password=? ");
            pst.setString(1,c.getcPhone());
            pst.setString(2,c.getcPassword());
            ResultSet rst= pst.executeQuery();
            if (rst.next()){
                c.setcId(rst.getInt(1));
                c.setcName(rst.getString(4));
                c.setcAmount(rst.getDouble(5));
                c.setcAddress(rst.getString(6));
                return c;
            }
        } catch (SQLException e) {
            System.out.println("用户登录异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return null;
    }

    //用户修改字段
    public boolean updateClient(ClientInfo c){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("update client set c_phone=?,c_password=?,c_name=?,c_amount=?,c_address=? where c_id=? ");
            pst.setString(1,c.getcPhone());
            pst.setString(2,c.getcPassword());
            pst.setString(3,c.getcName());
            pst.setDouble(4,c.getcAmount());
            pst.setString(5,c.getcAddress());
            pst.setInt(6,c.getcId());
            return pst.executeUpdate()>0;
        } catch (SQLException e) {
            System.out.println("用户修改异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return false;
    }
}
ProductInfoDao
package com.shopping.jdbc_plus.dao;

import com.shopping.jdbc_plus.entity.ProductInfo;
import com.shopping.jdbc_plus.util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ProductInfoDao {
    Connection conn;
    PreparedStatement pst;
    ResultSet rst;

    //查询所有商品
    public List<ProductInfo> QueryAll(){
        List<ProductInfo> list=new ArrayList<>();
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("select * from product");
            ResultSet rst= pst.executeQuery();
            while (rst.next()){
                ProductInfo p=new ProductInfo(rst.getInt(1),rst.getString(2),rst.getDouble(3),rst.getInt(4));
                list.add(p);
            }
        } catch (SQLException e) {
            System.out.println("用户查询异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return list;
    }

    //利用id查询商品
    public ProductInfo findById(ProductInfo p){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("select * from product where p_id=?");
            pst.setInt(1,p.getpId());
            ResultSet rst= pst.executeQuery();
            if(rst.next()){
                p.setpId(rst.getInt(1));
                p.setpName(rst.getString(2));
                p.setpPrice(rst.getDouble(3));
                p.setpNum(rst.getInt(4));
                return p;
            }
        } catch (SQLException e) {
            System.out.println("用户查询id找商品异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return null;
    }

    //增加商品
    public boolean addProduct(ProductInfo p){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("insert into product values(null,?,?,?)");
            pst.setString(1,p.getpName());
            pst.setDouble(2,p.getpPrice());
            pst.setInt(3,p.getpNum());
            return pst.executeUpdate()>0;
        } catch (SQLException e) {
            System.out.println("用户增加商品异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return false;
    }

    //根据id修改商品
    public boolean updateProduct(ProductInfo p){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("update product set p_name=?,p_price=?,p_num=? where p_id=? ");
            pst.setString(1,p.getpName());
            pst.setDouble(2,p.getpPrice());
            pst.setInt(3,p.getpNum());
            pst.setInt(4,p.getpId());
            return pst.executeUpdate()>0;
        } catch (SQLException e) {
            System.out.println("用户修改商品异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return false;
    }

    //根据id删除商品
    public boolean delProduct(ProductInfo p){
        conn= DBUtil.getConnection();
        try {
            pst= conn.prepareStatement("delete from product where p_id=? ");
            pst.setInt(1,p.getpId());
            return pst.executeUpdate()>0;
        } catch (SQLException e) {
            System.out.println("用户删除商品异常"+e);
        }finally {
            DBUtil.release(conn,pst,rst);
        }
        return false;
    }
}
service

Main

package com.shopping.jdbc_plus.service;

import com.shopping.jdbc_plus.dao.AdminInfoDao;
import com.shopping.jdbc_plus.dao.ClientInfoDao;
import com.shopping.jdbc_plus.dao.ProductInfoDao;
import com.shopping.jdbc_plus.entity.AdminInfo;
import com.shopping.jdbc_plus.entity.ClientInfo;
import com.shopping.jdbc_plus.entity.ProductInfo;

import java.util.List;
import java.util.Scanner;

public class Main {
    Scanner scan=new Scanner(System.in);
    AdminInfoDao ad=new AdminInfoDao();
    ClientInfoDao cl=new ClientInfoDao();
    ProductInfoDao pd=new ProductInfoDao();
    void loginMenu() {
        System.out.println("请选择登录方式:1为管理员,2为用户,3为退出");
        int i = scan.nextInt();
        switch (i) {
            case 1:
                AdminInfo a=new AdminInfo();

                //错误
                /*a.setaUsername("root");
                a.setaPassword("12345");*/

                //正确
                a.setaUsername("root");
                a.setaPassword("123456");
                if(ad.loginAdmin(a)!=null){
                    System.out.println("登录成功");
                    adminMenu();
                }else
                    System.out.println("用户名或密码错误");
                break;
            case 2:
                cLoginMenu();

                break;
            case 3:
                System.exit(0);
            default:
                System.out.println("方式不可识别,请重新选择");
        }
        loginMenu();
    }
    void adminMenu(){
        ProductInfo p;
        System.out.println("管理员操作菜单:1为查看所有商品,2为添加新商品,3为修改某个商品,4为删除某个商品 5为返回");
        int i = scan.nextInt();
        switch (i){
            case 1:
                List<ProductInfo> list=pd.QueryAll();
                System.out.println("查看所有商品");
                list.forEach(a->System.out.println(a));
                break;
            case 2:
                p=new ProductInfo();
                p.setpName("螺蛳粉");
                p.setpPrice(12.4);
                p.setpNum(215);
                if(pd.addProduct(p))
                    System.out.println("添加新产品成功");
                else
                    System.out.println("添加新产品失败");
                break;
            case 3:
                p=new ProductInfo();
                p.setpId(5);
                if(pd.findById(p)!=null){
                    p.setpNum(1000);
                    p.setpPrice(50);
                    if(pd.updateProduct(p))
                        System.out.println("商品修改成功");
                    else
                        System.out.println("商品修改失败");
                }
                else
                    System.out.println("该商品不存在,无法修改");
                break;
            case 4:
                p=new ProductInfo();
                p.setpId(7);
                if(pd.findById(p)!=null){
                    if(pd.delProduct(p))
                        System.out.println("商品删除成功");
                    else
                        System.out.println("商品删除失败");
                }
                else
                    System.out.println("该商品不存在,无法删除");
                break;
            case 5:
                loginMenu();
                break;
        }
        adminMenu();
    }
    void cLoginMenu(){
        ClientInfo c;
        System.out.println("请选择操作:1为用户登录,2为用户注册,3为返回");
        int i = scan.nextInt();
        switch (i){
            case 1:
                c=new ClientInfo();
                //错误
                /*c.setcPhone("11165432112");
                c.setcPassword("12345675");*/

                //正确
                c.setcPhone("98765432112");
                c.setcPassword("12345678");
                if(cl.loginClient(c)!=null){
                    System.out.println("登录成功");
                    clientMenu(c);
                }
                else
                    System.out.println("用户名或密码错误");
                break;
            case 2:
                c=new ClientInfo();
                //错误
                /*c.setcPhone("98765432112");
                c.setcPassword("12345678");*/
                //正确
                c.setcPhone("21323432112");
                c.setcPassword("12345678");

                if (cl.findByPhone(c)==null){
                    if (cl.signClient(c)){
                        System.out.println("注册成功");
                        if(cl.loginClient(c)!=null){
                            System.out.println("自动登录!!");
                            clientMenu(c);
                        }
                    }
                }else {
                    System.out.println("该用户已注册");
                }
                break;
            case 3:
                loginMenu();
                break;
        }
        cLoginMenu();
    }
    void clientMenu(ClientInfo c){
        ProductInfo p;
        System.out.println("客户操作菜单:1为查看所有商品,2为查看某个商品,3为购买某个商品,4为充值余额,5为查看个人信息,6为返回");
        int i = scan.nextInt();
        switch (i){
            case 1:
                System.out.println("");
                List<ProductInfo> list=pd.QueryAll();
                System.out.println("查看所有商品");
                list.forEach(a->System.out.println(a));
                break;
            case 2:
                p=new ProductInfo();
                p.setpId(12);
                if(pd.findById(p)!=null)
                    System.out.println(p);
                else
                    System.out.println("该商品未查找到");
                break;
            case 3:
                System.out.println("购买");
                int count=10;
                p=new ProductInfo();
                p.setpId(11);
                if(pd.findById(p)==null)
                    System.out.println("该商品不存在");
                else if(p.getpNum()<count)
                    System.out.println("该商品库存不足");
                else if(c.getcAmount()<count*p.getpPrice())
                    System.out.println("该用户余额不足");
                else {
                    p.setpNum(p.getpNum()-count);
                    c.setcAmount(c.getcAmount()-count*p.getpPrice());
                    if(pd.updateProduct(p)&&cl.updateClient(c)){
                        System.out.println("商品名:" + p.getpName());
                        System.out.println("购买数量:" + count);
                        System.out.println("订单金额:" + count * p.getpPrice());
                        System.out.println("购买成功");
                    }
                    else
                        System.out.println("购买失败");

                }
                break;
            case 4:
                c.setcAmount(c.getcAmount()+5000);
                if(cl.updateClient(c))
                    System.out.println("充值成功");
                else
                    System.out.println("充值失败");
                break;
            case 5:
                System.out.println(c);
                break;
            case 6:
                cLoginMenu();
                break;
        }
        clientMenu(c);
    }

    public static void main(String[] args) {
        Main m=new Main();
        m.loginMenu();
    }
}

112");
c.setcPassword(“12345678”);

            if (cl.findByPhone(c)==null){
                if (cl.signClient(c)){
                    System.out.println("注册成功");
                    if(cl.loginClient(c)!=null){
                        System.out.println("自动登录!!");
                        clientMenu(c);
                    }
                }
            }else {
                System.out.println("该用户已注册");
            }
            break;
        case 3:
            loginMenu();
            break;
    }
    cLoginMenu();
}
void clientMenu(ClientInfo c){
    ProductInfo p;
    System.out.println("客户操作菜单:1为查看所有商品,2为查看某个商品,3为购买某个商品,4为充值余额,5为查看个人信息,6为返回");
    int i = scan.nextInt();
    switch (i){
        case 1:
            System.out.println("");
            List<ProductInfo> list=pd.QueryAll();
            System.out.println("查看所有商品");
            list.forEach(a->System.out.println(a));
            break;
        case 2:
            p=new ProductInfo();
            p.setpId(12);
            if(pd.findById(p)!=null)
                System.out.println(p);
            else
                System.out.println("该商品未查找到");
            break;
        case 3:
            System.out.println("购买");
            int count=10;
            p=new ProductInfo();
            p.setpId(11);
            if(pd.findById(p)==null)
                System.out.println("该商品不存在");
            else if(p.getpNum()<count)
                System.out.println("该商品库存不足");
            else if(c.getcAmount()<count*p.getpPrice())
                System.out.println("该用户余额不足");
            else {
                p.setpNum(p.getpNum()-count);
                c.setcAmount(c.getcAmount()-count*p.getpPrice());
                if(pd.updateProduct(p)&&cl.updateClient(c)){
                    System.out.println("商品名:" + p.getpName());
                    System.out.println("购买数量:" + count);
                    System.out.println("订单金额:" + count * p.getpPrice());
                    System.out.println("购买成功");
                }
                else
                    System.out.println("购买失败");

            }
            break;
        case 4:
            c.setcAmount(c.getcAmount()+5000);
            if(cl.updateClient(c))
                System.out.println("充值成功");
            else
                System.out.println("充值失败");
            break;
        case 5:
            System.out.println(c);
            break;
        case 6:
            cLoginMenu();
            break;
    }
    clientMenu(c);
}

public static void main(String[] args) {
    Main m=new Main();
    m.loginMenu();
}

}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值