【JDBC】目标厦门实习!

在这里插入图片描述

JDBC学习!目标厦门实习!

全程Java Database Connectivity

Java连接数据库技术

JDBC概念理解

通俗来讲是在Java代码中,使用JDBC提供的方法,可以发送字符串类型的SQL语言到数据库管理软件(MySQL等),并且获取语句执行结果,进而实现数据库数据CURD的操作的技术

Java提供规范的接口

数据库厂商根据java的jdbc规范接口,完成具体的实现驱动代码

JDBC的六个操作步骤

  1. 注册驱动 安装依赖的jar包
  2. 建立连接 connection
  3. 创建发送SQL语句的对象 statement
  4. 让statement对象发送SQL语句到数据库,并且获取返回结果集resultset
  5. 解析结果集resultset
  6. 销毁资源,顺序为:connection、statement、resultset

查询全部用户

建表sql语句
create database shangguigujdbc;
use shangguigujdbc;
create table t_user(
    id int primary key auto_increment comment '用户主键',
    account varchar(20) not null unique comment '账号',
    password varchar(64) not null comment '密码',
    nickname varchar(20) not null comment '昵称'
);

insert into t_user(account, password, nickname)
values
    ('root','123456','经理'),
    ('admin','666666','管理员');
代码
package com.shangguigu.api.statement;

import com.mysql.cj.jdbc.Driver;

import javax.sound.midi.Soundbank;
import java.sql.*;

/**
 * @author 王修豪
 * @version 1.0
 * 使用statement查询t_user表下全部的数据
 */
public class StatementQueryPart {
    /**
     * 核心API
     * TODO:
     *  DriverManger 驱动管理
     *  Connection
     *  Statement
     *  ResultSet
     *
     * @param args
     */
    public static void main(String[] args) throws SQLException {
        //1.注册驱动
        /**
         * TODO:
         *     注册驱动
         */
        DriverManager.registerDriver(new Driver());//是一个静态方法,可以直接调用
        //2.
        /**
         * TODO:
         *      Java程序要和数据库创建连接
         *      Java程序要连接数据库,就要调用某个方法,方法也需要填入连接数据库的基本信息
         *          数据库的IP地址 127.0.0.1
         *          数据库端口号 3306
         *          账号密码
         *          连接数据库的名称
         */
        /**
         * 参数1:url
         *      jdbc:数据库厂商名://ip地址:port/数据库名
         *      jdbc:mysql://127.0.01:3306/shangguigujdbc
         * 参数2:username 数据库账号
         * 参数3:password 数据库密码
         *
         */
        //接口 = 实现类
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.01:3306/shangguigujdbc","root","2333");

        //3.创建statement
        Statement statement = connection.createStatement();
        //4.发送sql语句,并且获取返回结果
        String sql = "select * from t_user;";
        ResultSet resultSet = statement.executeQuery(sql);

        //5.进行结果集解析
        //看看有没有下一行数据,有就可以获取
        while(resultSet.next()){
            int id = resultSet.getInt("id");
            String account = resultSet.getString("account");
            String password = resultSet.getString("password");
            String nickname = resultSet.getString("nickname");
            System.out.println(id+"--"+account+"--"+password+"--"+nickname);
        }
        //6.关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

问题

出现的问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

问题寻找

在navicat下运行一下sql,发现运行不了

问题出在

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拼写错误

登录

基于statement实现模拟登陆

package com.shangguigu.api.statement;

import com.mysql.cj.jdbc.Driver;
import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;

import java.sql.*;
import java.util.Scanner;
import java.util.zip.CheckedOutputStream;

/**
 * @author 王修豪
 * @version 1.0
 *
 * TODO:
 *      1.明确jdbc的使用流程和详细讲解内部设计api的方法
 *      2.发现问题,引出preparedStatement
 *
 * TODO:
 *      输入账号密码
 *      进行数据库信息查询
 *      反馈登陆成功还是登陆失败
 *
 * TODO:
 *      键盘输入事件:收集账号和密码信息
 *      注册成功
 *      获取连接
 *      创建statement
 *      发送查询SQL语句,并获取返回结果
 *      结果判断,显示登陆成功还是失败
 *      关闭资源
 */

public class StatementUserLoginPart {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.获取用户输入信息
        // 键盘输入事件
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入账号");
        String account = scanner.nextLine();
        System.out.println("请输入密码");
        String password = scanner.nextLine();

        //2.注册驱动
        /**
         * 方案1:
         *      DriverManager.registerDriver(new Driver());
         *
         *      问题:注册两次驱动
         *          一次是DriverManager.registerDriver(new Driver());方法本身注册一次
         *          另一次是Driver内容的静态代码块会注册一次
         *      解决:只想注册一次驱动
         *          只出发静态代码块即可 Driver
         *      触发静态代码块:
         *          类加载机制:类加载的时刻,会触发静态代码块
         *          类加载的过程:1.加载:【class文件->jvm虚拟机的class对象】
         *                      2.连接【验证是否有错->准备 静态变量默认值-> 解析 这个时候触发静态代码块】
         *                      3.初始化 给静态属性赋真实值
         *      触发  类加载:
         *          1.new 关键字
         *          2.调用静态方法
         *          3.调用静态属性
         *          4.default默认实现
         *          5.反射
         *          6.子类出发父类
         *          7.程序入口main
         */
//        方案一:淘汰
//        DriverManager.registerDriver(new Driver());

//        方案二:不太优雅,这是mysql的驱动,换成Oracle麻烦 淘汰
//        new Driver();

        //使用反射的方法,字符串 提取到外部的配置文件,这样的好处是字符串信息可以提取到外部的配置文件 -> 不改变代码的情况下,完成数据库驱动的切换
        Class.forName("com.mysql.cj.jdbc.Driver");//触发类加载,触发静态代码块的调用

        //2.获取数据库连接
        /**
         * getConnection(1,2,3)方法,是一个重载方法
         * 允许开发者用不同的形式传入数据库连接的核心参数
         *
         * 核心属性:
         *      1.数据库软件所在的主机的ip地址 127.0.0.1
         *      2.数据库软件所在主机的端口号 3306
         *      3.连接具体哪个库
         *      4.连接的账号
         *      5.连接的密码
         *      6.可选的信息
         * 三个参数:
         *      String url      数据库所在的信息,丽娜姐的具体库,以及其他的可选信息
         *                      语法:jdbc:数据库管理软件名称[MySQL, oracle]://IP地址|主机名:port端口号/数据库名?key=value
         *                      具体: jdbc:mysql://127.0.0.1:3306/shangguigujdbc
         *                          如果数据库软件安装到本机,且端口号3306,可以进行一些省略
         *                          jdbc:mysql:///shangguigujdbc
         *      String user
         *      String password
         * 两个参数:
         *      String url      同上
         *      Properties info 存储账号密码类似map
         * 一个参数:
         *      String url      账号密码填到可选信息
         */
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc", "root", "2333");

        //3.发送SQL语句的statement对象
        //statement可以发送sql语句到数据库,并且获得返回结果
        Statement statement = connection.createStatement();

        //4.发送sql语句(1.编写sql 2.发送)
        String sql = "SELECT * FROM t_user WHERE account = '"+account+"' AND PASSWORD = '"+password+"';";
        //不能防止注入攻击

        /**
         * SQL分类: DDL(容易创建, 修改, 修改) DML(插入, 修改, 删除) DQL(查询) DCL(权限控制) TPL(事务控制语言)
         *
         * executeUpdate
         * 返回值: int
         *      情况一: DML 返回影响行数
         *      情况二:非DML 返回0
         *
         * executeQuery
         * 返回值: resultSet 结果封装对象
         *
         */
//        int i = statement.executeUpdate(sql);
        ResultSet resultSet = statement.executeQuery(sql);
        //5.查询结果解析
        /**
         *1.逐行获取数据
         *      resultSet内部包含一个游标,指定当前行数据
         *      默认游标指定的是第一行数据之前
         *      可以用next方法向后移动一行游标
         *      如果我们有很多行数据,我们可以使用while(next){获取每一行的数据}
         *
         *      boolean = next(); 如果为true表示更多行数据,并向下移动
         *                         如果为false,表示没有更多行
         * TODO:
         *      移动光标的方法还有很多,但是只记住next即可
         *2.获取行的列数据
         *      resultSet.get类型(填写String(名称)或者int(下标,从1开始))
         */
        if(resultSet.next()){
            //指定当前行
            int id = resultSet.getInt(1);
            String account1 = resultSet.getString("account");
            String password1 = resultSet.getString(3);
            String nikename = resultSet.getString(4);
            System.out.println("登录成功: nikename:" + nikename + " -- id:"+id+"-- account:"+account1);
        }else{
            System.out.println("登陆失败");
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

存在问题
  1. SQL语句需要字符串拼接,比较麻烦

  2. 只能拼接字符串类型,其他数据库类型无法处理

  3. 可能发生注入攻击

静态SQL语句,适用于没用动态值,

基于preparedStatement实现模拟登陆

preparedStatement是重点

package com.shangguigu.api.statement;

import java.sql.*;
import java.util.Scanner;

/**
 * @author 王修豪
 * @version 1.0
 */
public class PSUserLoginPart {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //1,收集用户信息
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入账号");
        String account = scanner.nextLine();
        System.out.println("请输入密码");
        String password = scanner.nextLine();
        //2注册驱动 获取连接
        Class.forName("com.mysql.cj.jdbc.Driver");
        //获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc", "root", "2333");

        //3.编写sql,问号? 是占位符
        String sql = "SELECT * FROM t_user WHERE account = ? and password = ? ;";

        //4.创建预编译praparedStatement 并且设置sql语句结果
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //5.单独的占位符进行赋值
        /**
         * 参数1:index占位符的位置
         * 参数2:占位符的数值
         */
        preparedStatement.setString(1,account);
        preparedStatement.setString(2,password);

        //6.发送sql语句,并获取返回结果
        ResultSet resultSet = preparedStatement.executeQuery();

        //7.遍历结果集
        while (resultSet.next()){
            System.out.println(resultSet.getString(1));
            System.out.println(resultSet.getString(2));
        }

        //8.关闭资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }


}

preparedStatement的crud操作

package com.shangguigu.api.statement;

import org.junit.Test;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.CheckedOutputStream;

/**
 * @author 王修豪
 * @version 1.0
 */
public class PSCURDPart {
    @Test
    public void testInsert() throws ClassNotFoundException, SQLException {
        /**
         * t_user表插入一条数据
         */
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //2.获取链接
        Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        //3.编写sql,动态值使用 ? 代替
        String sql = "insert into t_user(account,password,nickname) values(?,?,?)";
        //4.创建preparedStatement,传入sql
        PreparedStatement ps = conn.prepareStatement(sql);
        //5.创建占位符
        ps.setObject(1, "test");
        ps.setObject(2, "test");
        ps.setObject(3, "二狗子");

        //6.发送sql语句,并获取返回结果
        int i = ps.executeUpdate();
        //7.判断结果
        if(i>0){
            System.out.println("插入成功");
        }else{
            System.out.println("插入失败");
        }
        //8.关闭资源
        ps.close();
        conn.close();

    }

    @Test
    public void testUpdate() throws SQLException, ClassNotFoundException {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取链接
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        //3.编写sql,动态值使用 ? 代替
        String sql = "update t_user set nickname =?,account=? where id =?";
        //4.创建preparedStatement,传入sql
        PreparedStatement ps = connection.prepareStatement(sql);
        //5.创建占位符
        ps.setObject(1,"hhhh");
        ps.setObject(2,"xixi");
        ps.setObject(3,3);
        ps.executeUpdate();
        ps.close();
        connection.close();
    }

    @Test
    public void testDelete() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        String sql = "delete from t_user where id=?";
        PreparedStatement ps = connection.prepareStatement(sql);
        ps.setObject(1,3);
        ps.executeUpdate();
        ps.close();
        connection.close();

    }

    /**
     * 查询所有用户,封装到一个List<Map>list中
     * id account password nickname
     * 把一行数据封装到map中 多个map放到list中
     *
     * 难点:获取列的名称
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    @Test
    public void testSelect() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        String sql = "select id,account,password,nickname from t_user";
        PreparedStatement ps = connection.prepareStatement(sql);
        ResultSet resultSet = ps.executeQuery();

        /**
         * TODO回顾:
         *      resultSet:有行和列
         *          利用next()指向数据行
         *          获取数据列的数据
         */
        List<Map> list = new ArrayList<Map>();
        //获取列的信息对象
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        //获取列的数量
        int columnCount = resultSetMetaData.getColumnCount();
        while(resultSet.next()){
            Map map = new HashMap<String,Object>();
            //要从1开始! 因为数据库取值是从1开始的
            for(int i=1;i<=columnCount;i++){
                //需要获取列的名称 或缺列的信息从ResultSetMetaData中获取
                //getColumnLabel:先获取别名,没有别名再获取名称    getColumnName:只会获取列的名称
                String columnLabel = resultSetMetaData.getColumnLabel(i);
                Object value = resultSet.getObject(i);
                map.put(columnLabel,value);

                //获取列的类型
            }
            list.add(map);
        }
        for()


        resultSet.close();
        ps.close();
        connection.close();
    }

}

PreparedStatement使用方法总结

使用步骤总结
  1. 注册驱动

    第一种:DriverManager.registerDriver(new Driver());//是一个静态方法,可以直接调用,但是会注册两次
    第二种:Class.forName("com.mysql.cj.jdbc.Driver");//反射触发
    
  2. 获取链接

    Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
    有三种
    
  3. 编写sql语句

  4. 创建PreparedStatement对象,并且传入sql

  5. 设置占位符

  6. 发送SQL语句,获取运行结果

    两种
    int rows = executeUpdate();//非DQL
    ResultSet = executeQuery();//DQL
    
  7. 结果解析

    移动光标 next()
    获取列数据
    //获取列的数量
    int columnCount = resultSetMetaData.getColumnCount();
    //getColumnLabel:先获取别名,没有别名再获取名称    getColumnName:只会获取列的名称
    String columnLabel = resultSetMetaData.getColumnLabel(i);
    Object value = resultSet.getObject(i);
    
  8. 关闭资源

全新JDBC扩展提升

自增长主键回显显示

获取数据库自增长的主键

  1. 创建statement的时候告诉他要返回自增主键

    PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    
  2. 取值,获取结果集

    ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
    
/**
     * TODO:
     *      t_user插入一条数据 并且获取数据库自增长的主键
     *
     */
    @Test
    public void returnPrimaryKey() throws ClassNotFoundException, SQLException {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接
        Connection connection  = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        //3.编写SQL语句
        String sql = "INSERT INTO t_user (account,password,nickname) values(?,?,?);";
        //4,创建statement
        /**
         * 第一步:
         * 在创建statement的时候,告诉statement要传回来
         */
        PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        //5.设置占位符
        preparedStatement.setObject(1,"test1");
        preparedStatement.setObject(2,"123456");
        preparedStatement.setObject(3,"我去");
        //6,发送sql,获取结果
        int i  = preparedStatement.executeUpdate();

        //7.结果解析
        if(i>0){
            System.out.println("插入成功");
            /**
             * 第二步:
             * 取值,获取装GeneratedKeys的结果集
             */
            ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
            generatedKeys.next();
            int id = generatedKeys.getInt(1);
            System.out.println("主键是"+id);
        }else {
            System.out.println("插入失败");
        }
        //8.关闭资源
        preparedStatement.close();
        connection.close();
    }

批量插入数据优化

批量操作的原理是在后面追加,注意不要写分号,之后通过addBatch设置占位符 最后统一操作

  /**
     * TODO:
     *      使用批量插入的方法插入10000条数据
     *      1.连接路径添加 允许批量插入
     *      2.sql不能写分号
     *		3,批量添加 addBatch();
     *		4.统一执行 executeBatch();
     */
    @Test
    public void testBatchInsert10000() throws ClassNotFoundException, SQLException {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接,需要追加参数rewriteBatchedStatements = true
        Connection connection  = DriverManager.getConnection(
                "jdbc:mysql://127.0.0.1:3306/shangguigujdbc?rewriteBatchedStatements = true","root","2333");
        //3.编写SQL语句,不能写分号
        String sql = "INSERT INTO t_user (account,password,nickname) values(?,?,?)";
        //4,创建statement
        /**
         * 第一步:
         * 在创建statement的时候,告诉statement要传回来
         */
        PreparedStatement preparedStatement = connection.prepareStatement(sql);


        long start = System.currentTimeMillis();
        for(int j=2;j<=10001;j++){
            //5.设置占位符
            preparedStatement.setObject(1,"test"+Integer.toString(j));
            preparedStatement.setObject(2,"123456");
            preparedStatement.setObject(3,"我去");
            //6.不执行sql, 直接追加到values的后面
            preparedStatement.addBatch();
        }
        //7.最终统一执行 批量操作
        preparedStatement.executeBatch();
        long end = System.currentTimeMillis();
        System.out.println("需要的时间"+(end-start));
        //需要的时间206ms

        //8.关闭资源
        preparedStatement.close();
        connection.close();
    }

JDBC中数据库事务的实现

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

转账的动作分开写

Service存储业务,Dao存储数据访问对象

测试类调用service,存储表的业务方法,接着调用表的操作方法,存储数据库操作

BankService.java

connection.setAutoCommit(false); 设置事务

public class BankService {
    @Test
    public void start() throws SQLException, ClassNotFoundException {
        transfer("ergouzi","lvdandan",500);
    }

    /**
     * TODO
     *      事务添加在事务方法中
     *      利用try catch代码块,开启事务  提交事务  事务回滚
     *      将connection传入dao层 
     *      dao层只负责数据库操作
     * @param addAccount
     * @param subAccount
     * @param money
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    public void transfer(String addAccount,String subAccount,int money) throws SQLException, ClassNotFoundException {
        BankDao bankDao = new BankDao();

        //一个事务的最基本要求,必须是同一个连接对象 connection

        //一个转账方法 属于一个事务 (加钱,减钱)
        //所以要在这里获取连接
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.建立连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        try{
            //开启事务
            //关闭事务自动提交
            connection.setAutoCommit(false);

            //执行动作
            bankDao.add(addAccount,money,connection);
            System.out.println("----------");
            bankDao.sub(subAccount,money,connection);

            //事务提交
            connection.commit();
        }catch (Exception e){
            //事务回滚
            connection.rollback();
            throw e;
        }finally {
            connection.close();
        }

    }
}

BankDao.java

package com.shangguigu.api.transaction;

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

/**
 * @author 王修豪
 * @version 1.0
 */
public class BankDao {
    /**
     * 加钱的数据库操作方法
     * @param account
     * @param money
     */
    public void add(String account,int money,Connection connection) throws ClassNotFoundException, SQLException {
        //1.注册驱动
//        Class.forName("com.mysql.cj.jdbc.Driver");
//        //2.建立连接
//        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
        //3.编写sql语句
        String sql = "update t_bank set money = money + ? where account=?";
        //4.创建statement
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //5.设置占位符
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        //6.发送sql语句
        int i = preparedStatement.executeUpdate();
        //7.关闭资源
        preparedStatement.close();

        System.out.println("加钱成功");
    }


    /**
     * 减钱的数据库操作方法
     * @param account
     * @param money
     */
    public void sub(String account,int money,Connection connection) throws SQLException, ClassNotFoundException {
//1.注册驱动
//        Class.forName("com.mysql.cj.jdbc.Driver");
//        //2.建立连接
//        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shangguigujdbc","root","2333");
//        //3.编写sql语句
        String sql = "update t_bank set money = money - ? where account=?";
        //4.创建statement
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //5.设置占位符
        preparedStatement.setObject(1,money);
        preparedStatement.setObject(2,account);
        //6.发送sql语句
        int i = preparedStatement.executeUpdate();
        //7.关闭资源
        preparedStatement.close();

        System.out.println("减钱成功");
    }
}

连接池

连接池工作原理

实践开发中,连接创建和销毁的时间大于使用时间,这样不合理

连接池可以容纳一定数量的连接所想,只需要获取和回收连接,不需要创建和销毁连接,节约时间

国货之光Druid德鲁伊连接池

硬编码方法

​ 直接使用代码设置连接池连接参数方法

/**
     * 硬编码
     * 直接使用代码设置连接池连接参数方式
     * 步骤:
     *  1.创建连接池对象
     *  2.设置连接池参数【必须和非必须】
     *  3.获取连接 所有连接池都一样
     *  4.回收连接 所有连接池都一样
     */
    public void testHard() throws SQLException {
        //创建连接池对象
        DruidDataSource dataSource = new DruidDataSource();

        //设置参数
        //必须设置的参数
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/shangguigujdbc");
        dataSource.setUsername("root");
        dataSource.setPassword("2333");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        //非必须设置的参数
        dataSource.setInitialSize(5);//初始化连接数量
        dataSource.setMaxActive(10);//最大数量

        //获取连接
        Connection connection = dataSource.getConnection();

        //数据库curd

        //回收链接
        connection.close();//连接池的close是回收
    }

软编码方法

​ 将信息写在外部配置文件中

druid.properties

driverClassName = com.mysql.cj.jdbc.Driver
username = root
password = 2333
url = jdbc:mysql://127.0.0.1:3306/shangguigujdbc
 /**
     * 通过读取外部配置文件的方法,实例化druid连接池对象
     */
    public void testSoft() throws Exception {
        //1.读取外部配置文件
        Properties properties = new Properties();

        //src下的文件,可以使用类加载器提供的方法
        InputStream ips = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties");
        properties.load(ips);
        //2.使用连接池的工具类的工程模式,创建连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();

        //数据库crud

        //回收连接
        connection.close();
    }

获取连接工具类

线程本地变量ThreadLocal

可以为同一个线程存储共享变量,不需要传connection, 更加优雅

三个方法:

​ 1.get: 获取ThreadLocal中当前线程中共享变量的值

​ 2.set: 设置ThreadLocal中当前线程中共享变量的值

​ 3.remove: 移除ThreadLocal中当前线程中共享变量的值

/**
 * @Author 赵伟风
 * Description:
 *
 * v1.0版本工具类
 *   内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法!
 *
 * 小建议:
 *   工具类的方法,推荐写成静态,外部调用会更加方便!
 *
 * 实现:
 *   属性 连接池对象 [实例化一次]
 *       单例模式
 *       static{
 *           全局调用一次
 *       }
 *   方法
 *       对外提供连接的方法
 *       回收外部传入连接方法
 *
 *
 * TODO:
 *    利用线程本地变量,存储连接信息! 确保一个线程的多个方法可以获取同一个connection!
 *    优势: 事务操作的时候 service 和 dao 属于同一个线程,不同再传递参数了!
 *    大家都可以调用getConnection自动获取的是相同的连接池!
 *
 */
public class JdbcUtilsV2 {


    private static DataSource dataSource = null; //连接池对象

    private static ThreadLocal<Connection> tl = new ThreadLocal<>();


    static{
        //初始化连接池对象
        Properties properties = new Properties();
        InputStream ips = JdbcUtilsV2.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(ips);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对外提供连接的方法
     * @return
     */
    public static Connection getConnection() throws SQLException {

        //线程本地变量中是否存在
        Connection connection = tl.get();

        //第一次没有
        if (connection == null) {
            //线程本地变量没有,连接池获取
            connection = dataSource.getConnection();
            tl.set(connection);
        }

        return  connection;
    }

    public static void freeConnection() throws SQLException {

        Connection connection = tl.get();
        if (connection != null) {
            tl.remove(); //清空线程本地变量数据
            connection.setAutoCommit(true); //事务状态回顾 false
            connection.close(); //回收到连接池即可
        }

    }

}

BaseDao

DAO接口及其实现类对所有表的CRUD代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类抽取一个公共的父类,称之为BaseDao

package com.atguigu.cms.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;

public abstract class BaseDao {
    /*
    通用的增、删、改的方法
    String sql:sql
    Object... args:给sql中的?设置的值列表,可以是0~n
     */
    protected int update(String sql,Object... args) throws SQLException {
//        创建PreparedStatement对象,对sql预编译
        Connection connection = JDBCTools.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        //设置?的值
        if(args != null && args.length>0){
            for(int i=0; i<args.length; i++) {
                ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始
            }
        }

        //执行sql
        int len = ps.executeUpdate();
        ps.close();
        //这里检查下是否开启事务,开启不关闭连接,业务方法关闭!
        //没有开启事务的话,直接回收关闭即可!
        if (connection.getAutoCommit()) {
            //回收
            JDBCTools.free();
        }
        return len;
    }

    /*
    通用的查询多个Javabean对象的方法,例如:多个员工对象,多个部门对象等
    这里的clazz接收的是T类型的Class对象,
    如果查询员工信息,clazz代表Employee.class,
    如果查询部门信息,clazz代表Department.class,
     */
    protected <T> ArrayList<T> query(Class<T> clazz,String sql, Object... args) throws Exception {
        //        创建PreparedStatement对象,对sql预编译
        Connection connection = JDBCTools.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql);
        //设置?的值
        if(args != null && args.length>0){
            for(int i=0; i<args.length; i++) {
                ps.setObject(i+1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始
            }
        }

        ArrayList<T> list = new ArrayList<>();
        ResultSet res = ps.executeQuery();

        /*
        获取结果集的元数据对象。
        元数据对象中有该结果集一共有几列、列名称是什么等信息
         */
         ResultSetMetaData metaData = res.getMetaData();
        int columnCount = metaData.getColumnCount();//获取结果集列数

        //遍历结果集ResultSet,把查询结果中的一条一条记录,变成一个一个T 对象,放到list中。
        while(res.next()){
            //循环一次代表有一行,代表有一个T对象
            T t = clazz.newInstance();//要求这个类型必须有公共的无参构造

            //把这条记录的每一个单元格的值取出来,设置到t对象对应的属性中。
            for(int i=1; i<=columnCount; i++){
                //for循环一次,代表取某一行的1个单元格的值
                Object value = res.getObject(i);

                //这个值应该是t对象的某个属性值
                //获取该属性对应的Field对象
//                String columnName = metaData.getColumnName(i);//获取第i列的字段名
                String columnName = metaData.getColumnLabel(i);//获取第i列的字段名或字段的别名
                Field field = clazz.getDeclaredField(columnName);
                field.setAccessible(true);//这么做可以操作private的属性

                field.set(t, value);
            }

            list.add(t);
        }

        res.close();
        ps.close();
        //这里检查下是否开启事务,开启不关闭连接,业务方法关闭!
        //没有开启事务的话,直接回收关闭即可!
        if (connection.getAutoCommit()) {
            //回收
            JDBCTools.free();
        }
        return list;
    }

    protected <T> T queryBean(Class<T> clazz,String sql, Object... args) throws Exception {
        ArrayList<T> list = query(clazz, sql, args);
        if(list == null || list.size() == 0){
            return null;
        }
        return list.get(0);
    }
}
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值