JDBC(重点)

1.数据库驱动

驱动:声卡,显卡,数据库等

我们的程序会通过 数据库 驱动,和数据库打交道!

2.JDBC

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

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

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

java.sql

javax.sql

还需要导入一个数据库驱动包

3.第一个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,'zhansan','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.导入数据库驱动

3.编写测试代码

package com.zhang.test01;
import java.sql.*;

// 我的第一个JDBC程序
public class jdbc_firstdemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动

        //2.用户信息,数据库对象 (url)     //用?连接参数
        //  支持中文编码       字符集utf8         // 使用安全的连接
        //useUnicode=true&characterEncoding=utf8&useSSL=true
        String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username="root";
        String password="       ";

        //3.连接成功,数据库对象 Connection 代表数据库
        //驱动管理.获得链接
        Connection conn= DriverManager.getConnection(url,username,password);

        //4.执行sql的对象
        String sql="SELECT*FROM users";
        Statement statement=(Statement) conn.createStatement();
        ResultSet resultSet= statement.executeQuery(sql);//返回的结果集

        //5.执行sql对象
        while (resultSet.next()){
            System.out.println("id=" +resultSet.getObject("id"));
            System.out.println("name=" +resultSet.getObject("NAME"));
            System.out.println("pwd=" +resultSet.getObject("PASSWORD"));
            System.out.println("email=" +resultSet.getObject("email"));
            System.out.println("birth=" +resultSet.getObject("birthday"));
            System.out.println("================================");
        }

        //6.释放连接
        resultSet.close();
    }
}



DriverManager

//1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动

        Connection conn= DriverManager.getConnection(url,username,password);
//Connection代表数据库
//数据库自动提交
//事务提交
//事务回滚
        conn.commit();
        conn.rollback();
        conn.setAutoCommit();

URL

//2.用户信息,数据库对象 (url)     //用?连接参数
        //  支持中文编码       字符集utf8         // 使用安全的连接
        //useUnicode=true&characterEncoding=utf8&useSSL=true
        String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
// maysql 默认端口3306
//协议:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3(mysql写法)
//oracle 默认端口:1521
//jdbc:oracle:thin:@localhost:1521:sid
        String username="root";
        String password="zwy262425";

Statement 执行sql的对象 prepareStatement();执行sql的对象

//编写sql
String sql="SELECT*FROM users";

//       statement.executeQuery();//查询操作返回ResultSet
//        statement.execute();//执行任何sql
//        statement.executeUpdate();//更新,插入,删除,都是用这个,返回结果

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

获得指定的数据类型

resultSet.getObject();//不知道类型的情况下使用
        resultSet.getString();
        resultSet.getInt();
        resultSet.getFloat();
        resultSet.getDate();
...

遍历,指针

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

释放资源,必须写

//6.释放连接
        resultSet.close();
        statement.close();
        conn.close();

4.Statement 对象

使用 Statement 对象。在对数据库只执行一次性存取的时侯,用 Statement 对象进行处理。

查询:查询可以使用Statement对象的executeQuery()和execute()方法。

两个方法的参数均为一个拼接好的String类型字符串SQL。

executeQuery()方法只能执行一条SQL,返回ResultSet对象。

execute()方法应该仅在语句能返回多个 ResultSet 对象、

多个更新计数或ResultSet对象与更新计数的组合时使用。

可以通过getResultSet获取ResultSet对象等等。

executeUpdate:executeUpdate()方法 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQLDDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。

INSERT、UPDATE

或DELETE语句的效果是修改表中零行或多行中的一列或多列,可以根据返回结果判断是否执行成功

executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。

对于CREATE TABLE 或DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零,

所以不能因为其返回0就认为没有成功。

代码实现

0.配置数据库信息

//db.properties  数据库信息文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=zwy262425

1.提取工具类

package com.zhang.test02.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUitls {
    private static String driver=null;
    private static String url=null;
    private static String username=null;
    private static String password=null;

    static {
        try{
            InputStream in = JdbcUitls.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);
            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            username=properties.getProperty("username");
            password=properties.getProperty("password");

            //1.驱动只用加载一次
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public  static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }

    //释放连接资源
    public static void release(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement!=null){
            statement.close();
        }
        if (connection!=null){
            try {
                connection.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

}

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

增:

package com.zhang.test02;

import com.zhang.test02.utils.JdbcUitls;

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

public class TestInSert {
    public static void main(String[] args) throws SQLException {
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        try {
           connection= JdbcUitls.getConnection();//获取数据库链接
           statement= connection.createStatement();//获取SQL执行语句
            String sql="INSERT INTO `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`)\n" +
                    "VALUES(4,'zhangwenyu','123456','123456@qq.com','1999-05-11')";
            int i=statement.executeUpdate(sql);
            if(i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUitls.release(connection,statement,null);
        }
    }
}

删:

package com.zhang.test02;

import com.zhang.test02.utils.JdbcUitls;

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

public class TestDelect {
    public static void main(String[] args) throws SQLException {
        //固定写法
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        try {
            //固定写法
            connection= JdbcUitls.getConnection();//获取数据库链接
            statement= connection.createStatement();//获取SQL执行语句

            String sql="DELETE FROM `users` WHERE `id`=4";

            //Update有关的操作以下也是固定写法
            int i=statement.executeUpdate(sql);
            if(i>0){
                System.out.println("删除成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUitls.release(connection,statement,null);
        }
    }
}

改:

package com.zhang.test02;

import com.zhang.test02.utils.JdbcUitls;

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

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        try {
            connection= JdbcUitls.getConnection();//获取数据库链接
            statement= connection.createStatement();//获取SQL执行语句
            String sql="UPDATE `users` SET `NAME`='xiaoyun',`email`='12345@qq.com' WHERE `id`=1";
            int i=statement.executeUpdate(sql);
            if(i>0){
                System.out.println("更新成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUitls.release(connection,statement,null);
        }
    }
}

3.查询executeQuery

package com.zhang.test02;

import com.zhang.test02.utils.JdbcUitls;

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) throws SQLException {
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        try {
            connection = JdbcUitls.getConnection();
            statement=connection.createStatement();

            //SQL
            String sql="select *from users ";

            resultSet=statement.executeQuery(sql);//查询完毕会返回一个结果集

            while (resultSet.next()){
                System.out.println(resultSet.getString("NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUitls.release(connection,statement,resultSet);
        }
    }
}

SQL注入的问题

sql存在漏洞,会被攻击导致数据泄露SQL会被拼接:因为or存在使得只要满足一个条件即为真

5.PreparedStatement对象

PreparedStatement可以防止sql注入,并且效率更高

增:

public static void main(String[] args) throws SQLException {
    Connection connection=null;
    PreparedStatement preparedStatement=null;
    ResultSet resultSet=null;
    try {
        connection = JdbcUitls.getConnection();
        //区别
        //使用?占位符代替参数
       String sql= "INSERT INTO `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`)\n" +
                "VALUES(?,?,?,?,?)";

       preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行

        //手动传递参数
        preparedStatement.setInt(1,4);//给id赋值
        preparedStatement.setString(2,"xiaoxiao");
        preparedStatement.setString(3,"123456");
        preparedStatement.setString(4,"147147@qq.com");
        //注意点:sql.Date    数据库
        //       java.Date   Java
        //                                             new Date().getTime() 获得时间戳
        preparedStatement.setDate(5,new java.sql.Date(new Date().getTime()));

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

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,preparedStatement,null);
    }
}

删:

public static void main(String[] args) throws SQLException {
    Connection connection=null;
    PreparedStatement preparedStatement=null;
    ResultSet resultSet=null;
    try {
        connection = JdbcUitls.getConnection();
        //区别
        //使用?占位符代替参数
        String sql= "delete from users where id=?";

        preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行

        //手动传递参数
        preparedStatement.setInt(1,4);

        // 执行
        int i = preparedStatement.executeUpdate();
        if (i>0){
            System.out.println("删除成功");
        }

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,preparedStatement,null);
    }
}

改:

public static void main(String[] args) throws SQLException {
    Connection connection=null;
    PreparedStatement preparedStatement=null;
    ResultSet resultSet=null;
    try {
        connection = JdbcUitls.getConnection();
        //区别
        //使用?占位符代替参数
        String sql= "update  users set NAME=? where id=?";

        preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行

        //手动传递参数
        preparedStatement.setString(1,"xiaoyu");
        preparedStatement.setInt(2,3);

        // 执行
        int i = preparedStatement.executeUpdate();
        if (i>0){
            System.out.println("更新成功");
        }

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,preparedStatement,null);
    }
}

查:

public static void main(String[] args) throws SQLException {
    Connection connection=null;
    PreparedStatement preparedStatement=null;
    ResultSet resultSet=null;
    try {
        connection = JdbcUitls.getConnection();
        //区别
        //使用?占位符代替参数
        String sql= "select * from users where id=?";

        preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行

        //手动传递参数
        preparedStatement.setInt(1,3);

        // 执行
        resultSet=preparedStatement.executeQuery();
        while (resultSet.next()){
            System.out.println(resultSet.getString("NAME"));
        }

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,preparedStatement,null);
    }
}

防止sql注入:

public static void main(String[] args) throws SQLException {
    //login("lisi","123456");
    login("''or 1=1","''or 1=1");//技巧,当密码和用户名永远为真时
}
//登录
public static void  login(String name,String password) throws SQLException {
    Connection connection=null;
    PreparedStatement statement=null;
    ResultSet resultSet=null;
    try {
        connection = JdbcUitls.getConnection();
        //SQL
        //PreparedStatement防止sql注入的本质,把传递来的参数当作字符
        //假设其中存在转义字符,比如'会被直接转义
        String sql="SELECT *FROM `users` WHERE `NAME`=? AND `PASSWORD`=?";
        statement=connection.prepareStatement(sql);

        statement.setString(1,name);
        statement.setString(2,password);
        resultSet=statement.executeQuery();//查询完毕会返回一个结果集

        while (resultSet.next()){
            System.out.println(resultSet.getString("NAME"));
            System.out.println(resultSet.getString("PASSWORD"));
            System.out.println("=====================================");
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,statement,resultSet);
    }
}

6.使用IDEA连接数据库

连接成功后可以打开数据库

双击数据库

更新数据

8.事务

要么都成功,要么都失败

ACID原则

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

一致性:总数不变

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

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

隔离性的问题:

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

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

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

代码实现

1.开启事务:

//关闭数据库的自动提交功能,自动开启事务
connection.setAutoCommit(false);//开启事务

2.一组业务执行完毕,提交事务

3.可以在catch语句中显示定义回滚语句,但默认失败就会回滚

public static void main(String[] args) throws SQLException {
    Connection connection=null;
    PreparedStatement st=null;
    ResultSet resultSet=null;
    try {
        connection= JdbcUitls.getConnection();

        //关闭数据库的自动提交功能,自动开启事务
        connection.setAutoCommit(false);//开启事务

        String sql1="update account set money=money-100 where name ='a'";
        st= connection.prepareStatement(sql1);
        st.executeUpdate();

        int x=1/0;//报错

        String sql2="update account set money=money+100 where name ='b'";
        st=connection.prepareStatement(sql2);
        st.executeUpdate();

        //业务完毕,提交事务
        connection.commit();
        System.out.println("成功!");


    } catch (SQLException e) {
        //如果失败,默认回滚,下面try~catch可以不写
        try {
            connection.rollback();//如果失败则回滚事务
        }catch (SQLException e1){
            e1.printStackTrace();
        }
        e.printStackTrace();
    }finally {
        JdbcUitls.release(connection,st,resultSet);
    }
}

9.数据库连接池

数据库连接–执行完毕–释放

连接–释放 十分浪费资源

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

举例:银行业务

----- 开门 – 业务员:等待 – 服务 – 关门(服务关闭)

例如:(以下数字不固定,按自己需求设置)

常用连接数 10个

最小连接池 10个

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

超过上限,排队等待

等待超时:100ms

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

开源数据源(拿来即用)

DBCP

C3P0

Druid:阿里巴巴

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

DBCP

需要用到的jar包

commons-dbcp-1.4,commons-pool-1.6

dbcp简单配置

#连接设置 这里面的名字是DBCP数据源中定义好
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=zwy262425
#指定有连接池错创建的连接的自动提交
defaultAutoCommit=true

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

#driver default指定有连接池所创建的连接的事务级别
#可用值为下列之一:(详情可见javadoc)NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,
defaultTransactionIsolation=READ_UNCOMMITTED

#<!-- 初始化连接 -->
initialSize=10
#<!-- 最大连接数量 -->
maxActive=20
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位  -->
maxWait=10000

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

C3P0

需要导入的jar包

c3p0-0.9.5.5,mchange-commons-java-0.2.19

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 默认配置,如果没有指定则使用这个配置
     如果在代码中“CombPooledDataSource ds=new CombPooledDataSource(),括号为空则为默认”-->
    <default-config>
        <property name="user">root</property>
        <property name="password">zwy262425</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true;characterEncoding=utf8;useSSL=true</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>

        <property name="checkoutTimeout">30000</property>
        <property name="idleConnectionTestPeriod">30</property>
        <property name="initialPoolSize">3</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">2</property>
        <property name="maxStatements">200</property>
    </default-config>

    <!-- 命名的配置,可以通过方法调用实现
    如果在代码中“CombPooledDataSource ds=new CombPooledDataSource(“MySQL”),括号填上自己定义的数据源-->
    <named-config name="MySQL">
        <property name="user">root</property>
        <property name="password">zwy262425</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!-- 如果池中数据连接不够时一次增长多少个 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">20</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">25</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">5</property>
    </named-config>
</c3p0-config>

结论

无论使用什么数据库,本质还是一样的,DataSource接口不会变,当打就不会变

Druid