java大数据开发训练营--mysql数据库JDBC

题记:

文章内容输出来源:拉勾教育大数据开发高薪训练营
本篇文章是java学习课程中的一部分笔记。

本博文主要是记录一些基础的知识点,通过实操更容易理解

这章主要讲的是mysql在JAVA中的使用,

JDBC

JDBC 概述

什么是JDBC

JDBC(Java Data Base Connectivity) 是 Java 访问数据库的标准规范.是一种用于执行SQL语句的Java API,可以为 多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范.

JDBC 原理

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的版本,到官网https://dev.mysql.com/downloads/connector/j/,下载相应的zip,解压zip后得到jar包

 2. 将MySQL驱动包添加到jar包库文件夹中,Myjar文件夹,用于存放当前项目需要的所有jar包

3.在 idea中 配置jar包库的位置,新建一个module,并配置

 

 API使用: 1.注册驱动

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

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

加载注册驱动的方式

描述
Class.forName(数据库驱动实现类)

加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供 "com.mysql.jdbc.Driver"

1) 代码示例

public class JDBCDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.注册驱动
// forName 方法执行将类进行初始化
Class.forName("com.mysql.jdbc.Driver");
}
}

2) 为什么这样可以注册驱动?

我们知道 Class类的forName方法 ,可以将一个类初始化, 现在我们一起Driver类的 看一下源码

// Driver类是MySql提供的数据库驱动类, 实现了JDBC的Driver接口 java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 空参构造
public Driver() throws SQLException {
}
//静态代码块,Class类的 forName()方法将Driver类 加载到内存, static代码块会自动执行
static {
try {
/*
DriverManager 驱动管理类
registerDriver(new Driver) 注册驱动的方法
注册数据库驱动
*/
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}

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

API使用: 2.获得连接

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

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

获取连接的静态方法

说明

Connection getConnection(String url, String user, String password)

通过连接字符串和用户名,密码来获取数据 库连接对象

 1) getConnection方法 3个 连接参数说明

连接参数

说明
user登录用户名
password登录密码
url

mySql URL的格式

jdbc:mysql://localhost:3306/db4

 2) 对URL的详细说明

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

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

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

       第二部分是子协议,就是数据库名称,连接mysql数据库,第二部分当然是mysql了;

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

3) 代码示例

public class JDBCDemo02 { 
       public static void main(String[] args) throws Exception { 
              //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); 
              //2.获取连接 url,用户名, 密码 
              String url = "jdbc:mysql://localhost:3306/db4"; 
              Connection con = DriverManager.getConnection(url, "root", "123456");                                         
              //com.mysql.jdbc.JDBC4Connection@2e3fc542 
              System.out.println(con); 
       } 
}

报错:Exception in thread "main" java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.的解决方案

String url = "jdbc:mysql://127.0.0.1:3306/db4?characterEncoding=UTF-8&serverTimezone=UTC";

API 使用: 3.获取语句执行平台

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

Connection接口中的方法

说明
Statement createStatement()创建 SQL语句执行对象

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

Statement类 常用方法

说明
int executeUpdate(String sql);

执行insert update delete语句.返回int类型,代表受影响的行数

ResultSet executeQuery(String

sql);

执行select语句, 返回ResultSet结果集对象

代码示例

public class JDBCDemo03 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接 url,用户名, 密码
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
//4.执行创建表操作
String sql = "create table test01(id int, name varchar(20),age int);";
//5.增删改操作 使用executeUpdate,增加一张表
int i = statement.executeUpdate(sql);
//6.返回值是受影响的函数
System.out.println(i);
//7.关闭流
statement.close();
con.close();
}
}

 

API 使用: 4.处理结果集

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

代码示例

public class JDBCDemo04 {
public static void main(String[] args) throws SQLException {
//1.注册驱动 可以省略
//2.获取连接
String url = "jdbc:mysql://localhost:3306/db4";
Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
Statement statement = con.createStatement();
String sql = "select * from jdbc_user";
//执行查询操作,返回的是一个 ResultSet 结果对象
ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集 resultSet
}
}

ResultSet接口

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

ResultSet接口方法

说明
boolean next()

1) 游标向下一行

2) 返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false

xxx getXxx( String or int)

1) 通过列名,参数是 String 类型。返回不同的类型

2) 通过列号,参数是整数,从 1 开始。返回不同的类型

代码示例

public class jdbc_01 {
    public static void main(String[] args) throws SQLException {
//1.注册驱动 可以省略
//2.获取连接
        String url = "jdbc:mysql://localhost:3306/db4";
        Connection con = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
        Statement statement = con.createStatement();
        String sql = "select * from jdbc_user";
//执行查询操作,返回的是一个 ResultSet 结果对象
        ResultSet resultSet = statement.executeQuery(sql);
//4.处理结果集
// //next 方法判断是否还有下一条数据
// boolean next = resultSet.next();
// System.out.println(next);
//
// //getXXX 方法获取数据 两种方式
// int id = resultSet.getInt("id");//列名
// System.out.println(id);
//
// int anInt = resultSet.getInt(1);//列号
// System.out.println(anInt);
//使用while循环
        while(resultSet.next()){
//获取id
            int id = resultSet.getInt("id");
//获取姓名
            String username = resultSet.getString("username");
//获取生日
            Date birthday = resultSet.getDate("birthday");
            System.out.println(id + " = " +username + " : " + birthday);
        }
//关闭连接
        resultSet.close();
        statement.close();
        con.close();
    }
}

API 使用: 5.释放资源

1) 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接

2) 释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection

3) 放在哪个代码块中:fifinally 块

  • 与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关, 先得到的后关闭,后得到的先关闭

代码示例

package com.lagou.bigload;

import java.sql.*;

public class jdbc_01 {
    public static void main(String[] args)  {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
//1.注册驱动(省略)
//2.获取连接
            String url = "jdbc:mysql://127.0.0.1:3306/db4?characterEncoding=UTF-8&serverTimezone=UTC";
            connection = DriverManager.getConnection(url, "root", "123456");
//3.获取 Statement对象
            statement = connection.createStatement();
            //增删改
//            String sql = "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')" ;
//            int i =statement.executeUpdate(sql);
//            System.out.println(i);
            //查
            String sql = "select * from jdbc_user"; resultSet = statement.executeQuery(sql);
            resultSet = statement.executeQuery(sql);
            while(resultSet.next()){
//获取id
                int id = resultSet.getInt("id");
//获取姓名
                String username = resultSet.getString("username");
//获取生日
                Date birthday = resultSet.getDate("birthday");
                System.out.println(id + " = " +username + " : " + birthday);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
/**
 * 开启顺序: connection ==> statement => resultSet
 * 关闭顺序: resultSet ==> statement ==> connection
 */
            try {
                connection.close();
//                resultSet.close();
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

步骤总结

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

2. 获取连接

3. 获取Statement对象

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

5. 释放资源

JDBC实现增删改查

JDBC工具类

什么时候自己创建工具类?

  • 如果一个功能经常要用到,我们建议把这个功能做成一个工具类,可以在不同的地方重用。

  • “获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。

工具类包含的内容

  • 1) 可以把几个字符串定义成常量:用户名,密码,URL,驱动类

  • 2) 得到数据库的连接:getConnection()

  • 3) 关闭所有打开的资源:

代码示例

import java.sql.*;

/**
 * JDBC 工具类
 */
public class JDBCUtils {
    //1. 定义字符串常量, 记录获取连接所需要的信息
    public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://127.0.0.1:3306/db4?characterEncoding=UTF-8&serverTimezone=UTC";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";
    //2. 静态代码块, 随着类的加载而加载
    static{
        try {
//注册驱动
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //3.获取连接的静态方法
    public static Connection getConnection(){
        try {
//获取连接对象
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//返回连接对象
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    //关闭资源的方法
    public static void close(Connection con, Statement st){
        if(con != null && st != null){
            try {
                st.close();
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(Connection con, Statement st, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        close(con,st);
    }
}

DML操作

使用idea @Test报错参考https://blog.csdn.net/qq_45679541/article/details/106246714

    @Test
    public void DML() throws SQLException {
        //1.通过工具类获取连接
        Connection con=JDBCUtils.getConnection();
        //2.获取Statement
        Statement statement = con.createStatement();
        //执行sql
//        String sql = "insert into jdbc_user values(null,'张百万','123','2020/1/1')";   //添加
        //String sql="delete from jdbc_user where id=6";                  //删除
        String sql="update jdbc_user set password=1232344 where id=1";    //修改
        int i=statement.executeUpdate(sql);
        System.out.println(i);
        //关闭流
        JDBCUtils.close(con,statement);

    }

DQL操作

    @Test
    public void DQL() throws SQLException {
        Connection con=JDBCUtils.getConnection();
        Statement statement = con.createStatement();
        //查询
        String sql="select * from jdbc_user";
        ResultSet resultSet = statement.executeQuery(sql);
        while(resultSet.next()){
            int id=resultSet.getInt("id");
            String username=resultSet.getString("username");
            Date birthday=resultSet.getDate("birthday");
            System.out.println(id + " = " +username + " : " + birthday);
        }
        JDBCUtils.close(con,statement,resultSet);
    }

SQL注入问题

# SQL注入演示,当拼接字符串组成的sql就会有这样的问题

-- 填写一个错误的密码
SELECT * FROM jdbc_user WHERE username = 'tom' AND PASSWORD = '123' OR '1' = '1';
--如果这是一个登陆操作,那么用户就登陆成功了.显然这不是我们想要看到的结果

问题分析

  • 1) 什么是SQL注入?

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

  • 2) 如何实现的注入

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

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

       name='abc' and password='abc' 为假 '1'='1' 真

       相当于 select * from user where true=true; 查询了所有记录

  • 3) 如何解决

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

预处理对象

PreparedStatement 接口介绍

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

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

PreparedStatement 特点

  • 因为有预先编译的功能,提高 SQL 的执行效率。

  • 可以有效的防止 SQL 注入的问题,安全性更高

获取PreparedStatement对象

  • 通过Connection创建PreparedStatement对象

Connection 接口中的方法

说明
PreparedStatement prepareStatement(String sql)

指定预编译的 SQL 语句,

SQL 语句中使用占位符 ? 创建一个语句对象

PreparedStatement接口常用方法

常用方法

说明
int executeUpdate();执行insert update delete语句.
ResultSet executeQuery();执行select语句. 返回结果集对象 Resulet

使用PreparedStatement的步骤

  • 1) 编写 SQL 语句,未知内容使用?占位:
    "SELECT * FROM jdbc_user WHERE username=? AND password=?";
  • 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)使用给定对象设置指定参数的值。
    import java.sql.*;
    
    public class TestLogin {
        public static void main(String[] args) throws SQLException {
            Connection connection = JDBCUtils.getConnection();
            String sql="select * from jdbc_user where username=? and password=?";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,"test1");
            preparedStatement.setString(2,"123");
            ResultSet resultSet = preparedStatement.executeQuery();
    //        while(resultSet.next()){
    //            
     System.out.println(resultSet.getInt("id")+"="+resultSet.getString("username")+"="+resultSet.getDate("birthday"));
    //
    //        }
            System.out.println(resultSet.next()?"登录成功":"登陆失败");
            JDBCUtils.close(connection,preparedStatement,resultSet);
    
        }
    }

    PreparedStatement的执行原理

     

    Statement PreparedStatement的区别?

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

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

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

JDBC 控制事务

之前我们是使用 MySQL 的命令来操作事务。接下来我们使用 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. 最后关闭资源

       代码示例


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

public class JDBCTransaction {
    //JDBC 操作事务
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps=null;
        try {
//1. 获取连接
            con = JDBCUtils.getConnection();
//2. 开启事务
            con.setAutoCommit(false);
//3. 获取到 PreparedStatement 执行两次更新操作
//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();
            try {
//5. 出现异常回滚事务
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
//6. 最后关闭资源
            JDBCUtils.close(con,ps);
        }
    }
}

数据库连接池&DBUtils

数据库连接池

1 连接池介绍

         1) 什么是连接池

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

        2) 连接池的好处

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

2 JDBC方式与连接池方式

        普通 JDBC方式

        连接池方式

 3 如何使用数据库连接池

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。

这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池有 DBCP连接池, C3P0连接池, Druid连接池, 接下里我们就详细学习一下

4 数据准备

#创建数据库
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');

5 DBCP连接池

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

5.1 创建项目 导入 jar

1)将这两个 jar包添加到 myJar文件夹中 (jar包在资料里的软件文件夹中)

http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

https://commons.apache.org/proper/commons-pool/download_pool.cgi

如果使用DBCP 2.7.0报错找不到org/apache/commons/logging/logfactory,就去下载logging的jar包http://commons.apache.org/proper/commons-logging/download_logging.cgi

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

5.2 编写工具类

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

Java中提供了一个连接池的规则接口 : DataSource , 它是java中提供的连接池

DBCP包中提供了DataSource接口的实现类,我们要用的具体的连接池 BasicDataSource

代码示例

import org.apache.commons.dbcp2.BasicDataSource;

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

public class DBCPUtils {
    //1.定义常量 保存数据库连接的相关信息
    public static final String DRIVERNAME = "com.mysql.cj.jdbc.Driver";
    public static final String URL = "jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&serverTimezone=UTC";
    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();
            }
        }
    }
}

5.3 测试工具类

#需求: 查询eid为1的员工;

import java.sql.*;
/*
* DBCPUtils的测试类
* */
public class DECPTest {
    public static void main(String[] args) throws SQLException {
        //从DBCP连接池中拿到连接
        Connection connection = DBCPUtils.getConnection();
        //获得preparedstatement对象
        String sql="select * from employee where eid =?";
        PreparedStatement ps = connection.prepareStatement(sql);
        //查询并处理
        ps.setInt(1,1);
        ResultSet resultSet = ps.executeQuery();
        while (resultSet.next()){
            System.out.println(resultSet.getInt("eid") + " = " +resultSet.getString("ename")
                    +" : "+resultSet.getInt("age")
                    + " : " + resultSet.getString("sex")
                    + " : " + resultSet.getString("salary")
                    + " : " + resultSet.getDate("empdate"));
        }
        //释放资源
        DBCPUtils.close(connection,ps,resultSet);


    }
}

5.4 常见配置项

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

6 C3P0连接池

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

6.1 导入jar包及配置文件

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

https://sourceforge.net/projects/c3p0/?source=navbar

2) 导入配置文件 c3p0-confifig.xml

       c3p0-confifig.xml 文件名不可更改

       直接放到src,也可以放到到资源文件夹中

       注意:

       jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&serverTimezone=UTC

       会报错对实体 "serverTimezone" 的引用必须以 ';' 分隔符结尾。

       修改为  jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&serverTimezone=UTC

<c3p0-config>
    <!--默认配置-->
    <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <!-- initialPoolSize:初始化时获取三个连接,
        取值应在minPoolSize与maxPoolSize之间。 -->
        <property name="initialPoolSize">3</property>
        <!-- maxIdleTime:最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。-->
        <property name="maxIdleTime">60</property>
        <!-- maxPoolSize:连接池中保留的最大连接数 -->
        <property name="maxPoolSize">100</property>
        <!-- minPoolSize: 连接池中保留的最小连接数 -->
        <property name="minPoolSize">10</property>
    </default-config>
    <!--配置连接池mysql-->
    <named-config name="mysql">
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
    </named-config>
    <!--配置连接池2,可以配置多个-->
</c3p0-config>

6.2 编写C3P0工具类

C3P0提供的核心工具类, ComboPooledDataSource , 如果想使用连接池,就必须创建该类的对象

  • new ComboPooledDataSource(); 使用 默认配置
  • new ComboPooledDataSource("mysql"); 使用命名配置
import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class C3P0Utils {
    //1.创建连接池对象 C3P0对DataSource接口的实现类
    //使用的配置是 配置文件中的默认配置
//    public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //使用指定的配置
    public static ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
    //获取连接的方法
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //释放资源
    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();
            }
        }
    }
}

6.3 测试工具类

#需求: 查询eid为2的员工信息

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

public class C3P0Test {
    public static void main(String[] args) throws SQLException {
        //获得连接
        Connection connection = C3P0Utils.getConnection();
        //获得预处理对象
        String sql="select * from employee where eid=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //设置占位符,处理结果集
        preparedStatement.setInt(1,2);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            System.out.println(resultSet.getInt("eid") + " = " +resultSet.getString("ename")
                    +" : "+resultSet.getInt("age")
                    + " : " + resultSet.getString("sex")
                    + " : " + resultSet.getString("salary")
                    + " : " + resultSet.getDate("empdate"));
        }
        //释放资源
        C3P0Utils.close(connection,preparedStatement,resultSet);


    }
}

6.4 常见配置

7 Druid连接池

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

7.1 导入jar包及配置文件

1) 导入 jar

https://repo1.maven.org/maven2/com/alibaba/druid/1.1.23/

2) 导入配置文件

  • properties形式的

  • 可以叫任意名称,可以放在任意目录下,我们统一放到 resources资源目录

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000

7.2 编写Druid工具类

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

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DruidUtils{
    //1.定义成员变量
    public static DataSource dataSource;
    //2.静态代码块
    static{
        try {
            //3.创建属性集对象
            Properties p = new Properties();
            //4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
            InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("mysql_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;
        }
    }
    //释放资源
    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();
            }
        }
    }
}

7.3 测试工具类

#需求: 查询薪资在3000 - 5000元之间的员工

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

public class DruidTest {
    public static void main(String[] args) throws SQLException {
        //获得连接
        Connection connection = DruidUtils.getConnection();
        //获得预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement("select * from employee where salary between ? and ?");
        preparedStatement.setInt(1,3000);
        preparedStatement.setInt(2,5000);
        //处理结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            System.out.println(resultSet.getInt("eid") + " = " +resultSet.getString("ename")
                    +" : "+resultSet.getInt("age")
                    + " : " + resultSet.getString("sex")
                    + " : " + resultSet.getString("salary")
                    + " : " + resultSet.getDate("empdate"));
        }
        //释放资源
        DruidUtils.close(connection,preparedStatement,resultSet);
    }
}

DBUtils工具类

1 DBUtils简介

使用JDBC我们发现冗余的代码太多了,为了简化开发 我们选择使用 DbUtils

Commons DbUtilsApache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。

使用方式:

       DBUtils就是JDBC的简化开发工具包。需要项目导入commons-dbutils-1.6.jar

       http://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi

1.1 Dbutils核心功能介绍

  • 1. QueryRunner 中提供对sql语句操作的API.
  • 2. ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
  • 3. DbUtils,他就是一个工具类,定义了关闭资源与事务处理相关方法.

2 案例相关知识

2.1 表和类之间的关系

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

2.2 JavaBean组件

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

  • 1. 需要实现 序列化接口, Serializable (暂时可以省略)
  • 2. 提供私有字段: private 类型 变量名;
  • 3. 提供 getter setter
  • 4. 提供 空参构造

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

  • 我们可以创建一个 entity,专门用来存放 JavaBean类

public class Employee implements Serializable {
    private int eid;
    private String ename;
    private int age;
    private String sex;
    private double salary;
    private Date empdate;
    //空参 getter setter省略
}

2.3 DBUtils完成 CRUD

2.3.1 QueryRunner核心类

       构造方法

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

       常用方法

  • update(Connection conn, String sql, Object... params) ,用来完成表数据的增加、删除、更新操作
  • query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) ,用来完成表数据的查询操作

2.3.2 QueryRunner的创建

手动模式

//手动方式 创建QueryRunner对象
QueryRunner qr = new QueryRunner();

自动模式

//自动创建 传入数据库连接池对象
QueryRunner qr2 = new QueryRunner(DruidUtils.getDataSource());

自动模式需要传入连接池对象,在DruidUtils类中添加方法getDataSource,返回dataSource对象

//获取连接池对象
public static DataSource getDataSource(){
return dataSource;
}

2.3.3 QueryRunner实现增、删、改操作

核心方法

       update(Connection conn, String sql, Object... params)

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

      步骤

  • 1.创建QueryRunner(手动或自动)
  • 2.占位符方式 编写SQL
  • 3.设置占位符参数
  • 4.执行
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;

public class QueryRunnerTest {
    @Test
    public void QueryRunnerDML() throws SQLException {
        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        //1.创建 QueryRunner 手动模式创建
        QueryRunner qr = new QueryRunner();

        /*增*/
        //2.编写 占位符方式 SQL
        String sql = "insert into employee values(?,?,?,?,?,?)";
        //3.设置占位符的参数
        Object[] param = {null,"张百万",20,"女",10000,"1990-12-26"};
        /*改*/
//        String sql="update employee set sex=? where ename=?";
//        Object[] param={"男","张百万"};
        /*删*/
//        String sql="delete from employee where ename=?";
//        Object[] param={"张百万"};
        //4.执行 update方法
        Connection con = DruidUtils.getConnection();
        int i = qr.update(con, sql, param);
        //5.释放资源
        DbUtils.closeQuietly(con);
    }
}

2.3.4 QueryRunner实现查询操作

2.3.4.1 ResultSetHandler接口简介

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

2.3.4.2 ResultSetHandler 结果集处理类

  • 本例展示的是使用ResultSetHandler接口的几个常见实现类实现数据库的增删改查,可以大大减少代码量,优化程序。
  • 每一种实现类都代表了对查询结果集的一种处理方式
ResultSetHandler 实现类
说明
ArrayHandler
将结果集中的第一条记录封装到一个 Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
ArrayListHandler
将结果集中的每一条记录都封装到一个 Object[] 数组中,将这些数组在封装到 List集合中。
BeanHandler将结果集中第一条记录封装到一个指定的javaBean.
BeanListHandler
将结果集中每一条记录封装到指定的 javaBean 中,再将这些 javaBean 在封装到List集合中
ColumnListHandler将结果集中指定的列的字段值,封装到一个list集合中
KeyedHandler
将结果集中每一条记录封装到M ap<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 表操作。

2.3.4.3 ResultSetHandler 常用实现类测试

  • QueryRunner的查询方法
  • query方法的返回值都是泛型,具体的返回值类型,会根据结果集的处理方式,发生变化
方法
说明
query(String sql, handler ,Object[] param)自动模式创建QueryRunner, 执行查询
query(Connection con,String sql,handler,Object[] param)手动模式创建QueryRunner, 执行查询
  • 创建一个测试类, ResultSetHandler接口的几个常见实现类进行测试

查询id5的记录,封装到数组中

查询所有数据,封装到List集合中

查询id5的记录,封装到指定JavaBean

查询薪资大于 3000 的所员工信息,封装到JavaBean中再封装到List集合中

查询姓名是 张百万的员工信息,将结果封装到Map集合中

查询所有员工的薪资总额

@Test
    public void QueryRunnerDQL() throws SQLException {
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        /*查询id为5的记录,封装到数组中*/
//        String sql="select * from employee where eid=?";
//        Object[] parms={2};
//        //获取数据
//        Object[] query=  qr.query(sql,new ArrayHandler(),parms);
//        System.out.println(Arrays.toString(query));

        /*查询所有数据,封装到List集合中*/
//        String sql="select * from employee";
//        List<Object[]> query=qr.query(sql,new ArrayListHandler());
//        for(Object[] q :query){
//            System.out.println(Arrays.toString(q));
//        }
        /*查询id为3的记录,封装到指定JavaBean中*/
//        String sql="select * from employee where eid=?";
//        Object[] parms={3};
//        Employee query=qr.query(sql,new BeanHandler<Employee>(Employee.class),parms);
//        System.out.println(query);
        /*查询薪资大于 3000 的所员工信息,封装到JavaBean中再封装到List集合中*/
//        String sql="select * from employee where salary>3000";
//        List<Employee> query=qr.query(sql, new BeanListHandler<Employee>(Employee.class));
//        for(Employee e :query){
//            System.out.println(e);
//        }
        /*查询姓名是 张百万的员工信息,将结果封装到Map集合中*/
//        String sql="select * from employee where ename='张百万'";
//        Map<String, Object> map=qr.query(sql,new MapHandler());
//        Set<Map.Entry<String, Object>> entries = map.entrySet();
//        for (Map.Entry<String, Object> e:entries){
//            System.out.println(e.getKey()+"="+e.getValue());
//        }
        /*查询所有员工的薪资总额*/
        String sql="select sum(salary) from employee";
        Double sumsalary = (Double)qr.query(sql, new ScalarHandler());
        System.out.println(sumsalary);


    }

3.数据库批处理

3.1 什么是批处理

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

       举例: 送货员的工作:

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

3.2 实现批处理

StatementPreparedStatement都支持批处理操作,这里我们介绍一下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) )

4) 测试向表中插入 1万条数据

    @Test
    public void batchIndex() throws SQLException {
        Connection connection = DruidUtils.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement("insert into testbatch(uname) values(?)");
        for (int i = 0; i <10000 ; i++) {
            preparedStatement.setString(1,"小明"+i);
            //将SQL添加到批处理 列表
            preparedStatement.addBatch();

        }
        //统一执行
        preparedStatement.executeBatch();
        DruidUtils.close(connection,preparedStatement);

    }

4.MySql元数据

4.1 什么是元数据

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

4.2 常用命令

-- 元数据相关的命令介绍

命令功能
show status;
查看服务器当前状态
select version();
查看MySQl的版本信息 
show columns from table_name;
查询表中的详细信息
show index from table_name;
显示数据表的详细索引信息
show databases;
列出所有数据库
show tables ;
显示当前数据库的所有表
select database();
获取当前的数据库名

4.3 使用JDBC 获取元数据

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

4.3.1 常用类介绍

JDBC中描述元数据的类

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

获取元数据对象的方法 : getMetaData ()

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

DatabaseMetaData的常用方法

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

ResultSetMetaData的常用方法

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

4.3.2 代码示例

    @Test
    public void metaDataTest() throws SQLException {
        Connection connection = DruidUtils.getConnection();
        //获取描述数据库的元数据对象
        DatabaseMetaData dataBaseMetaData = connection.getMetaData();
        System.out.println("数据库url"+dataBaseMetaData.getURL());
        System.out.println("当前数据库的用户名"+dataBaseMetaData.getUserName());
        System.out.println("数据库产品名称"+dataBaseMetaData.getDatabaseProductName());
        System.out.println("数据库的版本号"+dataBaseMetaData.getDatabaseProductVersion());
        System.out.println("驱动程序的名称"+dataBaseMetaData.getDriverName());
        System.out.println("判断数据库是否只允许只读"+dataBaseMetaData.isReadOnly());
        
        
        
        PreparedStatement preparedStatement = connection.prepareStatement("select ename from employee where salary>?");
        //获取描述结果集的元数据对象
        ResultSetMetaData resultSetMetaData = preparedStatement.getMetaData();

        System.out.println("当前结果集有多少列"+resultSetMetaData.getColumnCount());
        System.out.println("获得指定列号的列名"+resultSetMetaData.getColumnName(1));
        System.out.println("获得指定列号列的类型"+resultSetMetaData.getColumnTypeName(1));

        DruidUtils.close(connection,preparedStatement);
    }

XML  

1. XML基本介绍

1.1 概述

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

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

特点

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

1.2 XML的作用

      XML能做什么?

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

2. XML的语法

2.1 XML文档声明格式

  • 文档声明必须为结束;
  • 文档声明必写在第一行;
<?xml version="1.0" encoding="UTF-8"?>

1) 语法格式:

2) 属性说明:

        versioin:指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0

        encoding:指定当前文档的编码。可选属性,默认值是utf-8

2.2 元素

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

元素的命名规则

  • 1. 不能使用空格,不能使用冒号
  • 2. xml 标签名称区分大小写
  • 3. XML 必须有且只有一个根元素

语法格式:

<users><users>
1) XML 必须有且只有一个根元素,它是所有其他元素的父元素,比如以下实例中 users 就是根元素:
<?xml version="1.0" encoding="utf-8" ?> 
<users> </users>
2) 普通元素的结构开始标签、元素体、结束标签组成。
<hello> 大家好 </hello>
3) 元素体:元素体可以是元素,也可以是文本
<hello> 
    <a>你好</a> 
</hello>
4) 空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合
<close/>

2.3 属性

<bean id="" class=""> </bean>
  • 1. 属性是元素的一部分,它必须出现在元素的开始标签中
  • 2. 属性的定义格式:属性名=属性值,其中属性值必须使用单引或双引
  • 3. 一个元素可以有0~N个属性,但一个元素中不能出现同名属性
  • 4. 属性名不能使用空格、冒号等特殊字符,且必须以字母开头

2.4 注释

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

2.5 使用XML 描述数据表中的数据

<?xml version="1.0" encoding="UTF-8" ?>
<employees>
    <employee eid="2">
        <ename>林黛玉</ename>
        <age>20</age>
        <sex>女</sex>
        <salary>5000</salary>
        <empdate>2019-03-14</empdate>
    </employee>
    <employee eid="3">
        <ename>杜甫</ename>
        <age>40</age>
        <sex>男</sex>
        <salary>15000</salary>
        <empdate>2010-01-01</empdate>
    </employee>
</employees>
 

3. XML约束

       在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。

常见的xml约束:

  • DTD
  • Schema

作为程序员只要掌握两点

会阅读 会引入 不用自己编写

3.1 DTD约束

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

3.1.1 编写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 #REQUIRED
        student子元素中 有一个ID属性叫做 number,是必须填写的
        ID: 唯一 值只能是字母或者下划线开头
        -->

3.1.2 引入DTD

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

本地:<!DOCTYPE 根据签名SYSTEM "dtd文件的位置">

网络:<!DOCTYPE 根据签名PUBLIC "dtd文件名字" "dtd文件的位置URL">

student.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
    <student number="s1">
        <name>小斌</name>
        <age>22</age>
        <sex>男</sex>
    </student>
    <student number="s2">
        <name>广坤</name>
        <age>55</age>
        <sex>男</sex>
    </student>
</students>
 

3.2 Schema约束

3.2.1 什么是Schema

  • 1. Schema是新的XML文档约束, DTD强大很多,是DTD 替代者;
  • 2. Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml
  • 3. Schema 功能更强大,内置多种简单和复杂的数据类型
  • 4. Schema 支持命名空间 (一个XML中可以引入多个约束文档)

3.2.2 Schema约束示例

student.xsd

<?xml version="1.0"?>
<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的根元素:

3.2.3 XML引入Schema约束

xml中引入schema约束的步骤:

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

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

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

<?xml version="1.0" encoding="UTF-8" ?>
<students
xmlns="http://www.lagou.com/xml"
>
</students>

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

<?xml version="1.0" encoding="UTF-8" ?>
<students
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
</students>

4) 引入的命名空间跟哪个xsd文件对应?

使用schemaLocation来指定:两个取值:第一个为命名空间 第二个为xsd文件的路径

<?xml version="1.0" encoding="UTF-8" ?>
<students
xmlns="http://www.lagou.com/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
</students>

5) 命名空间:指的是一个环境,所用的标签来自于哪个环境定义的。

 

6) student.xml

<?xml version="1.0" encoding="UTF-8" ?>
<students
        xmlns="http://www.lagou.com/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
    <student number="hehe_1234">
        <name>张百万</name>
        <age>25</age>
        <sex>female</sex>
    </student>
    <student number="hehe_0000">
        <name>小斌</name>
        <age>20</age>
        <sex>male</sex>
    </student>
</students>

4. XML 解析

4.1 解析概述

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

4.2 XML解析方式

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

  • DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。

                        优点:元素与元素之间保留结构关系,故可以进行增删改查操作。

                        缺点:XML文档过大,可能出现内存溢出显现。

  • SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)

                优点:占用内存少 处理速度快,可以处理大文件

                缺点:只能读,逐行后将释放资源。

4.3 XML常见的解析器

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

JAXPsun公司提供的解析器,支持DOMSAX两种思想

DOM4J:一款非常优秀的解析器 , Dom4j是一个易用的、开源的库,用于XMLXPathXSLT。它应用于Java平台,采用了Java集合框架并完全支持DOMSAXJAXP

Jsoupjsoup 是一款Java HTML解析器 ,也可以解析XML

PULLAndroid内置的XML解析方式,类似SAX

4.4 dom4j 的使用

4.4.1 导入JAR

https://dom4j.github.io/

4.4.2 API介绍

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

常用API如下:

  • SaxReader对象

             read(…) 加载执行xml文档

  • Document对象

             getRootElement() 获得根元素

  • Element对象

             elements(…) 获得指定名称的所有子元素。可以不指定名称

             element(…) 获得指定名称的第一个子元素。可以不指定名称

             getName() 获得当前元素的元素名

             attributeValue(…) 获得指定属性名的属性值

             elementText(…) 获得指定名称子元素的文本值

             getText() 获得当前元素的文本内容

4.4.3 准备xml文件

编写user.xsd schema约束

<?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="users" type="usersType"/>
    <xsd:complexType name="usersType">
        <xsd:sequence>
            <xsd:element name="user" type="userType" minOccurs="0"
                         maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="userType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="age" type="ageType" />
            <xsd:element name="hobby" type="hobbyType" />
        </xsd:sequence>
        <xsd:attribute name="id" type="numberType" use="required"/>
    </xsd:complexType>
    <xsd:simpleType name="ageType">
        <xsd:restriction base="xsd:integer">
            <xsd:minInclusive value="0"/>
            <xsd:maxInclusive value="100"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="hobbyType">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="抽烟"/>
            <xsd:enumeration value="喝酒"/>
            <xsd:enumeration value="烫头"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="numberType">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="\d"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

编写user.xml 引入约束

<?xml version="1.0" encoding="UTF-8" ?>
<users
        xmlns="http://www.lagou.com/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
    <user id="1">
        <name>张百万</name>
        <age>20</age>
        <hobby>抽烟</hobby>
    </user>
    <user id="2">
        <name>于谦</name>
        <age>50</age>
        <hobby>喝酒</hobby>
    </user>
    <user id="3">
        <name>刘能</name>
        <age>40</age>
        <hobby>烫头</hobby>
    </user>
</users>

4.4.4 读取XML

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.util.List;

public class xmlTest {

    @Test
    public void xmlread() throws DocumentException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("resource\\some.xml");
        Element rootElement = document.getRootElement();
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            System.out.print(element.attributeValue("id"));
            System.out.print(element.elementText("name"));
            System.out.print(element.elementText("age"));
            System.out.print(element.elementText("hobby"));
            System.out.println();
        }
    }
}

4.5 xpath方式读取xml

4.5.1 xpath介绍

  • XPath 是一门在 XML 文档中查找信息的语言。 可以是使用xpath查找xml中的内容。

XPath 的好处

由于DOM4J在解析XML时只能一层一层解析,所以当XML文件层数过多时使用会很不方便,结合XPATH就可以直接获取到某个元素

1)需要再导入 jaxen-1.1-beta-6.jar

http://www.java2s.com/Code/Jar/j/Downloadjaxen11beta6jar.htm

4.5.2 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的属性值是

4.5.3 API介绍

2) 常用方法:

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

       Node: 节点对象

4.5.4 Xpath读取XML

3) 数据准备 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>

4) 代码示例

1. 使用selectSingleNode方法 查询指定节点中的内容

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;

public class xmlTest {

    @Test
    public void xmlread() throws DocumentException {
        //1.创建解析器对象
        SAXReader saxReader = new SAXReader();
        //2.获取文档对象
        Document document = saxReader.read("resource\\some.xml");
        //3.调用 selectSingleNode() 方法,获取name节点对象
        Node node = document.selectSingleNode("//book/name");
        System.out.println(node.getText());
        //4.获取第二本书的名称
        node = document.selectSingleNode("//book[2]/name");
        System.out.println(node.getText());
        //5.获取第一个book节点的 id属性的值
        node=document.selectSingleNode("//book/attribute::id");
        System.out.println(node.getText());
        //6.获取最后一个book节点的 id属性的值
        node=document.selectSingleNode("//book[last()]/attribute::id");
        System.out.println(node.getText());
        //7.获取id属性值为 book2的 书名
        node=document.selectSingleNode("//book[@id='book2']/name");
        System.out.println(node.getText());
    }
}

3. 使用 selectNodes()方法 获取对应名称的所有节点

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.util.List;

public class xmlTest {

    @Test
    public void xmlread() throws DocumentException {
        //1.创建解析器对象
        SAXReader saxReader = new SAXReader();
        //2.获取文档对象
        Document document = saxReader.read("resource\\some.xml");
        //3.获取所有节点,打印节点名
        List<Node> nodes = document.selectNodes("//*");
        for (Node node : nodes) {
            System.out.println(node.getName());
        }

        //4.获取所有的书名
        nodes=document.selectNodes("//book/name");
        for (Node node : nodes) {
            System.out.println(node.getText());
        }
        //5.获取指定 id值为book1的节点的所有 内容
        nodes=document.selectNodes("//book[@id='book1']/*");
        for (Node node : nodes) {
            System.out.println(node.getText());
        }
    }
}

5. JDBC自定义XML

5.1 定义配置文件

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

<?xml version="1.0" encoding="utf-8" ?>
<jdbc>
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db5?characterEncoding=UTF-8&amp;serverTimezone=UTC</property>
    <property name="user">root</property>
    <property name="password">123456</property>

</jdbc>

jdbc-confifig.xml

5.2 编写工具类(配置式)

2) 编写工具类 ,使用xpath 读取数据库信息

3) 测试 : 获取所有员工的姓名

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;

import java.sql.*;

public class JDBCXmlUtils {
    public static String DRIVER;
    public static String URL;
    public static String USER;
    public static String PASSWORD;
    static {
        SAXReader saxReader = new SAXReader();
        Document doc = null;
        try {
            doc = saxReader.read("JDBC_task/resource/jdbc_config.xml");
            DRIVER=doc.selectSingleNode("//property[@name='driverClass']").getText();
            URL=doc.selectSingleNode("//property[@name='jdbcUrl']").getText();
            USER=doc.selectSingleNode("//property[@name='user']").getText();
            PASSWORD=doc.selectSingleNode("//property[@name='password']").getText();
            Class.forName(DRIVER);
        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
    public static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
        return connection;
    }

    public static void main(String[] args) throws SQLException {
        Connection con=JDBCXmlUtils.getConnection();
        Statement statement = con.createStatement();
        ResultSet resultSet = statement.executeQuery("select * from employee");
        while (resultSet.next()){
            System.out.println(resultSet.getString("ename"));

        }
        resultSet.close();
        statement.close();
        con.close();
    }

}
课堂作业: https://gitee.com/bigload/java_big_data/tree/master/mysqltest
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值