关闭

java程序员第十八课 -JDBC02

标签: java数据库sun程序员回顾
204人阅读 评论(0) 收藏 举报
分类:

课程回顾:JDBC第一天

JDBC的概述

1.JDBC作用:Java数据库的链接。使用Java代码操作数据库。
2.JDBC接口是SUN提供的接口(规范),各个数据库的生厂商提供接口的实现类(jar包 – 驱动)
3.JDBC开发的步骤
* 加载驱动 Class.forName(“com.mysql.jdbc.Driver”);
* 获取链接 DriverManager.getConnection(url,username,password) url:jdbc:mysql:///day17
* 编写SQL语句
* 获取能执行SQL语句的接口 conn.createStatement();
* 执行SQL语句 stmt.executeQuery(sql); stmt.executeUpdate(sql);
* 返回的结果 while(rs.next){ rs.getXXX(“字段名称”) }
* 释放资源 (可以在finally块中,调用close()销毁链接)
DriverManager

1.加载驱动(过于依赖某个jar包,加载两次)
* Class.forName(“com.mysql.jdbc.Driver”);
2.获取链接
* DriverManager.getConnection(url,username,password) url:jdbc:mysql:///day17
* jdbc: 数据库协议
* mysql: 子协议
Connection接口(链接)

1.创建能执行SQL语句的对象
* Statement接口 – 能执行SQL语句
* PreparedStatement接口(重要) – 能执行SQL语句,可以进行预编译,防止SQL注入漏洞。

2.管理事物
* set autoCommit(false);
* commit(); – 提交事物
* rollback(); – 回滚事物
Statement接口(能执行SQL语句)

1.能执行SQL语句
* stmt.executeQuery(sql);
* stmt.executeUpdate(sql);

2.能执行批处理(MySQL批处理默认没有开启,需要通过参数手动的开启)
ResultSet接口(结果集)

1.如果执行查询语句的时候,把结果集(表格)封装到ResultSet接口中。
2.结果集中存在游标,默认存在第一行数据之前,调用rs.next()移动游标,游标移动到哪一行,获取到该行的数据。
while(rs.next){
// 获取数据
rs.getInt
rs.getString
rs.getObject 返回是Object
}

3.结果集默认只能向下移动,并且不能修改记录。了解设置滚动的结果集。
释放资源

1.可以在finally块中,调用close()销毁链接
JavaEE三层结构

1.SUN公司提出解决问题的方案,WEB层 – 业务层 – 持久层(操作数据库)
2.DAO的模式(规范:提供接口,提供实现类,封装的是表的单个操作(增删改查的操作))
登陆的案例

SQL注入的问题

1.产生:SQL语句使用的拼接的方式,用户在已知用户名的情况下,通过输入一些关键字
* ccc ’ or ’ 1=1
* ccc ’ – ’ 注释

2.解决问题:
* 使用PreparedStatement接口,能执行SQL语句,预编译。先把SQL语句发送到服务器,编译SQL语句。格式就固定了,再传入任何值,做为参数数据。
* 编写SQL语句的时候,使用?占位符代替参数。
数据库中的大的数据

1.文本 字符
2.MP3 字节
批处理

1.把一批SQL语句存入到一个批处理中,执行该批处理。
2.通过参数进行设置
今天内容:JDBC的第二天

事物

事物的概述

1.事物的概述:在同一个事物中,执行的SQL语句,要么都成功,要么都失败。
在MySQL数据库中使用事物

1.在MySQL的数据库中使用事物
* 可以使用命令操作事物
* start transaction; – 开启事物
sql1…
sql2…
* commit; – 提交事物(数据保存数据库,交易完成了)
* rollback; – 回滚事物(数据保存数据库中,恢复最初的状态)

* MySQL数据库中的事物是默认提交的。(你只要写一条SQL语句,该条SQL语句就独占一个事物,写完之后什么都不做,事物也是默认提交了)
    * 设置MySQL的事物不默认提交(在编写SQL语句,事物提交了吗?没有提交。)
        sql1...
        sql2...
    * 手动的提交获取回滚

2.测试MySQL中的事物
create database day18;
use day18;
create table t_account(
id int primary key auto_increment,
name varchar(50),
money double
);

insert into t_account values (null,'美美',10000);
insert into t_account values (null,'冠希',10000);
insert into t_account values (null,'小凤',10000);
insert into t_account values (null,'小花',10000);
insert into t_account values (null,'熊大',10000);

3.模拟测试
* 冠希给美美转1000块钱。(第一种方式)
* 开启事物 start transaction;
* 扣除冠希的1000块钱 update t_account set money = money - 1000 where name = ‘冠希’;
* 给美美加1000块钱 update t_account set money = money + 1000 where name = ‘美美’;
* 先编写查询的语句 select * from t_account;
* 提交或者回顾 rollback;

* 冠希给美美转1000块钱。(第二种方式)
    * show variables like '%commit%';       -- 查询和commit命令相关的属性信息(判断数据库的事物是否是默认提交的)
    * 手动设置不自动提交(默认只针对当前的窗口)
        * set autocommit = off或者0;设置不默认提交。

    * 事物就不默认提交了     set autocommit = 0;
    * 扣除冠希的1000块钱   update t_account set money = money - 1000 where name = '冠希';
    * 给美美加1000块钱        update t_account set money = money + 1000 where name = '美美';
    * 手动提交或者回滚      rollback;

在JDBC中使用事物

1.如果在JDBC中使用事物,先找Connection接口。
2.和事物相关的方法
* void setAutoCommit(boolean autoCommit) – 传入false,设置MySQL事物不是默认提交的。
* void commit() – 提交事物
* void rollback() – 回滚事物
* void rollback(Savepoint savepoint) – 回滚到保存点的位置
3.代码
@Test
public void run(){
Connection conn = null;
PreparedStatement stmt = null;
try {
// 链接链接
conn = MyJdbcUtil.getConnection();
// 添加事物(设置MySQL数据库事物不是默认提交的)
conn.setAutoCommit(false);
// 编写SQL语句
String sql = “update t_account set money = money + ? where name = ?”;
// 预编译SQL
stmt = conn.prepareStatement(sql);
// 传入参数(给冠希扣除1000元)
stmt.setDouble(1, -1000);
stmt.setString(2, “冠希”);
// 先执行
stmt.executeUpdate();
// 添加点异常
int a = 10 / 0;
// 在美美加1000元
stmt.setDouble(1, 1000);
stmt.setString(2, “美美”);
stmt.executeUpdate();
// 提交事物
conn.commit();
} catch (Exception e) {
// 回滚事物
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
MyJdbcUtil.release(stmt, conn);
}
}
回滚到保存点的位置(了解)

1.语句执行的过程中,设置一个保存点,事物回滚到保存点的位置上。
2.方法
* Savepoint sp = conn.setSavepoint(); – 设置保存点
* conn.rollback(sp); – 回滚到保存点的位置上
* conn.commit(); – 提交事物

3.模拟,冠希借给美美10000块钱,(中间添加保存点)美美说我还你100000元。
事物的特性ACID(面试)

1.原子性 :强调的是事物的不可分隔的特性。
2.一致性 :强调的是事物的执行的前后,数据完整性保持一致。
3.隔离性 :强调的多个事物同时操作一条记录,并发访问的问题。
4.持久性 :强调的事物结束后,数据永久的保存在数据库中。
不考虑事物的隔离性,产生一些问题

1.脏读 – 一个事物读取到另一个事物没有提交的数据
2.不可重复读 – 一个事物读取到了另一个事物提交的数据,导致两次查询的结果不一致。(强调的是更新数据的操作)
3.虚读(幻读) – 一个事物读取到了另一个事物提交的数据,导致两次查询的结果不一致。(强调的是添加数据的操作)
设置事物的隔离性(在数据库中进行设置)

1.read uncommitted – 未提交读 如果数据库的隔离级别是该级别,脏读、不可重复读、虚读都有可能发生
2.read committed – 提交读 如果数据库的隔离级别是该级别,脏读不能发生,但是不可重复读、虚读都有可能发生
3.repeatable read – 可重复读 如果数据库的隔离级别是该级别,脏读和不可重复不能发生,虚读有可能发生
4.serializable – 串行的 如果数据库的隔离级别是该级别,脏读、不可重复读和虚读都不能发生

  • 效率 read uncommitted > read committed > repeatable read > serializable
  • 安全 read uncommitted < read committed < repeatable read < serializable
  • 注意:数据库中一般都不会采用效率最低的和安全性最低的
  • MySQL默认的隔离级别:repeatable read
  • Oracle默认的隔离级别:read committed

  • 通过一些命令查询和设置数据库的隔离级别

    • select @@tx_isolation;
    • set session transaction isolation level XXX; (XXX代表隔离级别)
      演示脏读

1.脏读:一个事物读取到了另一个事物未提交的数据。
2.进行测试
* 开启两个窗口,一个A窗口(左),一个B窗口(右)。
* 在A窗口中,查询A窗口的隔离级别:select @@tx_isolation;
* 设置A窗口的隔离级别为最低级别: set session transaction isolation level read uncommitted;
* 在两个窗口中,都开启事物
* start transaction;
* 在B窗口中完成转账的工作。(冠希给美美转1千钱)
* update t_account set money = money - 1000 where name = ‘冠希’;
* update t_account set money = money + 1000 where name = ‘美美’;
* 注意:B窗口的事物未提交。
* 在A窗口中,查询的结果(美美)
* select * from t_account;
* 在B窗口中,回滚的是事物
* rollback;
避免脏读和演示不可重复读

1.避免脏读的发生,提高的隔离级别。
* set session transaction isolation level read committed;

* 在两个窗口中,都开启事物
    * start transaction;
* 在B窗口中完成转账的工作。(冠希给美美转1千钱)
    * update t_account set money = money - 1000 where name = '冠希';
    * update t_account set money = money + 1000 where name = '美美';
    * 注意:事物还是没有提交,但是你需要观察A窗口中是否能读取到B创建未提交的数据。
* 在A窗口中,查询的结果(美美)
    * select * from t_account;

* 在B窗口中把事物提交了
    * commit;

* 在A窗口中,再查询一次钱。
    * 两次查询的结果不一致,发生了不可重复读。

避免不可重复读

1.设置隔离级别:set session transaction isolation level repeatable read;
* 在两个窗口中,都开启事物
* start transaction;
* 在B窗口中完成转账的工作。(冠希给美美转1千钱)
* update t_account set money = money - 1000 where name = ‘冠希’;
* update t_account set money = money + 1000 where name = ‘美美’;
* 注意:事物还是没有提交,但是你需要观察A窗口中是否能读取到B创建未提交的数据。
* 在A窗口中,查询的结果(美美)
* select * from t_account;

* 在B窗口中把事物提交了
    * commit;

* 在A窗口中,再查询一次钱。
    * 两次查询的结果不一致,发生了不可重复读。

避免各种读

1.设置隔离级别:set session transaction isolation level serializable;
* 在两个窗口中,都开启事物
* start transaction;
* 在B窗口中添加一条数据
* insert into t_account values (null,’小苍’,10000);
* 注意:没有提交事物
* 在A窗口中查询数据库
* select * from t_account;
JDBC中设置事物的隔离级别

1.void setTransactionIsolation(int level) – 通过该方法设置数据库的隔离级别
2.包含4个值,在Connection接口中的4个常量
* static int TRANSACTION_READ_UNCOMMITTED :指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量
* static int TRANSACTION_READ_COMMITTED :指示不可以发生脏读的常量;不可重复读和虚读可以发生。
* static int TRANSACTION_REPEATABLE_READ :指示不可以发生脏读和不可重复读的常量;虚读可以发生。
* static int TRANSACTION_SERIALIZABLE :指示不可以发生脏读、不可重复读和虚读的常量。
数据库的连接池

连接池的概述

1.概述:为什么要有数据库的连接池,连接池有什么样的作用?
* 在没有连接池的情况下,需要创建链接的对象和销毁链接的对象。比较销毁时间的。
* 在程序中,指定连接池,中存放的都是连接,如果在使用的时候,从池中获取链接,然后直接使用。使用完之后,把链接归还到连接池中。

2.连接池的池参数(如果不指定值,肯定会有默认值的)
* 初始大小:10个
* 最小空闲连接数:3个
* 增量:一次创建的最小单位(5个)
* 最大空闲连接数:12个
* 最大连接数:20个
* 最大的等待时间:1000毫秒

3.连接池的时候,有4个配置的参数,必须要配置的
* driverClass – 驱动
* url – 数据库的链接
* username – 用户名
* password – 密码
自定义连接池

1.目的:连接池有一定的问题,讲解装饰者的模式。
2.条件
* 如果你想开发自定义连接池,需要实现javax.sql.DataSource接口。
* 实现Connection getConnection()方法。具体实现怎么写,需要自己来编写。
* 数据库的连接池,创建数据库的连接池的时候,需要先创建几个链接的对象,保存启动。最后指定的归还的链接方法。

3.问题
* 不能使用DataSource接口进行操作了。
* 自定义的归还链接的方法,用户需要掌握。

4.目的:
* 现在目的:就想修改close()方法,把销毁链接改成归还链接!!!!
装饰者模式(难点)

0.玩的面向接口的开发,你们知道MySQL实现类的名称叫什么么?
1.目的:增强某个类中的某个方法。
2.方式:
* 继承该类,重写该方法。
* 要继承的类的构造方法!!

* 使用装饰者的模式来完成该操作。   
    * 增强的类和被增强的类必须要实现同一个接口
    * 在增强的类中必须要获取到被增强类的引用

* 使用动态代理完成该操作。  

3.过程
* 假设MySQL数据库的Connection接口的实现类的名字叫MysqlConnection
class MysqlConnection implements Connection{
public Statement createStatement(){
// 逻辑代码已经写完了
}

    public PreparedStatement prepareStatement(String sql) {
        // 实现了....
    }
    ...
    public void close(){
        // 销毁链接
    }
}


* 是你还有你,一切拜托你。

* 自己编写一个实现类
class myConnection implements Connection{

    private Connection sqlConn;

    public myConnection(Connection sqlConn){
        this.sqlConn = sqlConn;
    }

    public void close(){
        // 这还是销毁
        // sqlconn.close();
        // 自己去定义一些内容,归还链接
    }

    public Statement createStatement(){
        // 调用MySQL实现类的方法
        return sqlConn.createStatement();
    }
    public PreparedStatement prepareStatement(String sql) {
        // 我自己实现了....
        return sqlConn.prepareStatement(sql);
    }
    ...  都能使用sqlConn.xxxx实现一遍

}

myConnection myconn = new myConnection(new MysqlConnection());
myconn.createStatement();

开源的数据库连接池

1.这些开源的数据库的连接池,Connection接口的close()方法都已经增强过了,调用该方法默认就是归还的方法了。
2.要求大家会使用就ok了。会完成一些配置。
DBCP连接池(重点)

1.是Apache组织提供开源的数据库的连接池。
2.导入2个jar包。
* commons-pool-1.5.6.jar
* commons-dbcp-1.4.jar
3.开发入门
* BasicDataSource 是DBCP的连接池对象(手动设置4大参数)
* BasicDataSourceFactory 有方法: static DataSource createDataSource(Properties properties) – 通过 properties获取到DataSource接口,获取连接对象。

* 设置4大参数
    * 驱动
    * url
    * username
    * password

4.代码:入门
@Test
public void run(){
Connection conn = null;
PreparedStatement stmt = null;
try {
// 先有DBCP的连接池
BasicDataSource dataSource = new BasicDataSource();

        // 手动设置4大参数
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///day18");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        // 获取链接(从DBCP的连接池获取)
        conn = dataSource.getConnection();
        // 编写SQL
        String sql = "update t_account set money = ? where name = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setDouble(1, 300);
        stmt.setString(2, "美美");
        stmt.executeUpdate();

    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        // 先释放资源
        MyJdbcUtil.release(stmt, conn);
    }
}

5.通过配置文件的方式(properties文件)
* 需要先编写配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day18
username=root
password=root

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

/**
 * 读取配置文件的方式使用DBCP的连接池
 */
@Test
public void run2(){
    Connection conn = null;
    PreparedStatement stmt = null;
    try {
        // 先解析dbcp.properties文件
        // 获取dbcp.properties文件
        Properties pro = new Properties();
        // 获取dbcp.properties文件的输入流
        InputStream in = DbcpDemo.class.getClassLoader().getResourceAsStream("dbcp.properties");
        pro.load(in);

        DataSource dataSource = BasicDataSourceFactory.createDataSource(pro);
        // 获取链接(从DBCP的连接池获取)
        conn = dataSource.getConnection();
        // 编写SQL
        String sql = "update t_account set money = ? where name = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setDouble(1, 1300);
        stmt.setString(2, "美美");
        stmt.executeUpdate();

    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        // 先释放资源
        MyJdbcUtil.release(stmt, conn);
    }
}

C3P0连接池(重点)

1.有需要导入jar包。
2.开发的操作
* ComboPooledDataSource 类代表C3P0连接池的对象
* 设置4个参数
* cpds.setDriverClass( “org.postgresql.Driver” );
* cpds.setJdbcUrl( “jdbc:postgresql://localhost/testdb” );
* cpds.setUser(“dbuser”);
* cpds.setPassword(“dbpassword”);

3.代码
@Test
public void run(){
Connection conn = null;
PreparedStatement stmt = null;
try {
// 获取连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置4大参数
dataSource.setDriverClass(“com.mysql.jdbc.Driver”);
dataSource.setJdbcUrl(“jdbc:mysql:///day18”);
dataSource.setUser(“root”);
dataSource.setPassword(“root”);
// 先获取链接
conn = dataSource.getConnection();
String sql = “update t_account set money = ? where name = ?”;
stmt = conn.prepareStatement(sql);
stmt.setDouble(1, 100);
stmt.setString(2, “美美”);
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
MyJdbcUtil.release(stmt, conn);
}
}

4.编写配置文件
* C3P0默认查询XML的配置文件,配置文件的名称c3p0-config.xml。
* 配置文件存放在src的目录下。
* 满足上述的条件,C3P0默认查询到配置文件,解析。把4大参数配置到XML文件中。

<c3p0-config>
    <!-- 默认的配置,C3P0默认加载默认配置 -->
    <default-config>
        <!-- 配置4大参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///day18</property>
        <property name="user">root</property>
        <property name="password">root</property>
    </default-config>

    <!-- 类加载的时候指定了配置的名称(named-config name="intergalactoApp") -->
    <named-config name="myoracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///day18</property>
        <property name="user">root</property>
        <property name="password">root</property>
    </named-config>
</c3p0-config>

* 代码:
    @Test
    public void run2(){
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // 获取连接池对象
            ComboPooledDataSource dataSource = new ComboPooledDataSource("myoraclesssssssss");
            // 有4大参数没关
            // 先获取链接
            conn = dataSource.getConnection();
            String sql = "update t_account set money = ? where name = ?";
            stmt = conn.prepareStatement(sql);
            stmt.setDouble(1, 11100);
            stmt.setString(2, "美美");
            stmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            MyJdbcUtil.release(stmt, conn);
        }
    }

工具类已经重写了

package cn.itcast.utils;

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

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
* JDBC的工具类
* @author rt
*/
public class MyJdbcUtil2 {

public static ComboPooledDataSource dataSource = new ComboPooledDataSource();

/**
 * 获取链接对象
 * @throws SQLException 
 */
public static Connection getConnection() throws SQLException{
    return dataSource.getConnection();
}

/**
 * 释放资源(两个参数和三个参数)
 */
public static void release(ResultSet rs,Statement stmt,Connection conn){
    if(rs != null){
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        rs = null;
    }
    if(stmt != null){
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        stmt = null;
    }
    if(conn != null){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        conn = null;
    }
}

/**
 * 释放资源(两个参数和三个参数)
 */
public static void release(Statement stmt,Connection conn){
    if(stmt != null){
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        stmt = null;
    }
    if(conn != null){
        try {
            // 归还链接
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        conn = null;
    }
}

}
Tomcat内置的连接池(了解)

1.把连接池的信息内置到Tomcat服务器中。
2.配置配置,来决定WEB服务器中的项目是否可以使用内置的连接池。
3.

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场