2021-3-28 JDBC入门包含数据库连接池

里面的数据库连接池所需要的jar包可以在这里面下载:

https://blog.csdn.net/m0_49647974/article/details/115287069

一.概念:Java DataBase Connectivity

Java 数据库连接,Java语言操作数据库

JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

二.具体流程:

(1)导入驱动jar包 如:mysql-connector-java-5.1.37-bin.jar

  1. 复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
  2. 右键–>Add As Library

(2)注册驱动

(3)获取数据库连接对象connection4.

(4)定义sql

(5)获取执行sql语句的对象statement6.

(6)执行sq1,接受返回结

(7)处理结果

(8)释放资源

三. 代码实现

/**
 * @Auther: Parsifal
 * @Date: 2021/03/22/20:04
 * @Description:
 */
public class JDBCTest {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        try {
            //1、导入驱动jar包
            //2、注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //3、获取数据库连接对象
            String userName = "root";
            String password = "root";
            //报错一:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to util
            // 解决方法与原因:当mysql和connector版本八以上时,系统时区错误,需要在配置连接数据库的url加上:?serverTimezone=GMT%2B8
            // 报错二: javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify
            //解决方法:需要在配置连接数据库的url加上: &useSSL=false
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false", userName, password);
            //4、定义sql语句
            String sql = "SELECT * FROM student";
            //5、获取执行sql的对象、Statement
            statement = connection.createStatement();
            boolean execute = statement.execute(sql);
            //6、处理结果
            System.out.println(execute);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            //7、关闭资源
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

但是我们一般都会把这些代码封装成一个Util,把数据库信息写在配置文件里面,如下:

/**
 * @Auther: Parsifal
 * @Date: 2021/03/24/21:24
 * @Description:
 */
public class JDBCUtil {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;


    //静态模块会在类加载时执行
    static {
        FileInputStream fileInputStream = null;
        try {
            //properties处理属性文件
            //获取src路径下的文件的方式一(stream)
            Properties properties = new Properties();
            fileInputStream = new FileInputStream("jdbc.properties");
            //加载流对应的文件
            properties.load(fileInputStream);

            //获取src路径下的文件的方式二(path)
/*            ClassLoader classLoader = JDBCUtil.class.getClassLoader();
            URL resource = classLoader.getResource("jdbc.properties");
            String path = resource.getPath();  //获取绝对路径
            System.out.println(path);
            properties.load(new FileReader(path));*/


            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            driver = properties.getProperty("driver");
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static Connection getConnection(){
        try {
            return DriverManager.getConnection(url,user,password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;

    }
    //关闭资源
    public static void close(Statement statement, Connection connection, ResultSet resultSet){
        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection!=null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            if (resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
}
    //关闭资源 重载方法
    public static void close(Statement statement, Connection connection){
        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection!=null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

        }

    }
}

jdbc.properties配置文件

url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false
user=root
password=root
driver=com.mysql.cj.jdbc.Driver

四.详解各个对象:

1.DriverManager :驱动管理对象

(1)注册驱动:

​ static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。

​ 写代码使用: class.forName(“com.mysql.jdbc.Driver”); 把此类加载进内存。

(注意:在mysql5之后的驱动jar包可以省略注册驱动的步骤)

​ 在com.mysql.jdbc.Driver类中存在以下静态代码块,在类加载时运行创建Driver类。

static {
    try {
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}
(2)获取数据库连接:

方法:DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”, userName, password);

参数

  • url:指定连接路径 : jdbc:mysql:// ip地址(域名): 端口 /数据库名称

  • 用 " ?"在url后边可以添加属性,”&”可以连接不同的属性,以下举例一些属性:

    ​ serverTimezone=GMT mysql8版本以上要加上,作用为调整时区

    ​ allowMultiQueries=true,支持mybatis执行多条语句;

    ​ useSSL=false,是否进行ssl连接,高版本可能需要设为true;

    ​ serverTimezone=UTC,设置时区

  • uersName:用户名称,password:密码。

  • 注意如果是本机mysql服务器,并且mysql服务默认端口为3306,则url可以不写(jdbc:mysql:///test)

2.Connection :数据库连接对象

(1)获取执行中的sql对象
  • Statement createStatement() 创建Statement对象
  • PreparedStatement preparedStatement(String sql)
(2)管理事务

事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败

为什么要使用事务?

首先我们先了解没有事务的时候:如果不通过事务方式管理SQL语句。此时的SQL语句的提交(commit),是由驱动程序负责管理,在SQL执行完后,就会自动帮我们进行提交,每一条SQL都相当于一个事务。但是执行多条相关联SQL语句,而不通过事务来管理时。如果再某些步骤中中止或操作失误,就会破坏数据库的一致性。(如下,由于没有通过事务sql,我有两条sql语句修改两个内容,当第一条语句执行完后,中间遇到步骤终止和操作失误,会导致第二条sql无法执行。这是很严重的后果。)
步骤:

  • 开启事务: setAutocommit(boolean autocommit):调用该方法设置参数为false,即开启事务
  • 提交事务: commit()
  • 回滚事务: rollback()
 //JDBC事务
    @Test
    public void test2(){
        Connection connection = null;
        Statement statement = null;
        PreparedStatement preparedStatement1 =null;
        PreparedStatement preparedStatement2 =null;
        try {
            //获取连接
            connection = JDBCUtil.getConnection();

            //开启事务
            connection.setAutoCommit(false);

            //修改id=1的学生的绩点
            String sql1 = " update student set name='张三'  where id= ? ";
            //修改id=2的学生的绩点
            String sql2 = "update student set name='李四'  where id= ? ";

           preparedStatement1 = connection.prepareStatement(sql1);
           preparedStatement2 = connection.prepareStatement(sql2);
           preparedStatement1.setInt(1,1);
           preparedStatement2.setInt(1,2);
            preparedStatement1.executeUpdate();
            //手动制造异常
//            int a =1/0;
            preparedStatement2.executeUpdate();
            //提交事务
            connection.commit();
        } catch (Exception throwables) {
            try {
                //事务的回滚
                if (connection!=null) {
                    connection.rollback();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            JDBCUtil.close(preparedStatement1, connection);
            if (preparedStatement2!=null) {
                try {
                    preparedStatement2.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }

    }

没有使用事务(两条语句中间手动制造异常,第一条语句执行)

在这里插入图片描述
使用事务同时无法执行(两条语句都无法执行)
在这里插入图片描述

3.Statement :执行sql的对象

  • 执行sql语句
    • boolean execute(string sql):可以执行任意的sql
    • int executeUpdate(string sql)∶执行DML (insert、update、delete)语句、DDL(对表和库的操作如:create,alter,drop)语句
      • 该方法的返回值是影响的行数、如果返回值>0则执行成功,反之,则失败
    • Resultset executeQuery(string sql)︰执行DQL (select)语句

4.Resultset :结果集对象

  • 常用方法:

    • boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
    • getxxx(参数):获取数据xxx:代表数据类型如: int getInt() , string getstring()
      1. int : 代表列的编号,从1开始如:getstring(1)
      2. String :代表列名称。如: getDouble(“email”)
      3. 如图:getString(1)与getDouble(“id”)是同一个值
        在这里插入图片描述

5.Preparedstatement :执行sql的对象

作用: 预编译sql语句防止恶意sql注入

  1. SQL注入问题∶在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

    • 输入用户随便,输入密码:a’ or "a’ = 'a
    • sql : select * from user where username = 'fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
  2. 使用方法:

    (1)使用 ?占位符写sql(如 :select * from student where id = ?and grade=?)

    (2)获取执行sql语句的对象Preparedstatement Connection.prepareStatement(String sql)

    (3)给?赋值:格式preparedstatement.setXxx(参数1,参数2) ==>参数1:?的位置,参数2:?的值

    (4)执行sql,接受返回结果,不需要传递sql语句
    在这里插入图片描述

@Test
public void test1(){
    Connection connection = null;
    PreparedStatement preparedStatement =null;
    try {
       connection = JDBCUtil.getConnection();
        //使用 ?占位符写sql
        String sql = "select * from  student where id = ? and  grade=? ";
        //给?赋值
         preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,1);
        preparedStatement.setFloat(2,3.5f);
        ResultSet resultSet = preparedStatement.executeQuery();

        //处理结果
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String email = resultSet.getString("email");
            double grade = resultSet.getDouble("grade");
            System.out.println(id + name + email + grade); //结果:1 zzz 564894525@qq.com 3.5
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
       JDBCUtil.close(preparedStatement, connection);
    }

}

五、数据库连接池

概念::其实就足一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

好处:

  • 节约资源
  • 用户访问高效

实现:

1.标准接口:DataSourcejavax.sql包下的
  • 方法∶
    • 获取连接:getConnection()
    • 归还连接:connection.close()。如果连接对貌Connection是从连接池中获取的,那么调用connection.close()方法,则不会再关闭连接了。而是归还连接
2.数据库厂商实现:
  • C3P0:数据库连接池技术
  • Druid:数据库连接池实现技术(阿里巴巴)
3.C3P0:数据库连接池技术
  1. 导入jar两个 c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
  2. 定义配置文件:
    • 名称:c3p0.properties 或者 c3p0-config.xml (注意:C3P0只能识别这两个配置文件)
    • 路径:直接接将文件放在src目录下即可。
  3. 创建核心对象数据库连接池对象comboPooledDataSource
  4. 获取连接: getConnection
/**
 * @Auther: Parsifal
 * @Date: 2021/03/27/20:38
 * @Description:  C3P0的演示
 */
public class C3P0Test {
    public static void main(String[] args) throws SQLException {
//        1、创建数据库连接池对象 ->使用默认配置
//        DataSource ds = new ComboPooledDataSource();
        //使用指定配置
        DataSource ds = new ComboPooledDataSource("otherc3p0");
//        2、获取连接对象
        Connection conn = ds.getConnection();

        System.out.println(conn);
        conn.close();

    }
<?xml version="1.0" encoding="utf-8"?>  
<c3p0-config>
<!--  使用默认的配置读取连接池对象-->
  <default-config>
<!--    连接参数-->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
      <property name="jdbcUrl">jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&amp;useSSL=false</property>
    <property name="user">root</property>
    <property name="password">root</property>
<!--    连接池参数-->
<!--    初始化申请的连接数量-->
    <!-- 初始化五个Connection对象-->
    <property name="initialPoolSize">5</property>
<!--  最大连接数量-->
    <property name="maxPoolSize">10</property>
<!--    如果申请不到,最多会等待三秒-->
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <named-config name="otherc3p0"> 
  </named-config>
</c3p0-config>
4.Druid:数据库连接池实现技术(阿里巴巴)
  1. 导人jar包druid-1.o.9.jar定义配置i文件:
    • 可以叫任何名称,可以放在任何目录下
    • 是properties形式的
  2. 获取数据库连接池对象∶通过工厂来来获取DruidDataSourceFactory
  3. 获取连接∶getConnection

实现代码:

package DateSource;

import DateSource.Druid.DruidTest;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @Auther: Parsifal
 * @Date: 2021/03/27/22:22
 * @Description:  使用Druid数据库连接池的JDBCUtil
 */
public class JDBCUtil {
    static DataSource ds;
    static {
        //加载配置文件
          try {
              //1、导入jar包
              //2、定义配置文件
              //3、加载配置文件  不可以默认配置文件 ,必须指定文件
              Properties  properties =new Properties();
              InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");
              properties.load(is);
              //4、获取数据库连接池对象
              ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    /**
     * 关闭资源
     * @param resultSet
     * @param statement
     * @param connection
     */
    public static void close(ResultSet resultSet, Statement statement, Connection connection){
       if (resultSet!=null){
           try {
               resultSet.close();
           } catch (SQLException throwables) {
               throwables.printStackTrace();
           }
       }

        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection!=null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

    }

    /**
     * 重载
     * @param statement
     * @param connection
     */
    public static void close(Statement statement ,Connection connection){
        close(null,statement,connection);
    }

    /**
     * 获取数据库连接池
     * @return
     */
    public static DataSource getDataSource(){
        return ds;
    }
}

六.Spring JDBC

spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发步骤︰

步骤:

  1. 导入四个jar包 spring-tx-5.0.5.RELEASE.jar spring-jdbc-5.0.5.RELEASE.jar spring-core-5.0.5.RELEASE.jar spring-beans-5.0.5.RELEASE.jar commons-logging-1.2.jar

  2. 创建JdbcTemplate对象。依赖于数据源Datasource

  3. 调用JdbcTemplate的方法来完成CRUD的操作

    • update():执行DML语句。增、删、改语句

    • queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value将这条记录封装为一个map集合

      • 注意:这个方法查询的结果集长度只能是1
    • queryForList():查询结果将结果集封装为list集合

      • 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    • query():查询结果,将结果封装为javaBean对象

      • 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
    • query的参数:RowMapper

      • 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装new
      • BeanPropertyRowMapper<类型>(类型.class)
    • queryForobject :查询结果,将结果封装为对象

/**
 * @Auther: Parsifal
 * @Date: 2021/03/28/14:12
 * @Description:
 */
public class JDBCTemplateTest {

        //1、导入jar
        //2、创建JDBCTemplate对象
      static   JdbcTemplate jdbcTemplate = new JdbcTemplate(JDBCUtil.getDataSource());
   


    //查询所有的记录用List集合
    @Test
    public void test(){
        String sql = "select * from student";
        //自定义接口
        List<Student> students = jdbcTemplate.query(sql, new RowMapper<Student>() {
            @Override
            public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                float grade = resultSet.getFloat("grade");
                Student student = new Student(id,name,email,grade);
                return student;
            }
        });
        students.forEach(System.out::println);
    }
    //查询所有的记录用List集合
    @Test
    public void test1(){
        String sql = "select * from student";
        //使用封装好的接口
        List<Student> students = jdbcTemplate.query(sql,new BeanPropertyRowMapper<Student>(Student.class));
        students.forEach(System.out::println);
    }
}

记录:学习中遇到的一些bug

mysql版本号为8.0.13

1.JDBC连接时错误:closing inbound before receiving peer’s close_notify

在这里插入图片描述

解决方法与原因:配置连接数据库的url错误,在后面加上&useSSL=false即可

2.JDBC连接时错误:

The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to util

在这里插入图片描述
解决方法与原因:当mysql和connector版本八以上时,系统时区错误,需要在配置连接数据库的url加上:?serverTimezone=GMT%2B8

3.C3P0数据库连接池报错:An attempt by a client to checkout a Connection has timed out.

在这里插入图片描述

解决方法与原因:配置文件的url错误(并且注意:&需要转义为&)

<property name="jdbcUrl">jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&amp;useSSL=false</property>

顺便说一下:可以试试看其他的解决方法:

  • 重启mysql服务

  • <property name="checkoutTimeout">3000</property>  改大等待时间
    
  • <property name="maxPoolSize">10</property> 改小最大连接数量
    

4.警告: Cannot resolve com.mysq.jdbc.Connection.ping method. Will use ‘SELECT 1’ instead.

在这里插入图片描述
解决方法与原因:druid版本与mysql版本不兼容,更换druid版本1.1.21即可

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值