JDBC&XML

JDBC

JDBC概述

客户端操作数据库的方式

1、方式1: 使用第三方客户端来访问 MySQL:SQLyog

2、方式2: 使用命令行

3、通过 Java程序 来访问 MySQL 数据库(JDBC)

JDBC概念

​ JDBC(Java Data Base Connectivity) 是 Java 访问数据库的标准规范。

​ 是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。

JDBC原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWcCQg5i-1621589561592)(E:\MarkDown\拉勾笔记\JDBC原理)]

​ JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库驱动jar包,我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。

JDBC开发

数据准备
-- 创建 jdbc_user表
CREATE TABLE jdbc_user (
id INT PRIMARY KEY AUTO_INCREMENT ,
username VARCHAR(50),
PASSWORD VARCHAR(50),
birthday DATE
);
-- 添加数据
INSERT INTO jdbc_user (username, PASSWORD,birthday)
VALUES('admin1', '123','1991/12/24'),
('admin2','123','1995/12/24'),
('test1', '123','1998/12/24'),
('test2', '123','2000/12/24');
MySQL驱动包

1、将MySQL驱动包添加到jar包库文件夹中,myJar文件夹,用于存放当前项目需要的所有jar包(E:\myJar)

2、在idea中配置jar包库的位置

API使用: 1.注册驱动

1、JDBC规范定义驱动接口:java.sql.Driver

2、MySql驱动包提供了实现类:com.mysql.jdbc.Driver

3、加载注册驱动的方式:Class.forName(数据库驱动实现类)——加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供–“com.mysql.jdbc.Driver”

public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {

        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");


    }
}

4、 为什么可以使用Class.forName方式注册驱动?

// Driver类是由MySql驱动包提供的一个实现类,它实现了java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    // 静态代码块,随着类的加载而加载,只加载一次
    static {
        try {
            // DriverManager类就是驱动管理类
            // registerDriver()方法就是用来注册驱动的
            
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

5、注意

​ 从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。 Class.forName 这句话可以省略。

API使用: 2.获得连接

1、Connection接口,代表一个连接对象,具体的实现类由数据库的厂商实现

2、使用DriverManager类的静态方法,getConnection可以获取数据库的连接

获取连接的静态方法说明
Connection getConnection(String url, String user, String password)通过连接字符串和用户名,密码来获取数据库连接对象

a、getConnection方法3个连接参数说明

连接参数说明
user登录用户名
password登录密码
urlmySql URL的格式 jdbc:mysql://localhost:3306/db4

b、对URL的详细说明

jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8

JDBC规定url的格式由三部分组成,每个部分中间使用冒号分隔。

​ 第一部分:协议 jdbc,这是固定的

​ 第二部分:子协议,就是数据库名称,连接mysql数据库

​ 第三部分:由数据库厂商规定,我们需要了解每个数据库厂商的要求,mysql的第三部分由数据库服务器的IP地址(localhost)、端口号(3306),以及要使用的数据库名称组成

public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {

        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.获取连接 url 用户名 密码
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding = UTF-8";
        Connection con = DriverManager.getConnection(url, "root", "123456");

        // 打印连接对象   com.mysql.jdbc.JDBC4Connection@56cbfb61
        System.out.println(con);
    }
}
API 使用: 3.获取语句执行平台

1、通过Connection 的createStatement方法获取sql语句执行对象

Connection接口中的方法说明
Statement createStatement()创建SQL语句执行对象

2、Statement : 代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象

Statement类 常用方法说明
int executeUpdate(String sql);执行insert update delete语句。返回int类型,代表受影响的行数
ResultSet executeQuery(String sql);执行select语句,返回ResultSet结果集对象
public class JDBCDemo01 {

    public static void main(String[] args) throws Exception {

        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.获取连接 url 用户名 密码
        String url = "jdbc:mysql://localhost:3306/db4?characterEncoding = UTF-8";
        Connection con = DriverManager.getConnection(url, "root", "123456");

        // 打印连接对象   com.mysql.jdbc.JDBC4Connection@56cbfb61
        //System.out.println(con);

        // 3.获取语句执行平台 Statement
        Statement statement = con.createStatement();

        // 3.1.通过Statement对象的executeUpdate方法创建一张表
        String sql = "create table test(id int, name varchar(20), age int);";
        int i = statement.executeUpdate(sql);   // 返回值是int类型表示受影响的行数

        System.out.println(i);

        // 4.关闭流
        statement.close();
        con.close();

    }
}
API 使用: 4.处理结果集

只有在进行查询操作的时候, 才会处理结果集

ResultSet接口

作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。

ResultSet接口方法说明
boolean next()1) 判断是否有下一条数据,游标向下一行
2) 返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false
xxx getXxx( String or int)1) 通过列名,参数是 String 类型。返回不同的类型
2) 通过列号,参数是int类型,从 1 开始。返回不同的类型

int getInt(“列名”);

int getInt(1);——表示列号

String getString(“列名”);

String getString(1);——1表示列号

public class JDBCDemo02 {

    public static void main(String[] args) throws Exception {

        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.获取连接
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db4", "root", "123456");

        // 3.获取语句执行平台 Statement
        Statement statement = con.createStatement();

        // 4.执行查询操作
        // 4.1使用executeQuery()
        String sql = "select * from jdbc_user";

        // resultSet是结果集对象
        ResultSet resultSet = statement.executeQuery(sql);


        // 通过where循环 遍历获取resultSet中的数据
        while(resultSet.next()){
            // 获取id
            int id = resultSet.getInt("id");

            // 获取姓名
            String username = resultSet.getString("username");

            // 获取密码
            String password = resultSet.getString("password");

            // 获取生日
            Date birthday = resultSet.getDate("birthday");

            System.out.println(id + " : " + username + " : " + password + " : " + birthday);
        }

        // 5.关闭流
        resultSet.close();
        statement.close();
        con.close();
    }
}

        // 4.2处理结果集对象
//        boolean next = resultSet.next();    // 判断是否有下一条数据
//        System.out.println(next);

            // 获取id
//        int id = resultSet.getInt("id");
//        System.out.println("通过列名获取的id为:" + id);
//        int anInt = resultSet.getInt(1);
//        System.out.println("通过列号获取的id为:" + id);
API 使用: 5.释放资源

1、需要释放的对象:ResultSet 结果集(只有查询会用到),Statement 语句,Connection 连接
2、释放原则:先开后关,后开先关。ResultSet ==> Statement ==> Connection
3、放在哪个代码块中:finally 块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hq5AxCxb-1621589561599)(E:\MarkDown\拉勾笔记\JDBC释放资源)]

步骤总结

1、获取驱动(可以省略)

2、获取连接

3、获取Statement对象

4、处理结果集(只在查询时处理)

5、释放资源

public class JDBCDemo03 {

    public static void main(String[] args) {
        
        Connection con = null;
        Statement statement = null;
        ResultSet resultSet = null;
        String sql = "jdbc:mysql://localhost:3306/db4";
        try {
            // 1.注册驱动 省略
            // 2.获取连接
            con = DriverManager.getConnection(sql, "root", "123456");
            
            // 3.获取语句执行对象
            statement = con.createStatement();
            
            // 4.执行sql
            String sql1 = "select * from jdbc_user";
            resultSet = statement.executeQuery(sql1);
            
            // 5.处理结果集对象
            

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // finally 中的代码始终会执行
            // 关闭资源
            try {
                resultSet.close();
                statement.close();
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}

JDBC实现增删改查

JDBC工具类

1、什么时候自己创建工具类?
a、如果一个功能经常要用到,建议把这个功能做成一个工具类,可以在不同的地方重用。
b、“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。

2、工具类包含的内容

​ a、可以把几个字符串定义成常量:用户名,密码,URL,驱动类
​ b、得到数据库的连接:getConnection()
​ c、关闭所有打开的资源:

public class JDBCUtils {

    // 1.将连接信息定义为 字符串常量
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://localhost:3306/db4?characterEncoding=UTF-8";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    // 2.编写静态代码块
    static{
        try {
            // 1.注册驱动
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 3.获取连接的 静态方法
    public static Connection getConnection(){
        // 获取连接对象并返回
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(URL, USER, PASSWORD);
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 4.关闭资源的方法
    public static void close(Connection con, Statement statement){
        if (con != null && statement != null){
            try {
                statement.close();
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){
        if (con != null && statement != null){
            try {
                statement.close();
                con.close();
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
DML操作
public class TestDML {

    /**
     * 插入数据
     */
    @Test
    public void testInsert() throws SQLException {

        // 1.通过JDBCUtils工具类 获取连接
        Connection con = JDBCUtils.getConnection();

        // 2.获取Statement 对象
        Statement statement = con.createStatement();

        // 2.1编写SQL
        String sql = "insert into jdbc_user values(null,'张百万','123','2020/11/11')";

        // 2.2执行SQL
        int i = statement.executeUpdate(sql);
        System.out.println(i);

        // 3.关闭流
        JDBCUtils.close(con,statement);
    }

    /**
     * 更新操作
     * 根据id修改用户名
     */
    @Test
    public void testUpdate() throws SQLException {

        Connection connection = JDBCUtils.getConnection();

        Statement statement = connection.createStatement();

        String sql = "update jdbc_user set username = '刘能' where id = 1";

        statement.executeUpdate(sql);

        JDBCUtils.close(connection,statement);
    }

    /**
     * 删除操作
     * 删除id 为1 和 2的数据
     */
    @Test
    public void testDelete() throws SQLException {

        Connection connection = JDBCUtils.getConnection();

        Statement statement = connection.createStatement();

        String sql = "delete from jdbc_user where id in(1,2)";

        statement.executeUpdate(sql);

        JDBCUtils.close(connection,statement);
    }
}
DQL操作
public class TestDQL {

    // 查询姓名为张百万的一条记录
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = JDBCUtils.getConnection();

        // 2.创建Statement 对象
        Statement statement = con.createStatement();

        // 3.编写SQL
        String sql = "select * from jdbc_user where username = '张百万'";

        ResultSet resultSet = statement.executeQuery(sql);

        // 4.处理结果集
        while(resultSet.next()){
            // 通过列名方式获取
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            Date birthday = resultSet.getDate("birthday");

            System.out.println(id + ":" + username + ":" + password + ":" + birthday);
        }

        // 5.是否资源
        JDBCUtils.close(con,statement,resultSet);
    }
}

SQL注入问题

SQL注入演示
# 插入2条数据
INSERT INTO jdbc_user VALUES(NULL,'jack','123456','2020/2/24');
INSERT INTO jdbc_user VALUES(NULL,'tom','123456','2020/2/24');

-- 查询用户操作
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123456';

-- SQL注入问题演示
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';
sql注入案例:用户登陆
/**
 * @auther weiwei
 * @date 2021/5/17 16:10
 * @description     用户登陆案例
 */
public class TestLogin01 {

    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = JDBCUtils.getConnection();

        // 2.创建Statement 对象
        Statement statement = con.createStatement();

        // 3.获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();

        // 4.拼接SQL语句
        String sql = "select * from jdbc_user where username = '" + name +"' and password = '" + password + "'";
        System.out.println(sql);

        // 5.执行查询 获取结果集对象
        ResultSet resultSet = statement.executeQuery(sql);

        // 6.处理结果集
        if (resultSet.next()){
            System.out.println("登陆成功!欢迎您:" + name);
        } else {
            System.out.println("登陆失败!");
        }

        // 关闭流
        JDBCUtils.close(con,statement,resultSet);

    }
}

// SQL注入结果
请输入用户名:
zbc
请输入密码:
zbc' or '1' = '1
select * from jdbc_user where username = 'zbc' and password = 'zbc'or '1' = '1'
登陆成功!欢迎您:zbc
问题分析

1、什么是SQL注入?

​ 我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正意义,即为SQL注入。

2、如何实现注入

根据用户输入的数据,拼接处的字符串

select * from jdbc_user where username = 'zbc' and password = 'zbc'or '1' = '1'

name = 'zbc' and password = 'abc' 为假, '1' = '1'真
相当于 select * from user where true = true; 查询了所有记录

3、要如何解决

不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接

预处理对象

PreparedStatement 接口介绍

1、PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象

2、预编译: 是指SQL 语句被预编译,并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句

PreparedStatement 特点

1、因为有预先编译的功能,提高SQL的执行效率。
2、可以有效的防止SQL注入的问题,安全性更高

获取PreparedStatement对象

通过Connection创建PreparedStatement对象

Connection 接口中的方法说明
PreparedStatement prepareStatement(String sql)指定预编译的 SQL 语句,SQL 语句中使用占位符 ? 创建一个语句对象
PreparedStatement接口常用方法
常用方法说明
int executeUpdate();执行insert update delete语句
ResultSet executeQuery();执行select语句。返回结果集对象 Resulet
使用PreparedStatement的步骤

1、 编写SQL语句,未知内容使用?占位:

2、获得 PreparedStatement 对象

3、设置实际参数:setXxx( 占位符的位置, 真实的值

4、执行参数化SQL语句

5、关闭资源

setXxx重载方法说明
void setDouble(int parameterIndex, double x)将指定参数设置为给定 Java double 值
void setInt(int parameterIndex, int x)将指定参数设置为给定 Java int 值
void setString(int parameterIndex, String x)将指定参数设置为给定 Java String 值
void setObject(int parameterIndex, Object x)使用给定对象设置指定参数的值
使用PreparedStatement完成登录案例

用 PreparedStatement 预处理对象,可以有效的避免SQL注入

步骤:

1.获取数据库连接对象

2.编写SQL 使用? 占位符方式

3.获取预处理对象 (预编译对象会将Sql发送给数据库 进行预编译)

4.提示用户输入用户名 & 密码

5.设置实际参数:setXxx(占位符的位置, 真实的值)

6.执行查询获取结果集

7.判断是否查询到数据

8.关闭资源

public class TestLogin02 {

    /**
     * SQL注入
     *   用户输入的用户名和密码 与我们编写的SQL进行了拼接,用户输入的内容成为了SQL语法的一部分,
     *   用户会利用这里的漏洞输入一些其他的字符串,改变SQL原有的意思
     *
     *   如何解决
     *     要解决SQL注入 就不能让用户输入的数据和我们的SQL语句进行直接的拼接
     *
     *  预处理对象 PreparedStatement 是Statement接口的子接口
     *    使用预处理对象 有预编译功能,提高SQL的执行效率
     *    使用预处理对象 通过占位符的方式,设置参数,可以有效防止SQL注入
     * @param args
     */
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = JDBCUtils.getConnection();

        // 2.获取PrepareStatement 预处理对象
        // 使用 ? 占位符的方式来设置参数
        String sql = "select * from jdbc_user where username = ? and password = ?";
        PreparedStatement ps = con.prepareStatement(sql);

        // 3.获取用户输入的用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();

        // 4.设置参数 使用 setXXX(占位符的位置(整数),要设置的值) 的方法设置占位符的参数
        ps.setString(1,name);       // 设置第一个问号值为 name
        ps.setString(2,password);   // 设置第二个问号值为 password

        // 5.执行查询
        ResultSet resultSet = ps.executeQuery();

        // 6.处理结果集
        if (resultSet.next()){
            System.out.println("登陆成功!欢迎您:" + name);
        } else {
            System.out.println("登陆失败!");
        }

        // 7.关闭流
        JDBCUtils.close(con,ps,resultSet);
    }
}
PreparedStatement的执行原理
public class TestPS {

    public static void main(String[] args) throws SQLException {

        Connection connection = JDBCUtils.getConnection();

        // 获取Statement
        // Statement对象每执行一条SQL,就会发送给数据库,数据库先编译再执行。即每次运行都需要编译,效率低
        Statement statement = connection.createStatement();

        // 向数据库插入两条数据
        statement.executeUpdate("insert into jdbc_user values(null,'张三','123456','2021/5/17')");
        statement.executeUpdate("insert into jdbc_user values(null,'李四','654321','2020/5/17')");

        // 获取预处理对象PrepareStatement
        // 预处理对象会将SQL发送给数据库进行一个预编译,然后将预编译的SQL保存起来(缓存),
        // 这样只需要编译一次,当执行多次插入操作时,只需要设置参数就可以了
        PreparedStatement ps = connection.prepareStatement("insert into jdbc_user values(?,?,?,?)");

        // 先插入第一条数据
        ps.setObject(1,null);
        ps.setString(2,"小斌");
        ps.setString(3,"qwer");
        ps.setString(4,"1999/11/11");
        //执行插入
        ps.executeUpdate();

        // 插入第二条数据
        ps.setObject(1,null);
        ps.setString(2,"长海");
        ps.setString(3,"asdf");
        ps.setString(4,"2000/11/11");
        //执行插入
        ps.executeUpdate();

        // 释放资源
        statement.close();
        ps.clearParameters();
        connection.close();
    }
}
Statement 与 PreparedStatement的区别?

1、Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句

2、PrepareStatement是预编译的SQL语句对象,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值

3、PrepareStatement可以减少编译次数提高数据库性能

JDBC控制事务

数据准备
-- 创建账户表
CREATE TABLE account(
    -- 主键
    id INT PRIMARY KEY AUTO_INCREMENT,
    -- 姓名
    NAME VARCHAR(10),
    -- 转账金额
    money DOUBLE
);

-- 添加两个用户
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);
事务相关API

使用 Connection中的方法实现事务管理

方法说明
void setAutoCommit(boolean autoCommit)参数是true或false如果设置为false,表示关闭自动提交,相当于开启事务
void commit()提交事务
void rollback()回滚事务
开发步骤

1、获取连接

2、开启事务

3、获取到 PreparedStatement , 执行两次更新操作

4、正常情况下提交事务

5、出现异常回滚事务

6、最后关闭资源

代码示例
public class TestJDBCTransaction {

    // 使用JDBC操作事务
    public static void main(String[] args) {

        Connection con = null;
        PreparedStatement ps = null;

        try {
            // 1.获取连接
            con = JDBCUtils.getConnection();

            // 2.开启事务
            con.setAutoCommit(false);   // 手动提交事务

            // 3.获取预处理对象,执行SQL(来你猜修改操作)
            // 3.1 tom账户 - 500
            ps = con.prepareStatement("update account set money = money - ? where name = ?");
            ps.setDouble(1,500.0);
            ps.setString(2,"tom");
            ps.executeUpdate();

            // 模拟 tom转账之后出现异常
            //System.out.println(1 / 0);

            // 3.2 Jack账户 + 500
            ps = con.prepareStatement("update account set money = money + ? where name = ?");
            ps.setDouble(1,500.0);
            ps.setString(2,"jack");
            ps.executeUpdate();

            // 4.提交事务(正常情况)
            con.commit();
            System.out.println("转账成功!");

        } catch (SQLException e) {
            e.printStackTrace();
            // 5.出现异常就回滚
            try {
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            // 6.释放资源
            JDBCUtils.close(con,ps);
        }

    }
}

数据库连接池和DBUtils

数据库连接池

连接池介绍

1、连接池概念

​ 实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池。

2、连接池的好处

​ 用池来管理Connection,这样可以重复使用Connection。 当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection归还给池。

JDBC方式与连接池方式

1、普通 JDBC方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jf5XfTFU-1621589561605)(E:\MarkDown\拉勾笔记\普通JDBC方式)]

2、连接池方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5t8BMbiy-1621589561611)(E:\MarkDown\拉勾笔记\连接池方式)]

如何使用数据库连接池

​ Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池:DBCP连接池,C3P0连接池,Druid连接池

数据准备
#创建数据库
CREATE DATABASE db5 CHARACTER SET utf8;

#使用数据库
USE db5;

#创建员工表
CREATE TABLE employee (
	eid INT PRIMARY KEY AUTO_INCREMENT ,
	ename VARCHAR (20), -- 员工姓名
	age INT , -- 员工年龄
	sex VARCHAR (6), -- 员工性别
	salary DOUBLE , -- 薪水
	empdate DATE -- 入职日期
);

#插入数据
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'李清照',22,'女',4000,'2018-11-12');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'林黛玉',20,'女',5000,'2019-03-14');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'杜甫',40,'男',6000,'2020-01-01');
INSERT INTO employee (eid, ename, age, sex, salary, empdate) VALUES(NULL,'李白',25,'男',3000,'2017-10-01');
DBCP连接池

​ DBCP也是一个开源的连接池,是Apache成员之一,在企业开发中也比较常见,tomcat内置的连接池。

创建项目 导入 jar包

1、将两个 jar包添加到 myJar文件夹中

2、 添加myJar库 到项目的依赖中

编写工具类

连接数据库表的工具类,采用DBCP连接池的方式来完成

​ a、Java中提供了一个连接池的规则接口 : DataSource , 它是java中提供的连接池
​ b、在DBCP包中提供了DataSource接口的实现类,我们要用的具体的连接池 BasicDataSource 类

public class DBCPUtils {
    
    // 1.定义常量,保存数据库连接信息
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8";
    public static final String USERNAME = "root";
    public static final String PASSWORD = "123456";
    
    // 2.创建连接池对象(由DBCP提供的实现类)
    public static BasicDataSource dataSource = new BasicDataSource();
    
    // 3.使用静态代码块进行配置
    static {
        dataSource.setDriverClassName(DRIVERNAME);
        dataSource.setUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);
    }
    
    // 4.获取连接的方法
    public static Connection getConnection() throws SQLException {
        
        // 从连接池中获取连接
        Connection connection = dataSource.getConnection();
        return connection;
    }
    
    // 5.释放资源
    public static void close(Connection con, Statement statement){

        if (con != null && statement != null){
            try {
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if (con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
}
测试工具类
public class TestDBCP {

    /**
     * 测试DBCP连接池
     * @param args
     */
    public static void main(String[] args) throws SQLException {

        // 1.从DBCP连接池中拿到连接
        Connection con = DBCPUtils.getConnection();

        // 2.获取Statement对象
        Statement statement = con.createStatement();

        // 3.查询所有员工的姓名
        String sql = "select ename from employee";
        ResultSet resultSet = statement.executeQuery(sql);

        // 4.处理结果集
        while (resultSet.next()){
            String ename = resultSet.getString("ename");
            System.out.println("员工姓名:" + ename);
        }

        // 5.释放资源
        DBCPUtils.close(con,statement,resultSet);

    }
}
常见配置项
属性描述
driverClassName数据库驱动名称
url数据库地址
username用户名
password密码
maxActive最大连接数量
maxIdle最大空闲连接
minIdle最小空闲连接
initialSize初始化连接
C3P0连接池

​ C3P0是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等

导入jar包及配置文件

1、 将jar包复制到myJar文件夹即可,IDEA会自动导入

2、导入配置文件 c3p0-config.xml

c3p0-config.xml 文件名不可更改
直接放到src下,也可以放到到资源文件夹中

3、 在项目下创建一个resource文件夹(专门存放资源文件)

4、选择文件夹,右键将resource文件夹指定为资源文件夹

5、将文件放在resource目录下即可,创建连接池对象的时候会去加载这个配置文件

编写C3P0工具类

C3P0提供的核心工具类,ComboPooledDataSource,如果想使用连接池,就必须创建该类的对象
new ComboPooledDataSource(); 使用默认配置
new ComboPooledDataSource(“mysql”); 使用命名配置

public class C3P0Utils {

    // 1.创建连接池对象 C3P0对DataSource接口的实现类
    // 使用的配置 是配置文件中的默认配置
    //public static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    // 2.使用指定的配置
    public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");

    // 3.获取连接的方法
    public static Connection getConnection() throws SQLException {

        return dataSource.getConnection();
    }

    // 4.释放资源
    public static void close(Connection con, Statement statement){

        if (con != null && statement != null){
            try {
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if (con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}
测试工具类
public class TestC3P0 {

    // 需求:查询姓名为李白的记录
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = C3P0Utils.getConnection();

        // 2.获取预处理对象
        String sql = "select * from employee where ename = ?";
        PreparedStatement ps = con.prepareStatement(sql);

        // 3.设置占位符的值
        ps.setString(1,"李白");
        ResultSet resultSet = ps.executeQuery();

        // 4.处理结果集
        while(resultSet.next()){
            int eid = resultSet.getInt("eid");
            String ename = resultSet.getString("ename");
            int age = resultSet.getInt("age");
            String sex = resultSet.getString("sex");
            double salary = resultSet.getDouble("salary");
            Date date = resultSet.getDate("empdate");

            System.out.println(eid + " " + ename + " " + age + " " + sex + " " + salary + " " + date);
        }

        // 5.释放资源
        C3P0Utils.close(con,ps,resultSet);
    }
}
常见配置
分类属性描述
必须项user
password
driverClass
jdbcUrl
用户名
密码
驱动
路径
基本配置initialPoolSize
maxPoolSize
minPoolSize
maxIdleTime
连接池初始化时创建的连接数,默认值:3
连接池中拥有最大的连接数,默恩值:15
连接池保持的最小连接数。10
连接的最大空闲时间。如果超过这个时间,某个数据库连接还没有被使用,则会断开这个连接,如果为0,则永远不会断开连接。默认值:0
Druid连接池

​ Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。

导入jar包及配置文件

1、导入 jar包

2、导入配置文件
是properties形式的
可以叫任意名称,可以放在任意目录下,我们统一放到 resources资源目录

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
编写Druid工具类

获取数据库连接池对象
通过工厂来来获取 DruidDataSourceFactory类的createDataSource方法
createDataSource(Properties p) 方法参数可以是一个属性集对象

public class DruidUtils {
    
    // 1.定义成员变量
    public static DataSource dataSource;
    
    // 2.静态代码块
    static {
        try {
            // 3.创建属性集对象
            Properties p = new Properties();

            // 4.加载配置文件 Druid 连接池不能主动加载配置文件,需要指定文件
            InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");

            // 5.使用Properties对象的 load方法 从字节流中读取配置信息
            p.load(inputStream);

            // 6.通过工厂类获取连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(p);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 获取连接的方法
    public static Connection getConnection(){

        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    // 获取Druid连接池对象的方法
    public static DataSource getDataSource(){
        
        return dataSource;
    }

    // 释放资源
    public static void close(Connection con, Statement statement){

        if (con != null && statement != null){
            try {
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Connection con, Statement statement, ResultSet resultSet){

        if (con != null && statement != null && resultSet != null){
            try {
                resultSet.close();
                statement.close();
                // 归还连接
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
}
测试工具类
public class TestDruid {

    // 需求:查询薪资在3000到5000之间的员工的姓名
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = DruidUtils.getConnection();

        // 2.获取Statement对象
        Statement statement = con.createStatement();

        // 3.执行查询
        ResultSet resultSet = statement.executeQuery("select ename from employee where salary between 3000 and 5000");

        // 4.处理结果集
        while(resultSet.next()){
            String ename = resultSet.getString("ename");
            System.out.println(ename);
        }

        // 5.释放资源
        DruidUtils.close(con,statement,resultSet);
    }
}

DBUtils工具类

DBUtils简介

​ 使用JDBC我们发现冗余的代码太多了,为了简化开发 我们选择使用 DbUtils。
​ Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
使用方式:
​ DBUtils就是JDBC的简化开发工具包。需要项目导入 commons-dbutils-1.6.jar。

Dbutils核心功能介绍

1、QueryRunner 中提供对sql语句操作的API

2、ResultSetHandler接口,用于定义select操作后,怎样封装结果集

3、DbUtils类,他就是一个工具类,定义了关闭资源与事务处理相关方法

案例相关知识
表和类之间的关系

1、整个表可以看做是一个类
2、表中的一行记录,对应一个类的实例(对象)
3、表中的一列,对应类中的一个成员属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aL82KbWL-1621589561615)(E:\MarkDown\拉勾笔记\表和类之间的关系)]

JavaBean组件

1、JavaBean就是一个类,开发中通常用于封装数据,有以下特点

​ a.需要实现序列化接口,Serializable (暂时可以省略)

​ b.提供私有字段: private 类型 变量名;

​ c.提供getter和setter

​ d.提供空参构造

2、创建Employee类和数据库的employee表对应

​ 创建一个 entity包,专门用来存放 JavaBean类

/**
 * @auther weiwei
 * @date 2021/5/18 15:56
 * @description     JavaBean类
 *                      用来存储数据 成员变量私有 提供get set 提供空参构造器 实现序列化接口
 *
 * Employee类 对应数据库中的 employee表
 *   `eid` int(11) NOT NULL AUTO_INCREMENT,
 *   `ename` varchar(20) DEFAULT NULL,
 *   `age` int(11) DEFAULT NULL,
 *   `sex` varchar(6) DEFAULT NULL,
 *   `salary` double DEFAULT NULL,
 *   `empdate` date DEFAULT NULL,
 */
public class Employee implements Serializable {

    // 成员变量名称 与 表中的列要一样
    private int eid;

    private String ename;

    private int age;

    private String sex;

    private double salary;

    private Date empdaye;

    public Employee() {
    }

    public Employee(int eid, String ename, int age, String sex, double salary, Date empdaye) {
        this.eid = eid;
        this.ename = ename;
        this.age = age;
        this.sex = sex;
        this.salary = salary;
        this.empdaye = empdaye;
    }

    public int getEid() {
        return eid;
    }

    public void setEid(int eid) {
        this.eid = eid;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

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

    public Date getEmpdaye() {
        return empdaye;
    }

    public void setEmpdaye(Date empdaye) {
        this.empdaye = empdaye;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "eid=" + eid +
                ", ename='" + ename + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", salary=" + salary +
                ", empdaye=" + empdaye +
                '}';
    }
}
DBUtils完成 CRUD
QueryRunner核心类

1、构造方法

​ QueryRunner()

​ QueryRunner(DataSource ds),提供数据源(连接池),DBUtils底层自动维护连接connection

2、常用方法

​ update(Connection conn, String sql, Object… params) ,用来完成表数据的增加、删除、更新操作

​ query(Connection conn, String sql, ResultSetHandler rsh, Object… params) ,用来完成表
数据的查询操作

QueryRunner的创建
public class DBUtilsDemo01 {

    // QueryRunner 核心类的创建方式
    public static void main(String[] args) {

        // 方式1:手动模式
        QueryRunner qr = new QueryRunner();

        // 方式2:自动模式 提供数据库连接池对象,DBUtils会自动的维护连接
        QueryRunner qr2 = new QueryRunner(DruidUtils.getDataSource());

    }
}
QueryRunner实现增、删、改操作

核心方法:update(Connection conn, String sql, Object… params)

参数说明
Connection conn数据库连接对象,自动模式创建QueryRun 可以不传,手动模式必须传递
String sql占位符形式的SQL,使用 ? 号占位符
Object… paramObject类型的可变参,用来设置占位符上的参数

步骤
1.创建QueryRunner(手动或自动)
2.占位符方式 编写SQL
3.设置占位符参数
4.执行

/**
 * @auther weiwei
 * @date 2021/5/18 16:26
 * @description     使用QueryRunner对象 完成增删改
 *                      update(Connection con, String sql, Object...param)方法
 */
public class DBUtilsDemo02 {

    // 插入操作
    @Test
    public void testInsert() throws SQLException {

        // 1.创建QueryRunner 手动模式创建
        QueryRunner qr = new QueryRunner();

        // 2.编写 占位符方式 SQL
        String sql = "insert into employee values(?,?,?,?,?,?)";

        // 3.设置占位符参数
        Object[] param = {null,"张百万",20,"女",10000,"1990-12-26"};

        // 4.执行update方法
        Connection con = DruidUtils.getConnection();
        int i = qr.update(con, sql, param);

        // 5.释放资源
        //con.close();
        DbUtils.closeQuietly(con);
    }

    // 修改操作 修改姓名为 张百万的员工的工资为 15000
    @Test
    public void testUpdate() throws SQLException {

        // 1.创建 核心类 自动模式 需要传递 数据库连接池对象
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "update employee set salary = ? where ename = ?";

        // 3.设置占位符参数
        Object[] param = {15000,"张百万"};

        // 4.执行修改操作 自动模式不需要传入connection对象
        qr.update(sql,param);
    }

    // 删除操作 删除id为1的 记录
    @Test
    public void testDelete() throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        String sql = "delete from employee where eid = ?";

        // 如果只有一个参数的话 不需要创建数组
        qr.update(sql,1);
    }
}
QueryRunner实现查询操作
ResultSetHandler接口简介

​ ResultSetHandler可以对查询出来的ResultSet结果集进行处理,达到一些业务上的需求。

ResultSetHandler 结果集处理类

​ 本例展示的是使用ResultSetHandler接口的几个常见实现类实现数据库的增删改查,可以大大减少代码量,优化程序。
​ 每一种实现类都代表了对查询结果集的一种处理方式

ResultSetHandler 实现类说明
ArrayHandler将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中
BeanHandler将结果集中第一条记录封装到一个指定的javaBean中
BeanListHandler将结果集中每一条记录封装到指定的javaBean中,再将这些javaBean在封装到List集合中
ColumnListHandler将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个Map的value,另一个Map集合的key是指定的字段的值
MapHandler将结果集中第一条记录封装到了Map<String, Object>集合中,key就是字段名称,value就是字段值
MapListHandler将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中
ScalarHandler它是用于封装单个数据。例如 select count(*) from 表操作
ResultSetHandler 常用实现类测试

​ QueryRunner的查询方法
​ query方法的返回值都是泛型,具体的返回值类型,会根据结果集的处理方式发生变化

方法说明
query(String sql, handler ,Object[] param)自动模式创建QueryRunner, 执行查询
query(Connection con,String sql,handler,Object[] param)手动模式创建QueryRunner, 执行查询
public class DBUtilsDemo03 {

    /**
     * 查询id为5的记录,封装到数组中
     * ArrayHandler 将结果集的第一条数据封装到数组中
     */
    @Test
    public void testFindById() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from employee where eid = ?";

        // 3.执行查询操作
        Object[] query = qr.query(sql, new ArrayHandler(), 5);

        // 4.获取数据
        System.out.println(Arrays.toString(query));
    }

    /**
     * 查询所有数据,封装到List集合中
     * ArrayListHandler可以将每条数据先封装到数组中,再将数组封装到集合中
     */
    @Test
    public void testFindAll() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from employee";

        // 3.执行查询
        List<Object[]> query = qr.query(sql, new ArrayListHandler());

        // 4.遍历集合获取数据
        for (Object[] objects : query) {
            System.out.println(Arrays.toString(objects));
        }
    }

    /**
     * 查询id为3的记录,封装到指定JavaBean中
     * BeanHandler 将结果集的第一条数据封装到 javaBean中
     */
    @Test
    public void testFindIdJavaBean() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from employee where eid = ?";

        // 3.执行查询
        Employee employee = qr.query(sql, new BeanHandler<Employee>(Employee.class), 3);

        // 4.打印获取数据
        System.out.println(employee);
    }

    /**
     * 查询薪资大于 3000 的所有员工信息,封装到JavaBean中再封装到List集合中
     * BeanListHandler 将结果集的每一条和数据封装到 JavaBean中 再将JavaBean 放到list集合中
     */
    @Test
    public void testFindBySalary() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from employee where salary > ?";

        // 3.执行查询
        List<Employee> list = qr.query(sql, new BeanListHandler<Employee>(Employee.class), 3000);

        // 4.遍历获取数据
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

    /**
     * 查询姓名是 张百万的员工信息,将结果封装到Map集合中
     * MapHandler 将结果集的第一条记录封装到 Map<String,Object>中
     * key对应的是 列名 value对应的是 列的值
     */
    @Test
    public void testFindByName() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from employee where ename = ?";

        // 3.执行查询
        Map<String, Object> map = qr.query(sql, new MapHandler(), "张百万");

        // 4.遍历获取数据
        Set<Map.Entry<String, Object>> entries = map.entrySet();
        for (Map.Entry<String,Object> entry : entries) {
            // 打印结果
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }

    /**
     * 查询所有员工的薪资总额
     * ScalarHandler 用于封装单个的数据
     */
    @Test
    public void testSum() throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select sum(salary) from employee";

        // 3.执行查询
        Double sum = (Double)qr.query(sql,new ScalarHandler<>());

        // 获取数据
        System.out.println("元薪资总额:" + sum);
    }
}

数据库批处理

什么是批处理

1、批处理(batch) 操作数据库

​ 批处理指的是一次操作中执行多条SQL语句,批处理相比于一次一次执行效率会提高很多。
​ 当向数据库中添加大量的数据时,需要用到批处理。

2、举例: 送货员的工作:

​ 未使用批处理的时候,送货员每次只能运送 一件货物给商家;
​ 使用批处理,则是送货员将所有要运送的货物, 都用车带到发放处派给客户。

实现批处理

Statement和PreparedStatement都支持批处理操作,这里我们介绍一下PreparedStatement的批处理方式:

1、用到的方法

方法说明
void addBatch()将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中,通过调用方法 executeBatch 可以批量执行此列表中的命令
int[] executeBatch()每次提交一批命令到数据库中执行,如果所有的命令都成功执行了,那么返回一个数组,这个数组是说明每条命令所影响的行数

2、mysql 批处理是默认关闭的,所以需要加一个参数才打开mysql 数据库批处理,在url中添加

rewriteBatchedStatements=true
例如: url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&rewriteBatchedStatements=true

3、测试

-- 创建一张表
CREATE TABLE testBatch (
	id INT PRIMARY KEY AUTO_INCREMENT,
	uname VARCHAR(50)
)
public class BatchInsert {

    // 使用批处理 向表中添加 10000条数据
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection con = DruidUtils.getConnection();

        // 2.获取预处理对象
        PreparedStatement ps = con.prepareStatement("insert into testbatch(uname) values(?)");

        // 3.执行批量插入操作
        for (int i = 0; i < 10000; i++) {
            ps.setString(1,"小强" + i);

            // 将SQL添加到批处理表里
            ps.addBatch();
        }
        // 添加时间戳,测试执行效率
        long start = System.currentTimeMillis();

        // 4.统一执行批量插入操作
        ps.executeBatch();

        long end = System.currentTimeMillis();

        System.out.println("插入10000条数据需要使用:" + (end - start) + "毫秒!");

        // 5.关闭连接
        DruidUtils.close(con,ps);
    }
}

MySql元数据

元数据概念

除了表之外的数据都是元数据,可以分为三类
查询结果信息: UPDATE 或 DELETE语句 受影响的记录数。
数据库和数据表的信息: 包含了数据库及数据表的结构信息。
MySQL服务器信息: 包含了数据库服务器的当前状态,版本号等。

常用命令
-- 元数据相关的命令介绍
-- 1.查看服务器当前状态
show status;

-- 2.查看MySQl的版本信息
select version(); 

-- 3.查询表中的详细信息,和desc table_name一样
show columns from table_name;

-- 4.显示数据表的详细索引信息,包括PRIMARY KEY(主键)
show index from table_name;

-- 5.列出所有数据库
show databases;

-- 6.显示当前数据库的所有表
show tables;

-- 7.获取当前的数据库名
select database();
使用JDBC 获取元数据

​ 通过JDBC 也可以获取到元数据,比如数据库的相关信息,或者当我们使用程序查询一个不熟悉的表时,我们可以通过获取元素据信息,了解表中有多少个字段,字段的名称 和 字段的类型

常用类介绍

1、JDBC中描述元数据的类

元数据类作用
DatabaseMetaData描述数据库的元数据对象
ResultSetMetaData描述结果集的元数据对象

2、获取元数据对象的方法 : getMetaData ()
connection 连接对象, 调用 getMetaData () 方法,获取的是DatabaseMetaData 数据库元数据对象
PrepareStatement 预处理对象调用 getMetaData () , 获取的是ResultSetMetaData , 结果集元数据对象

3、DatabaseMetaData的常用方法

方法说明
getURL() : 获取数据库的URL
getUserName(): 获取当前数据库的用户名
getDatabaseProductName(): 获取数据库的产品名称
getDatabaseProductVersion(): 获取数据的版本号
getDriverName(): 返回驱动程序的名称
isReadOnly(): 判断数据库是否只允许只读 true 代表只读

4、ResultSetMetaData的常用方法

方法声明
getColumnCount() : 当前结果集共有多少列
getColumnName(int i) : 获取指定列号的列名,参数是整数 从1开始
getColumnTypeName(int i): 获取指定列号列的类型,参数是整数 从1开始
public class TestMetaData {

    // 1.获取数据库相关的元数据信息 使用DatabaseMetaData
    @Test
    public void testDataBaseMetaData() throws SQLException {

        // 1.获取数据库连接对象 connection
        Connection connection = DruidUtils.getConnection();

        // 2.获取代表数据库的 元数据对象 DatabaseMetaData
        DatabaseMetaData metaData = connection.getMetaData();

        // 3.获取数据库相关元数据信息
        String url = metaData.getURL();
        System.out.println("数据库url:" + url);

        String userName = metaData.getUserName();
        System.out.println("当前用户:" + userName);

        String productName = metaData.getDatabaseProductName();
        System.out.println("数据库产品名:" + productName);

        String version = metaData.getDatabaseProductVersion();
        System.out.println("数据库版本:" + version);

        String driverName = metaData.getDriverName();
        System.out.println("驱动名称:" + driverName);

        // 判断当前数据库是否只允许只读
        boolean b = metaData.isReadOnly();  // 如果是true 表示只读
        if (b){
            System.out.println("当前数据库只允许读操作!");
        }else {
            System.out.println("不是只读数据库!");
        }

        // 释放资源
        connection.close();
    }

    // 获取结果集的元数据信息
    @Test
    public void testResultSetMetaData() throws SQLException {

        // 1.获取连接
        Connection con = DruidUtils.getConnection();

        // 2.获取预处理对象
        PreparedStatement ps = con.prepareStatement("select * from employee");
        ResultSet resultSet = ps.executeQuery();

        // 3.获取结果集元数据对象
        ResultSetMetaData metaData = ps.getMetaData();

        // 1.获取当前结果集共有多少列
        int count = metaData.getColumnCount();
        System.out.println("当前结果集中共有:" + count + "列");

        // 2.获取结果集中列的名称和类型
        for (int i = 1; i < count; i++) {
            String columnName = metaData.getColumnName(i);
            System.out.println("列名:" + columnName);

            String columnTypeName = metaData.getColumnTypeName(i);
            System.out.println("类型:" + columnTypeName);
        }

        // 释放资源
        DruidUtils.close(con,ps,resultSet);
    }
}

XML

XML基本介绍

概述

XML即可扩展标记语言(Extensible Markup Language)

​ W3C在1998年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本 !

特点:

​ 可扩展的,标签都是自定义的
​ 语法十分严格

XML的作用
功能说明
存储数据我们通常在数据库中存储数据。如果希望数据的可移植性更强,我们可以把数据存储 XML 文件中
配置文件作为各种技术框架的配置文件使用 (最多)
在网络中传输客户端可以使用XML格式向服务器端发送数据,服务器接收到xml格式数据,进行解析

XML的语法

XML文档声明格式

文档声明必须为结束;
文档声明必写在第一行;

1、语法格式:

<?xml version="1.0" encoding="UTF-8"?>

2、属性说明:

**versioin:**指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0;
**encoding:**指定当前文档的编码。可选属性,默认值是utf-8;

元素

Element 元素:是XML文档中最重要的组成部分

1、元素的命名规则

​ 1.不能使用空格,不能使用冒号

​ 2.xml 标签名称区分大小写

​ 3.XML 必须有且只有一个根元素

2、语法格式:

<users><users>

a.XML 必须有且只有一个根元素,它是所有其他元素的父元素,比如以下实例中 users 就是根元素:

<?xml version="1.0" encoding="utf-8" ?>
<users>
 
</users>

b.普通元素的结构开始标签、元素体、结束标签组成

<hello> 大家好 </hello>

c.元素体:元素体可以是元素,也可以是文本

<hello>
  <a>你好</a>
</hello>

d.空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合

<close/>
属性
<bean id="" class=""> </bean>

1.属性是元素的一部分,它必须出现在元素的开始标签中

2.属性的定义格式:属性名=属性值,其中属性值必须使用单引或双引

3.一个元素可以有0~N个属性,但一个元素中不能出现同名属性

4.属性名不能使用空格、冒号等特殊字符,且必须以字母开头

注释

XML的注释,以“ ”结束。注释内容会被XML解析器忽略!

<?xml version="1.0" encoding="UTF-8" ?>
<users>
    <user id ="123">
        <name>张百万</name>
        <age>15</age>
    </user>

    <user id = "456">
        <name>小斌</name>
        <age>18</age>
        <hobby>
            <pingpang>

            </pingpang>
        </hobby>
    </user>

    <!-- 空元素没有结束标签 -->
    <close/>
</users>
<!-- XML的注释
    1.XML中必须进行文档声明
        version 版本信息
        encoding 编码
    2.XML中的文档声明必须写在第一行

    3.XML中的元素标签 命名规则
        1.标签定义 不能使用空格或者冒号
        2.xml的名称 区分大小写

    4.XML中有且只有一个根元素

    5.元素体可以是文本或者还是标签

    6.属性是元素的一部分,只能出现在元素的开始标签中
        属性值必须使用单引号或者双引号包裹
        一个元素标签可以定义多个属性
-->
使用XML 描述数据表中的数据
<?xml version="1.0" encoding="UTF-8" ?>
<employees>     <!-- 根元素 -->

    <employee eid="2">           <!-- employee元素1 eid属性 -->
        <ename>林黛玉</ename>    <!-- 元素体 -->
        <age>20</age>
        <sex></sex>
        <salary>5000</salary>
        <empdate>2010-01-01</empdate>
    </employee>

    <employee eid="3">
        <ename>杜甫</ename>
        <age>40</age>
        <sex></sex>
        <salary>15000</salary>
        <empdate>2010-01-01</empdate>
    </employee>

</employees>

XML约束

在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
常见的xml约束:
DTD
Schema
作为程序员只要掌握两点
会阅读
会引入
不用自己编写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dVsX8KHu-1621589561617)(E:\MarkDown\拉勾笔记\XML约束)]

DTD约束

​ DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。

编写DTD

​ 开发中,我们不会自己编写DTD约束文档
​ 通常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。常见框架使用DTD约束有:Struts2、hibernate等

创建Student.dtd约束文件

<!ELEMENT students (student+) >
            <!ELEMENT student (name,age,sex)>
            <!ELEMENT name (#PCDATA)>
            <!ELEMENT age (#PCDATA)>
            <!ELEMENT sex (#PCDATA)>
            <!ATTLIST student number ID #REQUIRED>

<!--
    Element 定义元素
        students (student+):students 代表根元素
        student+:代表根标签中 至少有一个 student子元素
        student (name,age,sex):student标签中可以 包含的子元素 按顺序出现
        #PCDATA:表示普通文本内容
        ATTLIST:用来定义属性
        student number ID:student标签中 有一个ID属性 叫做number
        #REQUIRED:表示number属性必须填写
        ID 唯一的值 不能重复 值只能是字母或者下划线开头
 -->
引入DTD

引入dtd文档到xml文档中,两种方式
1、内部dtd:将约束规则定义在xml文档中
2、外部dtd:将约束的规则定义在外部的dtd文件中

​ 本地:

​ 网络:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
    <student number="s1">
        <name>长海</name>
        <age>20</age>
        <sex></sex>
    </student>
    
    <student number="s2">
        <name>大玲子</name>
        <age>18</age>
        <sex></sex>
    </student>
</students>
Schema约束
什么是Schema

​ 1.Schema是新的XML文档约束,比DTD强大很多,是DTD 替代者;

​ 2.Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。

​ 3.Schema 功能更强大,内置多种简单和复杂的数据类型

​ 4.Schema 支持命名空间 (一个XML中可以引入多个约束文档)

Schema约束示例

student.xsd文件

<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.lagou.com/xml"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.lagou.com/xml" elementFormDefault="qualified">

    <xsd:element name="students" type="studentsType"/>
    <xsd:complexType name="studentsType">
        <xsd:sequence>
            <xsd:element name="student" type="studentType" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="studentType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="age" type="ageType" />
            <xsd:element name="sex" type="sexType" />
        </xsd:sequence>
        <xsd:attribute name="number" type="numberType" use="required"/>
    </xsd:complexType>
    <xsd:simpleType name="sexType">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="male"/>
            <xsd:enumeration value="female"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="ageType">
        <xsd:restriction base="xsd:integer">
            <xsd:minInclusive value="0"/>
            <xsd:maxInclusive value="200"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="numberType">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="hehe_\d{4}"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

Xml Schema的根元素:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NDua1c11-1621589561619)(E:\MarkDown\拉勾笔记\Xml Schema的根元素)]

XML引入Schema约束

xml中引入schema约束的步骤:

1.查看schema文档,找到根元素,在xml中写出来

2.根元素来自哪个命名空间。使用xmlns指令来声明

3.引入 w3c的标准命名空间,复制即可

4.引入的命名空间跟哪个xsd文件对应?
使用schemaLocation来指定:两个取值:第一个为命名空间 第二个为xsd文件的路径

5.命名空间

6.student.xml

<?xml version="1.0" encoding="UTF-8" ?>
<students
        xmlns="http://www.wei.com/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.wei.com/xml student.xsd"
>
    <student number="hehe_1234">
        <name>张三</name>
        <age>20</age>
        <sex>male</sex>
    </student>
    
    <student number="hehe_4567">
        <name>张三</name>
        <age>200</age>
        <sex>male</sex>
    </student>

</students>

XML 解析

解析概述

​ 当将数据存储在XML后,我们就希望通过程序获得XML的内容。如果我们使用Java基础所学习的IO知识是可以完成的,不过你需要非常繁琐的操作才可以完成,且开发中会遇到不同问题(只读、读写)。人们为不同问题提供不同的解析方式,并提交对应的解析器,方便开发人员操作XML。

XML解析方式

开发中比较常见的解析方式有两种,如下:

DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。
优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
缺点:XML文档过大,可能出现内存溢出显现。

SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方
式进行具体解析,每执行一行,都将触发对应的事件。(了解)
优点:占用内存少 处理速度快,可以处理大文件
缺点:只能读,逐行后将释放资源。

XML常见的解析器

​ 解析器:就是根据不同的解析方式提供的具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包。

​ JAXP:sun公司提供的解析器,支持DOM和SAX两种思想
​ **DOM4J:**一款非常优秀的解析器 , Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。
​ 它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
​ Jsoup:jsoup 是一款Java 的HTML解析器,也可以解析XML
​ PULL:Android内置的XML解析方式,类似SAX。

dom4j 的使用
导入JAR包
API介绍

使用核心类SaxReader加载xml文档获得Document,通过Document 对象获得文档的根元素,然后就可以操作了。

常用API:

SaxReader对象
read(…) 加载执行xml文档
Document对象
getRootElement() 获得根元素
Element对象
elements(…) 获得指定名称的所有子元素。可以不指定名称
element(…) 获得指定名称的第一个子元素。可以不指定名称
getName() 获得当前元素的元素名
attributeValue(…) 获得指定属性名的属性值
elementText(…) 获得指定名称子元素的文本值
getText() 获得当前元素的文本内容

准备xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<users xmlns="http://www.wei.com/xml"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.wei.com/xml  user.xsd"
>
    <user id="001">
        <name>张百万</name>
        <age>25</age>
        <hobby>抽烟</hobby>
    </user> 
    
    <user id="002">
        <name>于谦</name>
        <age>55</age>
        <hobby>烫头</hobby>
    </user>
    
    <user id="003">
        <name>小斌</name>
        <age>25</age>
        <hobby>喝酒</hobby>
    </user>
    
</users>
读取XML
public class TestDOM4j {

    // 获取XML文件中的 所有元素(标签)
    @Test
    public void test1() throws DocumentException {

        // 1.获取XML解析对象
        SAXReader reader = new SAXReader();

        // 2.解析XML 获取文档对象 document
        Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml03\\user.xml");

        // 3.获取根元素
        Element rootElement = document.getRootElement();

        // 获取根元素的名称
        System.out.println(rootElement.getName());

        List<Element> elements = rootElement.elements();

        // 获取根元素下的标签
        for (Element element : elements) {
            System.out.println("根标签下的子节点:" + element.getName());

            List<Element> elist = element.elements();
            for (Element e : elist) {
                System.out.println("user标签下的子节点:" + e.getName());
            }
            break;
        }
    }

    // 获取XML中标签的文本信息 和 属性信息
    @Test
    public void test2() throws DocumentException {

        // 1.获取解析XML的SAXReader
        SAXReader reader = new SAXReader();

        // 2.获取文档对象
        Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml03\\user.xml");

        // 3.获取根节点
        Element rootElement = document.getRootElement();

        // 4.获取子节点
        List<Element> elements = rootElement.elements();

        // 5.获取集合中的第一个子节点
        Element user = elements.get(0);

        // 6.获取节点中的文本信息
        String id = user.attributeValue("id"); // 获取属性id的值
        String name = user.elementText("name");
        String age = user.elementText("age");
        String hobby = user.element("hobby").getText();

        //打印
        System.out.println(id + " " + name +" " + age + " " + hobby);
    }
}
xpath方式读取xml
xpath介绍

XPath 是一门在 XML 文档中查找信息的语言。 可以是使用xpath查找xml中的内容。
XPath 的好处
由于DOM4J在解析XML时只能一层一层解析,所以当XML文件层数过多时使用会很不方便,结合XPATH就可以直接获取到某个元素

XPath基本语法介绍

使用dom4j支持xpath的操作的几种主要形式

语法说明
/AAA/DDD/BBB表示一层一层的,AAA下面 DDD下面的BBB
//BBB表示和这个名称相同,表示只要名称是BBB,都得到
//*所有元素
BBB[1] , BBB[last()]第一种表示第一个BBB元素,第二种表示最后一个BBB元素
//BBB[@id]表示只要BBB元素上面有id属性,都得到
//BBB[@id=‘b1’]表示元素名称是BBB,在BBB上面有id属性,并且id的属性值是b1
API介绍

selectSingleNode(query): 查找和 XPath 查询匹配的一个节点。
参数是Xpath 查询串。
selectNodes(query): 得到的是xml根节点下的所有满足 xpath 的节点;
参数是Xpath 查询串。
Node: 节点对象

Xpath读取XML

准备数据:book.xml

<?xml version="1.0" encoding="UTF-8" ?>
<bookstore>
      <book id="book1">
        <name>金瓶梅</name>
        <author>金圣叹</author>
        <price>99</price>
      </book>
      <book id="book2">
        <name>红楼梦</name>
        <author>曹雪芹</author>
        <price>69</price>
      </book>
      <book id="book3">
        <name>Java编程思想</name>
        <author>埃克尔</author>
        <price>59</price>
      </book>
</bookstore>

解析xml

public class TestXPath {

    /**
     *  使用selectSingleNOde()方法 查询指定的节点信息
     */
    @Test
    public void test1() throws DocumentException {

        // 1.创建XML解析对象
        SAXReader reader = new SAXReader();

        // 2.解析XML 获取文档对象
        Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml04\\book.xml");

        // 3.通过selectSingleNOde()方法 获取name节点
        Node node1 = document.selectSingleNode("/bookstore/book/name");
        System.out.println("节点的名称:" + node1.getName());
        System.out.println("书名:" + node1.getText());

        // 4.获取第二本书的书名
        Node node2 = document.selectSingleNode("/bookstore/book[2]/name");
        System.out.println("书名:" + node2.getText());
    }

    /**
     * 使用selectSingleNode()方法 获取属性值 或 通过属性值获取到节点信息
     */
    @Test
    public void test2() throws DocumentException {

        SAXReader reader = new SAXReader();

        Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml04\\book.xml");

        // 1.获取第一个book节点中的 id属性值
        Node node1 = document.selectSingleNode("/bookstore/book/attribute::id");
        System.out.println("第一个book的id属性值:" + node1.getText());

        // 2.获取最后衣蛾book节点的 id属性值
        Node node2 = document.selectSingleNode("/bookstore/book[last()]/attribute::id");
        System.out.println("最后一个book节点中的属性值:" + node2.getText());

        // 3.通过id的值 获取book2节点中的书名
        Node node3 = document.selectSingleNode("/bookstore/book[@id='book2']");
        String name = node3.selectSingleNode("name").getText();
        System.out.println("id为book2的结点的书名是:" + name);
    }

    /**
     * 使用 selectNodes()方法 获取对应名称的所有节点
     */
    @Test
    public void test3() throws DocumentException {

        SAXReader reader = new SAXReader();

        Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml04\\book.xml");

        // 1.查询所有结点
        List<Node> list = document.selectNodes("//*");

        for (Node node : list) {
            System.out.println("节点名:" + node.getName());
        }

        // 2.获取所有的书名
        List<Node> list1 = document.selectNodes("//name");

        for (Node node : list1) {
            System.out.println("书名:" + node.getText());
        }

        // 3.获取id值为book1的节点中的所有内容
        List<Node> list2 = document.selectNodes("/bookstore/book[@id='book1']//*");
        for (Node node : list2) {
            System.out.println(node.getName() + " = " + node.getText());
        }

    }
}

JDBC自定义XML

定义配置文件

创建自定义xml 文件,保存 数据库连接信息

jdbc-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<jdbc>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db5?characterEncoding=UTF-8</property>
    <property name="user">root</property>
    <property name="password">123456</property>
</jdbc>
编写工具类(配置式)
public class JDBCUtils {

    //1.定义字符串变量 保存连接信息
    public static String DRIVERNAME;
    public static String URL;
    public static String USER;
    public static String PASSWORD;

    //2.静态代码块
    static{
        //使用 XPath语法 对xml中的数据进行读取
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read("E:\\jdbc_work\\xml_task03\\src\\com\\wei\\xml05\\jdbc_config.xml");

            //1.获取驱动名称
            Node driver = document.selectSingleNode("/jdbc/property[@name='driverClass']");
            DRIVERNAME = driver.getText();

            //2.获取URL
            Node url = document.selectSingleNode("/jdbc/property[@name='jdbcUrl']");
            URL = url.getText();

            //3.获取用户名
            Node user = document.selectSingleNode("/jdbc/property[@name='user']");
            USER = user.getText();

            //4.获取密码
            Node password = document.selectSingleNode("/jdbc/property[@name='password']");
            PASSWORD = password.getText();

            //注册驱动
            Class.forName(DRIVERNAME);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    //获取连接
    public static Connection getConnection(){

        try {

            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);

            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }

    }

}
测试工具类
public class TestJDBC {

    // 查询所有员工信息
    public static void main(String[] args) throws SQLException {

        // 1.获取连接
        Connection connection = JDBCUtils.getConnection();

        // 2.获取Statement对象 执行SQL
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("select * from employee");

        // 3.处理结果集
        while(resultSet.next()){
            String name = resultSet.getString("ename");
            System.out.println("员工的姓名:" + name);
        }

        // 4.关闭资源
        resultSet.close();
        statement.close();
        connection.close();

    }
}

综合案例

商城案例表设计

​ 创建数据库db6,用户表user、订单表orders,商品分类表category,商品表product,订单项表(中间表)orderitem

CREATE DATABASE db6 CHARACTER SET utf8;


-- 用户表
CREATE TABLE USER (
	  uid VARCHAR(32) PRIMARY KEY,	-- 用户ID
	  username VARCHAR(20), 	-- 用户名
	  PASSWORD VARCHAR(20), 	-- 密码
	  telephone VARCHAR(20), 	-- 电话
	  birthday DATE, 		-- 生日
	  sex VARCHAR(10) 		-- 性别
);

INSERT INTO USER VALUES 
('001','渣渣辉','123456','13511112222','2015-11-04','男'),
('002','药水哥','123456','13533334444','1990-02-01','男'),
('003','大明白','123456','13544445555','2015-11-03','男'),
('004','长海','123456','13566667777','2000-02-01','男'),
('005','乔杉','123456','13588889999','2000-02-01','男');


-- 订单表
CREATE TABLE orders (
	  oid VARCHAR(32) PRIMARY KEY,	-- 订单id
	  ordertime DATETIME,		-- 下单时间 
	  total DOUBLE, 		-- 总金额
	  NAME VARCHAR(20),		-- 收货人姓名
	  telephone VARCHAR(20),	-- 电话
	  address VARCHAR(30),		-- 地址
	  state INT(11),		-- 订单状态
	  uid VARCHAR(32),		-- 外键字段 对应用户表id
	  CONSTRAINT ofk_0001 FOREIGN KEY (uid) REFERENCES USER (uid)
);

-- 插入一条订单数据
INSERT INTO orders 
VALUES('order001','2019-10-11',5500,'乔杉','15512342345','皇家洗浴',0,'001');

-- 商品分类表
CREATE TABLE category (
	  cid VARCHAR(32) PRIMARY KEY,
	  cname VARCHAR(20)
);

INSERT INTO `category` VALUES ('1','手机数码'),('2','电脑办公'),('3','运动鞋服'),('4','图书音像');


-- 商品表
CREATE TABLE product (
	  pid VARCHAR(32)  PRIMARY KEY,		-- 商品id
	  pname VARCHAR(50),			-- 商品名称 
	  price DOUBLE,				-- 商品价格
	  pdesc VARCHAR(255),			-- 商品描述
	  pflag INT(11),			-- 商品状态 1 上架 ,0 下架
	  cid VARCHAR(32),			-- 外键对应 分类表id
	  KEY sfk_0001 (cid), 
	  CONSTRAINT sfk_0001 FOREIGN KEY (cid) REFERENCES category (cid)
);

INSERT INTO `product` VALUES 
('1','小米6',2200,'小米 移动联通电信4G手机 双卡双待',0,'1'),
('2','华为Mate9',2599,'华为 双卡双待 高清大屏',0,'1'),
('3','OPPO11',3000,'移动联通 双4G手机',0,'1'),
('4','华为荣耀',1499,'3GB内存标准版 黑色 移动4G手机',0,'1'),
('5','华硕台式电脑',5000,'爆款直降,满千减百',0,'2'),
('6','MacBook',6688,'128GB 闪存',0,'2'),
('7','ThinkPad',4199,'轻薄系列1)',0,'2'),
('8','联想小新',4499,'14英寸超薄笔记本电脑',0,'2'),
('9','李宁音速6',500,'实战篮球鞋',0,'3'),
('10','AJ11',3300,'乔丹实战系列',0,'3'),
('11','AJ1',5800,'精神小伙系列',0,'3');


-- 订单项表 (中间表)
CREATE TABLE orderitem (
	  itemid VARCHAR(32) PRIMARY KEY,	-- 订单项ID
	  pid VARCHAR(32), 			-- 外键 对应商品表 id
	  oid VARCHAR(32),			-- 外键 对应订单表 id
	  KEY fk_0001 (pid),
	  KEY fk_0002 (oid),
	  CONSTRAINT fk_0001 FOREIGN KEY (pid) REFERENCES product (pid),
	  CONSTRAINT fk_0002 FOREIGN KEY (oid) REFERENCES orders (oid)
);

-- 向中间表中插入两条数据
INSERT INTO orderitem VALUES('item001','1','order001');
INSERT INTO orderitem VALUES('item002','11','order001');

环境搭建

项目结构
com.wei.app 测试包 用于对DAO代码进行测试
com.wei.dao dao包  数据访问层,包含所有对数据库的相关操作的类
com.wei.entity 实体包 保存根据数据库表 对应创建的JavaBean类
com.wei.utils 工具包
导入所需Jar包

导入myjar仓库到项目中就可以了。

导入配置文件及工具类

JavaBean类创建

设计用户与订单
User类
public class User {

    private String uid;

    private String username;

    private String password;

    private String telephone;

    private String birthday;

    private String sex;

    public User() {
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "uid='" + uid + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", telephone='" + telephone + '\'' +
                ", birthday='" + birthday + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}
Orders类
public class Orders {
    
    private String oid; // 订单ID

    private String ordertime;   // 下单时间
    
    private double total;   // 订单总金额

    private String name;    // 收货人姓名

    private String telephone;   // 收货人电话

    private String address;     // 收货人地址
    
    private int state;      // 订单状态 1代表已支付,0代表未支付

    private String uid;     // 外键 uid

    private User user;      // 保存订单对应的用户的详细信息
    
    // 描述 多对一关系 一个订单中包含多个订单项信息
    List<OrderItem> list = new ArrayList<>();

    public Orders() {
    }

    public String getOid() {
        return oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public String getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
        this.ordertime = ordertime;
    }

    public double getTotal() {
        return total;
    }

    public void setTotal(double total) {
        this.total = total;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "oid='" + oid + '\'' +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", name='" + name + '\'' +
                ", telephone='" + telephone + '\'' +
                ", address='" + address + '\'' +
                ", state=" + state +
                ", uid='" + uid + '\'' +
                '}';
    }
}
设计商品与分类
Category类
/**
 * @auther weiwei
 * @date 2021/5/20 14:53
 * @description     商品分类表对应的java类
 */
public class Category {
    
    private String cid;
    
    private String cname;

    public Category() {
    }

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Category{" +
                "cid='" + cid + '\'' +
                ", cname='" + cname + '\'' +
                '}';
    }
}
Product类
public class Product {
    
    private String pid;

    private String pname;

    private double price;

    private String pdesc;

    private int pflag;  // 是否上架  1 上架,0 下架

    private String cid; // 外键
    
    private Category category;  // 保存分类的详细信息

    public Product() {
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getPdesc() {
        return pdesc;
    }

    public void setPdesc(String pdesc) {
        this.pdesc = pdesc;
    }

    public int getPflag() {
        return pflag;
    }

    public void setPflag(int pflag) {
        this.pflag = pflag;
    }

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    @Override
    public String toString() {
        return "Product{" +
                "pid='" + pid + '\'' +
                ", pname='" + pname + '\'' +
                ", price=" + price +
                ", pdesc='" + pdesc + '\'' +
                ", pfla=" + pflag +
                ", cid='" + cid + '\'' +
                '}';
    }
}
OderItem类
public class OrderItem {

    private String itemid;  // 订单项ID

    private String pid;     // 外键 指向了商品表的主键

    private String oid;     // 外键 指向了订单表的主键

    private Product product;    // 订单项中商品详细信息

    private Orders orders;      // 订单项所属的订单的详细信息

    public OrderItem() {
    }

    public String getItemid() {
        return itemid;
    }

    public void setItemid(String itemid) {
        this.itemid = itemid;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getOid() {
        return oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    public Orders getOrders() {
        return orders;
    }

    public void setOrders(Orders orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "OrderItem{" +
                "itemid='" + itemid + '\'' +
                ", pid='" + pid + '\'' +
                ", oid='" + oid + '\'' +
                '}';
    }
}

编写DAO类

UserDao
public class UserDao {

    // 需求一: 编写一个注册用户的方法,接收的参数是一个User对象
    public int register(User user) throws SQLException {

        // 1.获取QueryRunner对象
        QueryRunner qr = new QueryRunner(DruidUtils.dataSource);

        // 2.编写SQL
        String sql = "insert into user values(?,?,?,?,?,?)";

        Object[] param = {user.getUid(),user.getUsername(),user.getPassword(),user.getTelephone()
                ,user.getBirthday(),user.getSex()};

        // 3.执行插入操作
        int update = qr.update(sql,param);

        return update;
    }


    // 需求二: 编写一个用户登录的方法,接收的参数是 用户名 和密码,返回值是User对象
    public User login(String username, String password) throws SQLException {

        // 1.获取QueryRunner对象
        QueryRunner qr = new QueryRunner(DruidUtils.dataSource);

        // 2.编写SQL
        String sql = "select * from user where username = ? and password = ?";

        // 3.执行查询   使用BeanHandler来封装结果集,获取结果集中的第一条数据
        User user = qr.query(sql, new BeanHandler<User>(User.class), username, password);

        return user;

    }

}
public class TestUserDao {

    UserDao userDao = new UserDao();

    // 1.测试 注册用户
    @Test
    public void testRegister() throws SQLException {
        // 1.创建User
        User user = new User();
        user.setUid(UUIDUtils.getUUID());
        user.setUsername("武松");
        user.setPassword("123456");
        user.setTelephone("13512341234");
        user.setSex("男");
        user.setBirthday(DateUtils.getDateFormart());

        // 2.执行注册
        int register = userDao.register(user);

        if (register > 0){
            System.out.println("注册成功,欢迎您:" + user.getUsername());
        }else {
            System.out.println("注册失败!!");
        }
    }

    // 2.用户登陆测试
    @Test
    public void testLogin() throws SQLException {

        // 1.调用UserDao的login方法 传入用户名和密码
        User user = userDao.login("大郎", "123456");

        // 2.判断user是否为空
        if (user != null){
            System.out.println("欢迎您!" + user.getUsername());
        }else {
            System.out.println("用户名或者密码错误!登良失败!");
        }

    }
}
ProductDao
public class ProductDao {

    // 需求1: 根据商品ID 获取商品名称 ,商品价格以及商品所属分类的名称
    public Product findProductById(String pid) throws SQLException {

        // 1.创建QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 2.编写SQL
        String sql = "select * from product where pid = ?";

        // 3.执行查询
        Product product = qr.query(sql, new BeanHandler<Product>(Product.class), pid);

        // 4.获取外键的值 对用分类的ID
        String cid = product.getCid();
        // 商品对应的 分类信息
        Category category = findCategoryById(cid);

        product.setCategory(category);

        return product;
    }

    // 需求2: 根据分类ID查询分类信息
    public Category findCategoryById(String cid) throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        String sql = "select * from category where cid = ?";

        Category category = qr.query(sql, new BeanHandler<Category>(Category.class), cid);

        return category;
    }

    // 需求3: 查询指定分类ID 下的商品个数
    public int getCount(String cid) throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        String sql = "select count(*) from product where cid = ?";

        // 获取单列数据 使用ScalarHandler 来封装
        Long count = qr.query(sql, new ScalarHandler<>(), cid);

        // 将Long类型的包装类 转换为int类型并返回
        return count.intValue();
    }


    // 需求4: 查询指定分类ID 下的所有商品信息
    public List<Product> findProductByCid(String cid) throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        String sql = "select * from product where cid = ?";

        // 查询结果是一个list集合,使用BeanListHandler来封装
        List<Product> list = qr.query(sql, new BeanListHandler<Product>(Product.class), cid);

        return list;

    }

}
public class TestProductDao {

    ProductDao productDao = new ProductDao();

    // 测试 根据商品ID 获取商品名称 ,商品价格以及商品所属分类的名称
    @Test
    public void testFindProductById() throws SQLException {

        // 1.调用方法获取商品对象
        Product product = productDao.findProductById("1");

        // 2.打印信息
        System.out.println(product.getPname() + " " + product.getPrice() + " " + product.getCategory().getCname());

    }

    // 测试 查询指定分类ID 下的商品个数
    @Test
    public void testgetCount() throws SQLException {

        // 查询分类id为3 的分类下 有几个商品
        int count = productDao.getCount("3");

        System.out.println("分类id为3的商品个数是:" + count);

    }

    // 测试 查询指定分类ID 下的所有商品信息
    @Test
    public void testfindProductByCid() throws SQLException {

        // 查询分类id为2 的所有商品信息
        List<Product> list = productDao.findProductByCid("2");

        for (Product product : list) {
            System.out.println(product);
        }

    }

}
OrdersDao
public class OrdersDao {

    // 需求1: 获取 uid为 001 的用户的所有订单信息
    public List<Orders> fingAllOrders(String uid) throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        String sql = "select * from orders where uid = ?";

        // 一个用户所有的订单信息
        List<Orders> ordersList = qr.query(sql, new BeanListHandler<Orders>(Orders.class), uid);

        return ordersList;
    }


    // 需求2: 获取订单编号为 order001的订单中的所有商品信息
    public List<Product> findOrderById(String oid) throws SQLException {

        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 1.查询订单项表 获取订单项表中 订单ID为order001 的数据
        String sql = "SELECT pid FROM orderitem WHERE oid = ?";

        // 2.查询的结果是 多条订单项数据
        List<OrderItem> list = qr.query(sql, new BeanListHandler<OrderItem>(OrderItem.class), oid);

        // 3.创建集合保存商品信息
        List<Product> productList = new ArrayList<>();

        ProductDao productDao = new ProductDao();

        // 4.遍历订单项集合 获取pid
        for (OrderItem orderItem : list) {

            // 4.1从orderitem中获取pid
            String pid = orderItem.getPid();

            // 4.2调用productDao
            Product product = productDao.findProductById(pid);

            // 4.3保存到集合
            productList.add(product);
        }

        // 返回 订单中对应的商品信息
        return productList;
    }

}
public class TestOrdersDao {

    OrdersDao ordersDao = new OrdersDao();

    // 测试 获取 uid为 001 的用户的所有订单信息
    @Test
    public void testFindAllOrders() throws SQLException {

        List<Orders> allOrders = ordersDao.fingAllOrders("001");

        for (Orders orders : allOrders) {
            System.out.println(orders);
        }

    }


    // 测试 获取订单编号为 order001的订单中的所有商品信息
    @Test
    public void testFindOrderById() throws SQLException {

        List<Product> order001 = ordersDao.findOrderById("order001");

        for (Product product : order001) {
            System.out.println(product);
        }

    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值