JDBC相关介绍

JDBC是Java提供操作数据库的规范,也就是提供一系列接口,具体的实现类由各个数据库厂商提供,所以我们要导入相关Jar包,这些jar包提供对应的实现类,如果是mysql数据库,那么我们就用导入mysql的连接驱动的jar包,

一、JDBC的概念

JDBC指java database connectivity(java数据库连接),也就Java提供的执行SQL操作数据库的API,可为多种关系型数据库提供统一的操作,它是由一组java 语言编写的类或者接口组成(本质:就是Java提供一套操作数据库的规范,所有关系型数据库都实现了这一规范,这样程序员就可以通过统一的API操作不同的数据库)

二、JDBC操作

  1. 导入Jar包,因为JDBC只是Java提供的一套规范,具体实现需要不同数据库厂商去实现

注意:jdbc是java提供的操作数据库的规范,不同的数据库厂商需要实现规范,也就是提供实现类,而我们操作什么数据库就要导入对应的实现类jar包

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>8.0.25</version>
  5. </dependency>
  1. 注册驱动
  2. 获取连接
  3. 获取执行者对象(用来执行SQL)
  4. 执行SQL
  5. 处理结果
  6. 关闭连接 ```java package com.study;

import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class TestJDBC { public static void main(String[] args) throws Exception{ //第一步:导入Jar //第二步:注册驱动 Class.forName(“com.mysql.jdbc.Driver”); //第三步:获取连接 Connection connection = DriverManager.getConnection(“jdbc:mysql://182.92.71.236:3306/study”, “root”, “zhubowenmysql”); //第四步:获取执行者对象 Statement statement = connection.createStatement(); //第五步:执行SQL ResultSet resultSet = statement.executeQuery(“select * from jdbctest”); //遍历结果 while (resultSet.next()){ int id = resultSet.getInt(“id”); String username = resultSet.getString(“username”); String password = resultSet.getString(“password”); System.out.println(“id:”+id+”,username:”+username+”,password:”+password); } //释放资源 statement.close(); connection.close(); } }

  1. <a name="vrnVT"></a>
  2. ### 三、详细解释
  3. 1.注册驱动,我们注册驱动使用的是DriverManager这个对象,但是在com.mysql.jdbc包下有一个Driver类,这个Driver类的静态代码块就完成了注册,为了防止重复注册,我们都是调用这个类来完成注册,只要类加载那么静态代码块就会被加载,所以我们注册驱动使用的是加载类,注意MySQL5版本,jar包中有一个配置文件写了注册驱动,我们可以省略,但是不建议
  4. ```java
  5. public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  6. public Driver() throws SQLException {
  7. }
  8. static {
  9. try {
  10. DriverManager.registerDriver(new Driver());
  11. } catch (SQLException var1) {
  12. throw new RuntimeException("Can't register driver!");
  13. }
  14. }
  15. }

2.获取数据库连接
通过DriverManager提供的静态方法getConnection方法

        Connection connection = DriverManager.getConnection("jdbc:mysql://182.92.71.236:3306/study", "root", "zhubowenmysql");
//参数一:url   写法jdbc:mysql://IP:端口/要连接的数据库名&连接参数
//参数二:用户名
//参数三:密码

3.获取执行者对象

 //可以获取普通的执行者对象,
Statement statement = connection.createStatement();
//这个是获取预编译执行者对象
// connection.prepareStatement();
//开启事务,即是否自动提交,false表示手动提交(开启事务),默认是自动提交
connection.setAutoCommit(boolean autoCommit);
//提交事务
connection.commit();
//回滚事务
connection.rollback();
//释放连接资源
connection.close():

3.执行者对象:用来执行SQL语句(DML语句:增删改语句,DQL语句查询的语句)

//如果执行DML语句即增删改的语句,需要调用
statement.executeUpdate(String sql);//返回值是int表示修改的行数
//执行DQL语句,调用的是executeQuery,返回的是查询的结果集
ResultSet resultSet = statement.executeQuery("select * from jdbctest");
//执行者对象释放资源调用close()方法

4.结果集对象ResultSet:它是执行查询返回的结果,封装到结果集

//方法一:next()如果有结果返回true,并将索引向下移一位
//XXX getXXX()/获取结果集中的结果,获取什么类型就调用对应的get方法,例如获取int就调用getInt(String column);
while (resultSet.next()){
            int id = resultSet.getInt("id");
            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            System.out.println("id:"+id+",username:"+username+",password:"+password);
        }

四、SQL注入攻击

我们在组装SQL参数的时候,采用的是拼接的形式,导致页面如果传入参数含有SQL那么就被认为是SQL语句,也会被拼接上去,那么就会存在SQL的注入攻击,
解决方案获取预编译执行者对象,这样SQL会提前编译好,使用?作为参数的占位符,然后调用set方法来给占位符赋值,索引从1开始,后来加上的都会本人为是参数部分,即使传递了参数含有SQL也不会被识别还是认为是参数

 //SQL注入攻击演示
        @Test
        public void testSQLDI() throws Exception{
                Connection connection = GetConnectionUtil.getConnection();
                Statement statement = connection.createStatement();
                //这样写存在SQL的注入攻击
//                String username="test";
//                String password="1111";
//                ResultSet resultSet = statement.executeQuery("select * from jdbctest where username='test' and password=1111 ");
//                //使用预编译执行者对象来解决SQL注入攻击
                PreparedStatement preparedStatement = connection.prepareStatement("select * from jdbctest where username=? and password=?");
                preparedStatement.setString(1,"test");
                preparedStatement.setString(2,"1111");
                ResultSet resultSet = preparedStatement.executeQuery();
                while (resultSet.next()){
                        System.out.println("id:"+resultSet.getInt("id"));
                }
                //关闭资源
                statement.close();
                connection.close();
        }

五、JDBC事务的注意:

JDBC管理事务的对象是Connection对象
我们在进行增删改的时候一定是要提交事务,否者结果不会被修改,JDBC默认是自动提交事务

数据源介绍使用

一、自定义数据库连接池

1.数据库连接池介绍
对于程序来操作数据库,如果我们每操作一次就建立一次连接,然后使用完就释放连接这样性能低下,所以在此基础上提出了池的概念
数据库连接池,它是一个负责分配,管理,释放数据库连接的,他允许数据库重复使用一个现有的数据库连接,而不是新建立一个,这样可以提高性能
2.自定义数据库连接池
DataSource接口:javax.sql.DataSource它是数据源(java官方提供的数据库连接池规范,我们要定义数据连接池要实现这个接口提供抽象方法,即实现相关规范),这个接口提供getConnection()方法用来获取连接

package com.study.source;

import com.study.util.GetConnectionUtil;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

//步骤:第一步实现javax.sql.DataSource接口
//第二步:创建容器池:即创建一个线程安全的集合容器(这里一定要是线程安全的因为连接池程序线程共同操作的)
//第三步:在静态代码块中创建多个连接对象放入池中
//第四步:重写getConnection();来获取连接
public class SelfDataSource implements DataSource {
    private static List<Connection> pool= Collections.synchronizedList(new ArrayList<Connection>());

    static {
        for (int i = 0; i < 10; i++) {
            try {
                Connection connection = GetConnectionUtil.getConnection();
                pool.add(connection);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //重写获取连接对象,获取对象要判断池中是否还有连接
    public Connection getConnection() throws SQLException {
        if(pool.size()>0){
            //获取连接并将连接移除池子
            Connection connection = pool.remove(0);
            return connection;
        }else {
            throw  new RuntimeException("连接以用尽");
        }
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    public void setLoginTimeout(int seconds) throws SQLException {

    }

    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

这里我们就实现了通过连接池来获取连接对象,这个连接对象时DriverManager调用getConnection()获取的,他的类型是JDBC4Connection类型(我们引入的驱动包中的连接对象),通过调用这个类来执行SQL的,但是我们执行完SQL,调用这个类的Close()会关闭资源不能将资源归还给池子,所以需要优化,因为这个方法是源码里的方法,所以我们最好的方式是使用动态代理
优化归还连接方式
方式一:装饰者设计模式,我们自定义一个类实现我们要增强类的接口,这样这个类就具备了原有来相同的功能,我们可以在这个类上继续添加或者修改原有功能,对于不需要修改的功能我们还是调用原来类的对象调用其方法,相当于对原有类进行一个包装,这种设计模式称为装饰者设计模式
实现步骤:
装饰者设计模式来归还连接:对返回的连接对象进行装饰,即对需要的功能进行装饰(增强),其他功能可以调用原油对象进行保留
步骤:定义一个装饰者类用来装饰原有类即继承同一个接口
将原有类对象作为装饰者类的属性,在构造器为其赋值(不需要增强的类,需要原有对象调用)
重写要增强的方法

对于不需要增强的方法调用原有对象执行
装饰者模式实现原理:是定义一个类实现要装饰类同样的接口(这个接口要包含需要装饰的方法),重写需要装饰的方法,对于不需要重写的方法,我们需要调用原有的装饰对象去执行,所以需要原有对象作为成员变量,在构造器或其他地方为其值
缺点:需要重写很多不需要的方法

方式二:适配器模式
由于我们使用装饰者设计模式需要重写很多方法,再此基础上我们可以进行优化,提供一个中间类来实现接口,这样我们只需要中间类重写方法(需要增强的方法不进行重写,所以适配器类是一个抽象类),我们只需要写一个类来继承中间类即可

方式三:动态代理(在不该源码的情况,对需要代理的源码中的方法进行增强)
这里最值得注意的是我们在重写方法,执行代理规则的时候,一定要注意,如果需要执行原有方法那么,一定要将原有方法的对象进行返回保持调用类型相同

 @Override
    public Connection getConnection() throws SQLException {
        if(pool.size()>0){
            Connection con = pool.remove(0);
            //使用动态代理的方式
            //动态代理的时候指定增强规则时,这个方法的返回值一定要注意,
            //如果我们是直接执行原有方法,不进行增强修改,那么return是method.invoke()这个方法的返回值,
            //因为这样得到的对象类型才对,你要知道动态代理对象类型一定要对
            //如果是我们需要增强的方法,那么返回值更具我们实际调用来决定
            Connection connection= (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if(method.getName().equals("close")){
                        System.out.println("增强方法");
                        pool.add(con);
                        return null;
                    }else {
                        return method.invoke(con, args);
                    }
                }
            });
            return connection;
        }else {
            throw new RuntimeException("连接用尽");
        }
    }

二、开源的数据库连接池

2.1.C3P0数据源
使用步骤

  1. 导入坐标
  2. 配置配置文件
  3. 创建连接池对象
  4. 获取连接使用

注意:c3p0数据源的配置文件,可以自动加载,但是前提条件是必须放在src目录下,且文件名叫c3p9-config.xml或者叫c3p0-config.properites
c3p0使用实例:下面是硬编码的形式

package com.study.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
public class C3p0DataSourceNum {
    public static void main(String[] args) throws Exception{
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
        comboPooledDataSource.setJdbcUrl("jdbc:mysql://182.92.71.236:3306/study?characterEncoding=utf-8");
        comboPooledDataSource.setUser("root");
        comboPooledDataSource.setPassword("zhubowenmysql");
        //设置初始化连接池数量
        comboPooledDataSource.setInitialPoolSize(10);
        //设置最大连接池数量:表示初始化连接用完,它会继续申请连接,一直申请到最大连接数量
        comboPooledDataSource.setMaxPoolSize(12);
        //设置连接超时时长:如果连接用尽,会等待3秒,3秒没有获取连接会报错
        comboPooledDataSource.setCheckoutTimeout(3000);
        for (int i = 0; i < 13; i++) {
            Connection connection = comboPooledDataSource.getConnection();
            System.out.println(i+":"+connection);
        }
    }
}

2.2德鲁伊连接池技术(Druid)阿里巴巴提供连接池技术
使用步骤:

  1. 导入坐标
  2. 编写配置文件
  3. 通过properties集合加载配置文件(也可以采用其他构造器,传入map集合进行硬编码的形式读取)
  4. 获取数据库连接进行使用

注意:Druid连接池不会自动加载配置文件,需要我们手动加载

#连接参数
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://182.92.71.236:3306/study
username=root
password=zhubowenmysql
#初始连接数量
initialSize=5
#最大连接数量
maxActive=10
#超时时长
maxWait=3000
package com.study.druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Properties;

public class DruidDataSource {
   @Test
    public void testProperties() throws Exception{
       //通过读取配置文件的格式获取连接
       InputStream inputStream = new FileInputStream("E:\\selfstudy\\studyjdbc\\src\\main\\resources\\druid.properties");
       Properties properties = new Properties();
       properties.load(inputStream);
       System.out.println(properties);
       //创建Druid连接池工厂对象
       DruidDataSourceFactory druidDataSourceFactory = new DruidDataSourceFactory();
       //获取连接池对象
       DataSource dataSource = druidDataSourceFactory.createDataSource(properties);
       //获取连接
       Connection connection = dataSource.getConnection();
       Statement statement = connection.createStatement();

       ResultSet resultSet = statement.executeQuery("select * from jdbctest");

       while (resultSet.next()){
           System.out.println(resultSet.getInt("id"));
       }
      System.out.println(connection);
       connection.close();
   }

   @Test
   public void testMap() throws Exception{
      HashMap<String, String> map = new HashMap<>();
      map.put("driverClassName","com.mysql.jdbc.Driver");
      map.put("url","jdbc:mysql://182.92.71.236:3306/study");
      map.put("username","root");
      map.put("password","zhubowenmysql");
      map.put("initialSize","5");
      map.put("maxActive","10");
      map.put("maxWait","3000");

      DruidDataSourceFactory druidDataSourceFactory = new DruidDataSourceFactory();
      DataSource dataSource = druidDataSourceFactory.createDataSource(map);
      Connection connection = dataSource.getConnection();
      Statement statement = connection.createStatement();
      ResultSet resultSet = statement.executeQuery("select * from jdbctest");

      while (resultSet.next()){
         System.out.println(resultSet.getInt("id"));
      }
      System.out.println(connection);
      connection.close();
   }
}

**