JDBC笔记

JDBC编程六步

1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库

2.获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)。

3.获取数据库操作对象(专门执行sql语句的对象

        

Statement statement = connect.createStatement();

4.执行SQL语句(DQL,DML…,增删改
        

String sql = "insert into actor values('2','a')";
int rows = statement.executeUpdate(sql);

5.处理查询结果集 (只有当第四步执行的是select语句的时候,才有本步,查

while (rs.next()){
            System.out.println(rs.getString("ename"));
        }

6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭

if (rs != null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (stmt != null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

数据库连接的五种方式(前两步)

使用第三方Driver

//方式1   属于静态加载,灵活性差,依赖性强。
    @Test
    public void connect01() throws SQLException {
        Driver driver = new Driver(); //创建driver对象
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        //将 用户名和密码放入到Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "hsp"); //密码
        Connection connect = driver.connect(url, properties);
        System.out.println(connect);
    }

 二

通过反射机制获取Driver

//方式2
    @Test
    public void connect02() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
        //使用反射加载Driver类 , 动态加载,更加的灵活,减少依赖性
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver)aClass.newInstance();
 
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        //将 用户名和密码放入到Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "hsp"); //密码
 
        Connection connect = driver.connect(url, properties);
        System.out.println("方式2=" + connect);
 
    }

 三

使用DriverManager来注册,不用写配置文件, 更加方便

//方式3 使用DriverManager 替代 driver 进行统一管理
    @Test
    public void connect03() throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException {
 
        //使用反射加载Driver
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.newInstance();
 
        //创建url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";
        String password = "hsp";
 
        DriverManager.registerDriver(driver);//注册Driver驱动
 
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("第三种方式=" + connection);
    }

直接可以不用注册驱动

//方式4: 使用Class.forName 自动完成注册驱动,简化代码
    //这种方式获取连接是使用的最多,推荐使用
    @Test
    public void connect04() throws ClassNotFoundException, SQLException {
        //使用反射加载了 Driver类
        //在加载 Driver类时,完成注册
        /*
            源码: 1. 静态代码块,在类加载时,会执行一次.
            2. DriverManager.registerDriver(new Driver());
            3. 因此注册driver的工作已经完成
            static {
                try {
                    DriverManager.registerDriver(new Driver());
                } catch (SQLException var1) {
                    throw new RuntimeException("Can't register driver!");
                }
            }
         */
        Class.forName("com.mysql.jdbc.Driver");
 
        //创建url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";
        String password = "hsp";
        Connection connection = DriverManager.getConnection(url, user, password);
 
        System.out.println("第4种方式~ " + connection);
 
    }

把写死的连接数据库的配置信息,通过properties类来进行获取配置。

 //方式5 , 在方式4的基础上改进,增加配置文件,让连接mysql更加灵活
    @Test
    public void connect05() throws IOException, ClassNotFoundException, SQLException {
 
        //通过Properties对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
         
        Class.forName(driver);//版本变高,不需要写,但是建议写上
 
        Connection connection = DriverManager.getConnection(url, user, password);
 
        System.out.println("方式5 " + connection);
 
 
    }

使用

public class JDBC01 {
    public static void main(String[] args) throws SQLException {
        //前置工作: 在项目下创建一个文件夹比如 libs
        // 将 mysql.jar 拷贝到该目录下,点击 add to project ..加入到项目中
        
        //1. 注册驱动
        Driver driver = new Driver(); //创建driver对象
 
        //2. 得到连接
        // 老师解读
        //(1) jdbc:mysql:// 规定好表示协议,通过jdbc的方式连接mysql
        //(2) localhost 主机,可以是ip地址
        //(3) 3306 表示mysql监听的端口
        //(4) hsp_db02 连接到mysql dbms 的哪个数据库
        //(5) mysql的连接本质就是前面学过的socket连接
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        //将 用户名和密码放入到Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "zouwenhao"); //密码
        Connection connect = driver.connect(url, properties);
 
        //3. 执行sql
        //String sql = "insert into actor values(null, '刘德华', '男', '1970-11-11', '110')";
        //String sql = "update actor set name='周星驰' where id = 1";
        String sql = "delete from actor where id = 1";
        //statement 用于执行静态SQL语句并返回其生成的结果的对象
        Statement statement = connect.createStatement();
        // rows返回0,表示执行失败
        int rows = statement.executeUpdate(sql); // 如果是 dml语句,返回的就是影响行数,executeUpdate其实就是执行语句
        System.out.println(rows > 0 ? "成功" : "失败"); // 受影响的行数大于0,那么执行成功,反之为0,则执行失败
 
        //4. 关闭连接资源
        statement.close();
        connect.close();
    }
}

ResultSet[结果集]

查询数据库,将结果放在一个结果集对象中

ResultSet是一个接口,JDBC42ResultSet才是实现接口的实例对象

其中rowData用于存放查询数据

查询结果的每一行用ArrayList存放在rows中

String sql = "Select * from actor";
ResultSet resultSet =statement.executeQuery(sql);

while(resultSet.next()){
    int id = resultSet.getInt("id");
    String name =resultSet.getString("name");
    String sex = resultSet.getString("sex");
    Date date =resultSet.getDate("borndate");
    String phone =resultSet.getString("phone");
    System.out.println(id + "\t" + name + "\t" + sex + "\t" + date+ "\t" + phone);
}
resultSet.close();
statement.close();
connection.close();

注:查询的列名应当和while循环中的列名一一对应

若返回的查询中有date这一列,但while循环中没有会出现报错

getString()也可以用索引来获得对应的列(index=1为第一列),不管数据类型是什么,取出来的类型都是String,列下标从1开始

可以以特定类型取出,getInt, getDouble

当查询结束后,我们需要将resultSet关闭,查询的结果会丢失。

我们后续若想继续使用该结果集就要重新查询,这很不方便。

在后续DAO的介绍中,我们会为每个表创建一个JavaBean来解决复用这个问题。

 SQL注入

用户输入的信息中含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,
导致sql语句的原意被扭曲,进而达到SQL注入。

String sql = "select name , pwd  from admin where name ='"
                + admin_name + "' and pwd = '" + admin_pwd + "'";
ResultSet resultSet = statement.executeQuery(sql);

PreparedStatement(预处理)

解决sql注入问题,只要用户提供的信息 不参与sql语句的编译过程,问题就解决了
PreparedStatement接口继承了java.sql.PreparedStatement
先对sql语句的框架进行编译,然后再给sql语句传"值"


对比

1.Statement存在sql注入问题,PreparedStatement解决了sql注入问题
2.Statement是编译依次执行一次,PreparedStatement是编译一次,可以执行n次,效率略高
3.PreparedStatement会在编译阶段做类型安全检查
综上所述:大部分时候使用PreparedStatement   有些情况用statement,如需要拼接字符串,输入desc(降序) asc(升序)

        //得到PreparedStatement
        //1 组织SqL , Sql 语句的 ? 就相当于占位符
        //String sql = "insert into admin values(?, ?)";
        //String sql = "update admin set pwd = ? where name = ?";
        String sql = "select name , pwd  from admin where name =? and pwd = ?"; // ?用于占位
        //2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);//关联
        //3 给 ? 赋值,set类型
        preparedStatement.setString(1, admin_name);
        preparedStatement.setString(2, admin_pwd);
        //4 执行 select 语句使用  executeQuery
        //  如果执行的是 dml(update, insert ,delete) executeUpdate()
        //  这里执行 executeQuery ,不要在写 sql,因为preparedStatement已经与sql关联
        ResultSet resultSet = preparedStatement.executeQuery();

 API梳理

JDBCUtils

public class JDBCUtils {
    //定义相关的属性(4个)
    //因为只需要一份,因此用static修饰

    private static String user;
    private static String password;
    private static String url;
    private static String driver;

    //在static 代码块获得配置文件信息
    static{
        Properties properties = new Properties();
        try {
        	//加载之前用过的配置文件
            properties.load(new FileInputStream("src\\mysql.properties"));
            //读取相关属性
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            driver = properties.getProperty("driver");
        } catch (IOException e) {
            //在实际开发中,可以这样处理
            //1. 将编译异常转成运行异常
            //2. 调用这可以选择捕获该异常,也可以选择默认处理(报出编译异常)
            throw new RuntimeException(e);
            //e.printStackTrace();
        }
    }
    //连接数据库,返回Connection
    public static Connection getConnection(){
        try {
            return DriverManager.getConnection(url,user,password);
        }catch (SQLException e){
            throw  new RuntimeException(e);
        }
    }

    //关闭相关资源
    /*
     * 1. ResultSet 结果集
     * 2. Statement或PreparedStatement
     * 3. Connection
     */

    public static void close(ResultSet set, Statement statement, Connection connection){
        //判断是否为NULL
        try {
            if(set !=null){
                set.close();
            }
            if(statement!=null){
                statement.close();
            }
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            //转为运行时异常抛出
            throw new RuntimeException(e);
        }
    }
}

 使用

package com.lyxlearn.utils;
 
import org.junit.jupiter.api.Test;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
public class JDBCUtils_Use {
    @Test
//dml
    public void testDML() throws SQLException {
        Connection connection = JDBCUtils.getConnection();
 
        String sql = "update actor1 set name = ? where number = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"小红");
        preparedStatement.setString(2,"2020200");
        int rows = preparedStatement.executeUpdate();
        System.out.println(rows > 0 ? "成功":"失败");
        JDBCUtils.close(null,preparedStatement,connection);
    }
    @Test
//查询
    public void testQuery() throws SQLException {
        Connection connection = JDBCUtils.getConnection();
        String sql = "select name , sex from actor1 where number = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"2020200");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            String name = resultSet.getString(1);
            String sex = resultSet.getString(2);
            System.out.println(name + "\t" + sex);
        }
        JDBCUtils.close(resultSet,preparedStatement,connection);
 
 
    }
}

事务

JDBC中的事务自动提交的,什么是自动提交?
只要执行任意一条 DML语句,则自动提交一次。这是JDBC默认的事务行为。
但是在实际的业务中,通常都是N条DML语句共同联合才能完成,必须
保证这些DML语句在同一个事务中同时成功或者同时失败
解决方案:三行重要的代码
conn.setAutoCommit(false);//手动提交事务
conn.commit();//提交事务
conn.rooback();当发生异常时或者程序错误时,进行回滚。

package com.hspedu.jdbc.transaction_;
 
import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
 
/**
 * 演示jdbc 中如何使用事务
 */
public class Transaction_ {
 
    //没有使用事务.
    @Test
    public void noTransaction() {
 
        //操作转账的业务
        //1. 得到连接
        Connection connection = null;
        //2. 组织一个sql
        String sql = "update account set balance = balance - 100 where id = 1";
        String sql2 = "update account set balance = balance + 100 where id = 2";
        PreparedStatement preparedStatement = null;
        //3. 创建PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection(); // 在默认情况下,connection是默认自动提交
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第1条sql
 
            int i = 1 / 0; //抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第3条sql
 
 
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
 
    //事务来解决
    @Test
    public void useTransaction() {
 
        //操作转账的业务
        //1. 得到连接
        Connection connection = null;
        //2. 组织一个sql
        String sql = "update account set balance = balance - 100 where id = 1";
        String sql2 = "update account set balance = balance + 100 where id = 2";
        PreparedStatement preparedStatement = null;
        //3. 创建PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection(); // 在默认情况下,connection是默认自动提交
            //将 connection 设置为不自动提交
            connection.setAutoCommit(false); //开启了事务
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第1条sql
 
            int i = 1 / 0; //抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第3条sql
 
            //这里提交事务
            connection.commit();
 
        } catch (Exception e) {
            //这里我们可以进行回滚,即撤销执行的SQL
            //默认回滚到事务开始的状态.
            System.out.println("执行发生了异常,撤销执行的sql");
            try {
                connection.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}

悲观锁和乐观锁的概念

事务1–>读取到版本号1.1
事务2—>读取到版本号1.1

其中事务1先修改了,修改之后看了版本号是1.1 ,于是提交修改的数据,将版本号修改为1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2 ,和它最初读的版本号不一致。回滚。

悲观锁:事务必须排队执行。数据锁住了,不允许并发。 (行级锁: select后面添加for update )
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

行级锁 – 悲观锁

select ename, job, sal from emp where job= ' MANAGER ' for update; 

批处理 

批处理之前

批处理之后

 连接池

c3p0

user=root
password=123456
url=jdbc:mysql://localhost:3306/db01
driver=com.mysql.jdbc.Driver
public static void test() throws IOException, PropertyVetoException, SQLException {
        //1、创建数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        //2、获取配置信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\properties"));
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        //3、给数据源comboPooledDataSource 设置相关参数
        comboPooledDataSource.setDriverClass(driver);
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);
        //4、设置初始化连接数
        comboPooledDataSource.setInitialPoolSize(100);
        //最大连接数
        comboPooledDataSource.setMaxPoolSize(500);
        long start = System.currentTimeMillis();
        for(int i = 0; i < 5000; i ++) {
            Connection connection = comboPooledDataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

c3p0-config.xml配置文件

<c3p0-config>
    <!--使用默认的配置读取数据库连接池对象 -->
    <named-config name="abc">
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db01</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <!-- 连接池参数 -->
        <!--每次增长的连接数量-->
        <property name="acquireIncrement">5</property>
        <!--初始化申请的连接数量-->
        <property name="initialPoolSize">10</property>
        <!--最小的连接数量-->
        <property name="minPoolSize">5</property>
        <!--最大的连接数量-->
        <property name="maxPoolSize">50</property>
        <!--可连接的最多的命令对象数-->
        <property name="maxStatements">5</property>
        <!--每个连接对象可连接的最多的命令对象数-->
        <property name="maxStatementsPerConnection">2</property>
    </named-config>
</c3p0-config>
public static void test() throws IOException, PropertyVetoException, SQLException {
        //1、创建数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("abc");
        long start = System.currentTimeMillis();
        for(int i = 0; i < 5000; i ++) {
            Connection connection = comboPooledDataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

druid

# druid.properties文件的配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db01
username=root
password=123456
# 初始化连接数量
initialSize=10
# 最小连接数
minIdle=5
# 最大连接数
maxActive=50
# 最大超时时间
maxWait=3000
public void test() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));
        //创建一个指定参数的数据库连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        long start = System.currentTimeMillis();
        for(int i = 0; i < 5000; i ++) {
            Connection connection = dataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

JDBCUtilsByDruid

public class JDBCUtilsByDruid {
    private static DataSource ds;
    static {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src//druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    
    //把连接放回到连接池,并不是关闭
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if(resultSet != null) {
                resultSet.close();
            }
            if(statement != null) {
                statement.close();
            }
            if(connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Apache-DBUtils

 

 public void test() throws Exception {
        Connection connection = JDBCUtilsByDruid.getConnection();
        QueryRunner queryRunner = new QueryRunner();
        String sql = "select * from actor where id > ?";
        //new BeanListHandler<>(Actor.class)  将resultset -> Actor对象 -> 封装到ArrayList
        //底层使用反射机制获取Actor类的属性,然后封装
        //底层得到的resultset会在query关闭,PreparedStatment也会关闭
        List<Actor> query =
                queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
        for (Actor actor : query) {
            System.out.println(actor.getId() + actor.getName());
        }

        //返回单行记录
        String sql1 = "select * from actor where id = ?";
        Actor query1 = 
                queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 1);

        //返回单行单列,返回的就是Object
        String sql2 = "select name from actor where id = ?";
        Object query2 = queryRunner.query(connection, sql, new ScalarHandler<>(), 1);

        JDBCUtilsByDruid.close(null,null,connection);
    }

 DML

public void test() throws Exception {
        Connection connection = JDBCUtilsByDruid.getConnection();
        QueryRunner queryRunner = new QueryRunner();

        String sql = "update actor set name = ? where id = ?";
        //执行dml操作是 queryRunner.update()
        //返回值是受影响的行数
        int row = queryRunner.update(connection, sql, "aaa", 1);
        System.out.println(row > 0 ? "执行成功" : "没有影响");
        
        JDBCUtilsByDruid.close(null,null,connection);
    }

BasicDAO

public class BasicDAO<T> {
    private QueryRunner qr = new QueryRunner();
    public int update(String sql, Object...parameters) throws SQLException {
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            int update = qr.update(connection, sql, parameters);
            return update;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }

    public List<T> queryMulti(String sql, Class<T> clazz, Object...parameters) throws SQLException {
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            List<T> query = qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);
            return query;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }

    }

    public T querySingle(String sql, Class<T> clazz, Object...parameters) {
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }

    public Object queryScalar(String sql, Object...parameters) {
        Connection connection = null;
        try {
            connection = JDBCUtilsByDruid.getConnection();
            return qr.query(connection, sql, new ScalarHandler(), parameters);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCUtilsByDruid.close(null,null,connection);
        }
    }
}

ActorDAO、GoodsDAO可以继承BasicDAO,在Service类中使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值