mysql(八)

10,JDBC(重点)

10.1 数据库驱动

驱动:声卡,显卡,数据库
在这里插入图片描述
我们的程序会通过数据库驱动,和数据库打交道

10.2 JDBC

SUN 公司为了简化 开发人员的(对数据库的统一操作),提供了一个(java操作数据库的规范),俗称 JDBC

这些规范的实现由具体厂商去做~

对于开发人员来说,我们只需要掌握JDBC接口的操作即可!

架构——没有什么是加一层解决不了的

在这里插入图片描述
java.sql

javax.sql

还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar

第一个 JDBC 程序

创建测试数据库

CREATE DATABASE `jdbcStudy` CHARACTER SET utf8 COLLATE utf8_general_ci;

USE `jdbcStudy`;

CREATE TABLE `users`(
 `id` INT PRIMARY KEY,
 `NAME` VARCHAR(40),
 `PASSWORD` VARCHAR(40),
 `email` VARCHAR(60),
 birthday DATE
);

 INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`)
VALUES(1,zhangsan,123456,zs@sina.com,1980-12-04),
(2,lisi,123456,lisi@sina.com,1981-12-04),
(3,wangwu,123456,wangwu@sina.com,1979-12-04)
  1. 创建一个普通项目

  2. 导入数据库驱动

    将包复制到lib文件夹中

    右键——Add as libray

  3. 编写测试代码

    package lesson01;
    
    import java.sql.*;
    
    // 我的第一个JDBC程序
    public class JdbcFirstDemo {
        //1.加载驱动
        /**
         * 固定写法加载驱动
         * mysql 8.0的驱动写法: com.mysql.cj.jdbc.Driver
         */
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            Class.forName("com.mysql.jdbc.Driver");
            //2.用户信息和url
            // mysql8 要加时区 报错显示时区问题在url后面加&serverTimezone=GMT%2B8 连接错误就改成false
            String url="jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";
            String username="root";
            String password="123456";
    
            //3.连接成功,数据库对象
            Connection connection=DriverManager.getConnection(url, username, password);
            //4.执行SQL的对象
            Statement statement=connection.createStatement();
            //5.执行SQL对象 去 执行SQL
            String sql="SELECT * FROM users";
            ResultSet resultSet=statement.executeQuery(sql); // 返回结果集,结果集中封装了我们全部查询出来的结果
    
            while(resultSet.next()){
                System.out.println("id "+resultSet.getObject("id"));
                System.out.println("NAME "+resultSet.getObject("NAME"));
                System.out.println("PASSWORD "+resultSet.getObject("PASSWORD"));
                System.out.println("email "+resultSet.getObject("email"));
                System.out.println("birthday "+resultSet.getObject("birthday"));
                System.out.println("========================");
            }
            //6.释放连接
            resultSet.close();
            statement.cancel();
            connection.close();
        }
    }
    

    步骤总结:

    1. 加载驱动
    2. 连接数据库 DriverManager
    3. 获得执行sql的对象 statement
    4. 执行完了获得返回的结果集
    5. 释放连接

DriverManager

        /**
         * 固定写法加载驱动
         * mysql 8.0的驱动写法: com.mysql.cj.jdbc.Driver
         * 进行反射调用会默认进行类初始化
         */
//        DriverManager.registerDriver(new com.mysql.jdbc.Driver);
        Class.forName("com.mysql.jdbc.Driver"); //固定写法加载驱动
       Connection connection=DriverManager.getConnection(url, username, password);
// connection 代表数据库
// 数据库设置自动提交
// 事务提交
// 事务回滚
        connection.rollback();
        connection.commit();
        connection.setAutoCommit();

URL

String url="jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";

// mysql 默认3306
// "jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
// 协议://主机地址:端口号/数据库名?参数1&参数2&参数3
// oralce--1521
// jdbc:oracle:thin:@localhost:1521:sid

Statement 执行SQL的对象 prepareStatement 执行SQL对象

String sql="SELECT * FROM users";      //编写sql  
statement.executeQuery();// 执行查询 返回ResultSet
statement.executeUpdate();//更新,插入,删除 都是这个,返回一个受影响的行数
statement.execute();      // 执行任何SQL
statement.executeBatch(); // 批处理

ResultSet 查询的结果集:封装了所有的查询结果

获得指定的数据类型


        resultSet.getObject(); // 在不知道列类型的情况下使用
        // 如果知道列的类型就使用指定的类型
        resultSet.getString();
        resultSet.getInt();
        resultSet.getFloat();
        resultSet.getDate();

遍历,指针

        resultSet.beforeFirst(); // 移动到最前面
        resultSet.afterLast();  // 移动到最后面
        resultSet.next() // 移动到下一个数据
        resultSet.previous() // 移动前一行
        resultSet.absolute(row) //移动到指定行

释放资源

//6.释放连接
        resultSet.close();
        statement.cancel();
        connection.close();  // 耗内存

10.4 statement对象

在这里插入图片描述

CRUD操作-create

使用 executeUpdate(String sql) 方法完成数据添加操作,示例操作:

Statement st=conn,createStatement();
String sql="insert into user(...) values(...)";
int nums=st.executeUpdate(sql);
if(num>0){
    System.out.println("插入成功!!!");
}

CURD操作-delete

使用executeUpdate(String sql)方法完成数据删除操作,示例操作:

Statement st=conn,createStatement();
String sql="delete from user where id=";
int nums=st.executeUpdate(sql);
if(num>0){
    System.out.println("删除成功!!!");
}

CRUD操作-update

使用使用executeUpdate(String sql)方法完成数据修改操作,示例操作:

Statement st=conn,createStatement();
String sql="update user set name='' where name=''";
int nums=st.executeUpdate(sql);
if(num>0){
    System.out.println("修改成功!!!");
}

CRUD 操作-read

使用使用executeQuery(String sql)方法完成数据查询操作,示例操作:

Statement st=conn,createStatement();
String sql=" select * from user where id=1 ";
ResultSet rs=st.executeQuery(sql);
while(rs.next()){
    // 根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}

代码实现

unicode 为了实现中文

时区 ServerTimezone=Asia/Shanghai

目的类属性和功能分离,方法就是配置文件+工具类

  1. 提取工具类

  2. 编写增删改的方法,executeUpdate

    以删除为例子

    package lesson02;
    
    import lesson02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestDelete {
        public static void main(String[] args) {
            Connection conn=null;
            Statement st=null;
            ResultSet rs=null;
    
            try {
                conn= JdbcUtils.getConnection();
                st=conn.createStatement();
                String sql="DELETE FROM `users` WHERE `id`=4";
                int i=st.executeUpdate(sql);
                if(i>0){
                    System.out.println("删除成功");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            finally {
                JdbcUtils.release(conn,st,rs);
            }
        }
    }
    
  3. 查询

    静态代码块在内存加载的时候优先执行,也就是每次调用这个类都会去先执行静态代码块

    package lesson02;
    
    import lesson02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestQuery {
        public static void main(String[] args) {
            Connection conn=null;
            Statement st=null;
            ResultSet rs=null;
            try {
                conn=JdbcUtils.getConnection();
                st=conn.createStatement();
                String sql="select * from users where id=1";
                rs=st.executeQuery(sql);
                while (rs.next()){
                    System.out.println(rs.getString("NAME"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            finally {
                JdbcUtils.release(conn,st,rs);
            }
        }
    }
    

SQL注入的问题

SQL 注入(SQL Injection) 是发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。

主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。

因为正常的sql语句就是 where name =’测试用户1‘

例子

SELECT * FROM users WHERE `NAME`='wawa' AND `password`='123456'

10.5 PrepareStatement对象

PrepareStatement 可与防止SQl注入,效率更好

  1. 新增
  2. 删除
  3. 更新
  4. 查询
package com.hjy.lesson03;

public class TestInsert {
    public static void main(String[] args){
        Connection conn=null;
        PrePareStatement st=null;
        ResultSet rs=null;
        try{
            conn=JdbcUitls.getConnection();
            // 区别
            String sql="insert into users(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";、
            st=conn.prepareStatement(sql);

            conn.prepareStatement()// 预编译的SQL 先写SQL 但是不执行
            // 手动给参数赋值
            st.setInt(1,4);
            st.setString(2,"huangjieyu");
            st.setString(3,"123456");
            st.setString(4,"112233@qq.com");
            // 注意点:sql.Date   数据库
            //       util.Date  java  new Date().getTime() 获得时间戳
            st.setDate(5,new java.sql.Date(new Date().getTime()));

            // 执行
            int i= st.executeUpdate();
            if(i>0){
                System.out.println("插入成功");
            }

        }catch(SQlException e){
            e.printStackTrace();
        }
        finally{
            JdbcUtils.release(conn,st,null)
        }
    }
}

删除

String sql="delete from user where id=?";
st.setInt(1,4)

修改

String sql="update users set `NAME`=? where id=?";
st.setString(1,'huangjieyu');
st.setInt(2,1)

查询

package com.hjy.lesson03;

public class TestQuery {
    public static void main(String[] args){
        Connection conn=null;
        PrePareStatement st=null;
        ResultSet rs=null;
        try{
            conn=JdbcUitls.getConnection();
            // PrepareStatement 防止SQL注入的本质,把传进去的参数当作字符
            // 假设其中存在转义字符,就直接忽略,‘ 会被直接转义
            // 区别
            String sql="select * from users where id=?";
            st=conn.prepareStatement(sql);

            conn.prepareStatement()// 预编译的SQL 先写SQL 但是不执行
            // 手动给参数赋值
            st.setInt(1,4);
            
            // 注意点:sql.Date   数据库
            //       util.Date  java  new Date().getTime() 获得时间戳
            st.setDate(5,new java.sql.Date(new Date().getTime()));

            // 执行
            rs= st.executeQuery();
            if(re.next()){
                System.out.println(rs.getString("NAME"));
            }

        }catch(SQlException e){
            e.printStackTrace();
        }
        finally{
            JdbcUtils.release(conn,st,rs)
        }
    }
}

10.6 IDEA连接数据库

在这里插入图片描述

连接不成功

在URL栏中语句最后面添加?serverTimezone=UTC

设置-schemas 选择你要连接的数据库 双击数据库 查询内容

在这里插入图片描述

双击数据库里的列-提交 DB
在这里插入图片描述
在这里插入图片描述
连接失败,查看原因
在这里插入图片描述

10.7 事务

要么都成功,要么都失败

ACID原则

原子性:要么全部完成,要么都不完成

一致性:总数不变

隔离性:多个进程互不干扰

持久性:一旦提交不可逆,持久化到数据库了


隔离性的问题:

脏读:一个事务读取了另一个没有提交的事务

不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变

虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致


代码实现

  1. 开启事务conn.setAutoCommit(false);
  2. 一组业务执行完毕,提交事务
  3. 可以在catch语句中显式的定义 回滚语句,但默认失败就会回滚
package lesson04;

import lesson02.utils.JdbcUtils;

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

public class TestTransaction2 {
    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement st=null;
        ResultSet rs=null;
        try {
            conn=JdbcUtils.getConnection();
            // 关闭数据库的自动提交功能,关闭自动提交过后,自动会开启事务
            conn.setAutoCommit(false);

            String sql1="update account set money=money-100 where name='A'";
            String sql2="update account set money=money+100 where name='B'";
            st=conn.prepareStatement(sql1);
            st.executeUpdate();
            st=conn.prepareStatement(sql2);
            st.executeUpdate();

//            int x=1/0; // 报错

            // 业务完毕,提交事务
            conn.commit();
            System.out.println("成功!");
        } catch (SQLException e) {
            // 如果失败,则默认回滚
            // 下面是显式定义
            try {
                conn.rollback(); // 如果失败就回滚
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

10.8 数据库连接池(DBCP-CP30连接池)

数据库连接—执行完毕—释放 连接—释放 十分浪费系统资源

池化技术:准备一些预先的资源,过来就连接预先准备好的

例子:

—开门—服务—关门

—开门—业务员:等待连接—服务—

常用连接数 10

最小连接数:10

最大连接数:100 业务最高承载上限

超过最高承载上限,排队等待

等待超时:100ms(就自己走了)


编写连接池,实现一个接口 DataSource

开源数据源实现(拿来即用)

DBCP

C3P0

Druid:阿里巴巴 springboot

使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!

DBCP

需要用到的jar包

commons-logging-1.2.jar

需要用到的jar包都可以在阿里云仓库找到 commons-dbcp-1.4.jar commons-pool-1.6.ja

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=true
username=root
password=123456

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。

connectionProperties=useUnicode=true;characterEncoding=utf8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
package lesson05.utils;


import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class jdbcutils_dbcp {

    private static   DataSource source=null;
    static {
        try {

            InputStream in =jdbcutils_dbcp.class.getClassLoader().getResourceAsStream("dbcp.properties");
            Properties properties = new Properties();
            properties.load(in);
            //创建数据源 工厂模式--> 创建对象
            source = BasicDataSourceFactory.createDataSource(properties);


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


    // 获取连接
    public static Connection getConnection() throws SQLException {
        //从数据源中获取连接
        return source.getConnection();
    }
    //释放连接资源
    public static void  release(Connection conn, Statement st, ResultSet rs) throws SQLException {
        if(conn!=null) conn.close();

        if(st!=null) st.close();

        if(rs!=null) rs.close();
    }
}
package lesson05;

import lesson05.utils.jdbcutils_dbcp;

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

public class testdbcp {
    public static void main(String[] args) throws SQLException {
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet rs=null;
        try {
            //获取连接
            connection = jdbcutils_dbcp.getConnection();
            //sql
            String sql="SELECT * from users WHERE id>?";
            //预编译sql
            statement= connection.prepareStatement(sql);
            //设置参数
            statement.setObject(1,1);
            //执行sql
            rs=statement.executeQuery();
            //遍历结果
            while (rs.next()){
                System.out.println(rs.getObject("NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            jdbcutils_dbcp.release(connection,statement,rs);
        }
    }
}

c3p0

导入jar包 c3p0-0.9.5.5.jar mchange-commons-java-0.2.19.jar

xml不用读 加载的时候可以自动匹配

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--
    c3p0的缺省(默认)配置 多套数据源
    如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource();"这样写就表示使用的是c3p0的缺省(默认)-->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&amp;characterEncoding=utf8&amp;uesSSL=true&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>

    </default-config>

    <!--
    c3p0的命名配置
    如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource("MySQL");"这样写就表示使用的是mysql的缺省(默认)-->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&amp;characterEncoding=utf8&amp;uesSSL=true&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

package lesson05.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

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 JdbcUtils_C3P0 {
    private static ComboPooledDataSource source=null;
    static {
        try {
            // 代码版配置
//            source=new ComboPooledDataSource();
//            source.setDriverClass();
//            source.setUser();
//            source.setPassword();
//            source.setJdbcUrl();
//            source.setMaxPoolSize();
//            source.setMinPoolSize();

            //创建数据源 工厂模式--> 创建对象
            source = new ComboPooledDataSource("MySQL");//配置文件写法


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


    // 获取连接
    public static Connection getConnection() throws SQLException {
        //从数据源中获取连接
        return source.getConnection();
    }
    //释放连接资源
    public static void  release(Connection conn, Statement st, ResultSet rs) throws SQLException {
        if(conn!=null) conn.close();

        if(st!=null) st.close();

        if(rs!=null) rs.close();
    }
}
package lesson05;

import lesson05.utils.JdbcUtils_C3P0;
import lesson05.utils.jdbcutils_dbcp;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class TestC3P0 {
    public static void main(String[] args) throws SQLException {
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet rs=null;
        try {
            //获取连接
            connection = JdbcUtils_C3P0.getConnection(); // 原来是自己的实现的,现在是别人的
            //sql
            String sql="SELECT * from users WHERE id>?";
            //预编译sql
            statement= connection.prepareStatement(sql);
            //设置参数
            statement.setObject(1,1);
            //执行sql
            rs=statement.executeQuery();
            //遍历结果
            while (rs.next()){
                System.out.println(rs.getObject("NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils_C3P0.release(connection,statement,rs);
        }
    }
}

结论

void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
try {
//获取连接
connection = JdbcUtils_C3P0.getConnection(); // 原来是自己的实现的,现在是别人的
//sql
String sql=“SELECT * from users WHERE id>?”;
//预编译sql
statement= connection.prepareStatement(sql);
//设置参数
statement.setObject(1,1);
//执行sql
rs=statement.executeQuery();
//遍历结果
while (rs.next()){
System.out.println(rs.getObject(“NAME”));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils_C3P0.release(connection,statement,rs);
}
}
}


> 结论

无论使用什么数据源,本质还是一样的,DataSource接口还是一样的,方法就不会变
课程笔记都是看的狂神的课程做的,感谢。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值