JDBC使用详解

第一章_JDBC的基本使用

基本步骤

1加载(注册)驱动获取连接(Connection)

2使用xxxStatement执行SQL语句 executeUpdate/executeQuery

3查询继续使用ResultSet对象ResultSetMetaData对象拿到数据(增删改不需要)

4关闭xxxStatement和Connection

1.1获取连接(Connection接口)

1.1.1概述

数据库连接被用于向数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果,其实一个数据库连接(Connection)就是一个Socket对象

实现连接必不可少的

  • 数据库的连接信息 用户名 密码 url(包括ip地址端口号连接哪个数据库协议等)
  • 要有数据库的驱动(Driver)去连接数据库

1.1.2原生(底层)实现连接

@Test
public void 总结1() throws Throwable{
    //1、获取MySQL的驱动对象 
    com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();
    
    //2、编写配置文件 配置用户名和密码
    //规定key必须是 user和password
    //使用流加载配置文件
    Properties properties = new Properties();
    FileInputStream fis = new FileInputStream("xx");
    properties.load(fis);
    
  //3、获取连接对象使用驱动对象进行连接MySQL数据库  传入url 和用户名密码的配置文件对象
    Connection connection = driver.connect("jdbc:mysql://8.131.95.64/shkjdbc?useSSL=false", properties);
}

1.1.3使用DriverManager获取连接

  • 编写配置文件 将所有的配置信息全部写在配置文件中 (解耦合)
#不要加空格
user=root
password=xx
url=jdbc:mysql://ip:3306/shkjdbc?useSSL=false
driverClass=com.mysql.jdbc.Driver
  • 使用DriverManager调用getConnection()获取连接
@Test
public void test() throws Throwable{
    //使用properties和IO读取配置文件
    Properties properties = new Properties();
    FileInputStream fis = new FileInputStream("src/main/resources/user.properties");
    properties.load(fis);
	
    //将MySQL的驱动加载进来,默认的就已经注册进了DriverManager,(静态代码块)
    Class.forName((properties.getProperty("driverClass")));
	
    //3、通过DriverManager调用方法获取连接对象
Connection connection = DriverManager.getConnection(properties.getProperty("url"),                                                 properties.getProperty("user"),
                            properties.getProperty("password"));
    
}

1.2执行SQL语句(XxxStatement接口)

1.2.1三种不同的Statement

在java.sql包下有三个接口分别定义了对数据库的的调用的不同方式

  • Statement接口:用于执行静态SQL语句(需要拼串)并返回它所生成结果的对象

    会产生SQL注入 完全弃用

  • PreparedStatement接口:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效的执行该语句 解决了SQL注入Statement的子接口

  • CallableStatement接口:用于执行SQL的存储过程 是Statement的子接口

1.2.2使用PreparedStatement执行SQL

基本步骤

  1. 预编译SQL语句(获取PreparedStatement对象) : conn.preparedStatement(String sql)

  2. 设置预编译参数值:stmt.setString(int index,String value) / setInt(int index,int value) …

    也可以直接使用setObject(int index,Object value) (类型不确定时)

  3. 执行SQL语句:stmt.executeUpdate() / stmt.executeQuery()

1.2.2.1使用连接(Connection)对象创建PreparedStatement对象

通过prepareStatement()方法获取PreparedStatement对象

PreparedStatement preparedStatement = connection.prepareStatement(String sql);

1.2.2.2获取连接及关闭连接工具类

前提:用户名 密码 url 驱动信息 都写在了properties配置文件中

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {

	//尽量不使用try-catch 使用throws直接抛
    public static Connection getConnection() {
        FileReader fr = null;
        Connection connection = null;
        try {
            fr = new FileReader("src/main/resources/user.properties");
            Properties properties = new Properties();
            properties.load(fr);
            Class.forName(properties.getProperty("driverClass"));
            connection = DriverManager.getConnection(properties.getProperty("url"), properties.getProperty("user"), properties.getProperty("password"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            if (fr!=null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return connection;
    }

    public static void close(Connection connection, Statement statement) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

1.2.2.2增删改操作

PreparedStatement常用方法

执行insert/delete/update语句

执行SQL语句 返回的结果是int  代表受影响的行数 判断出语句是否执行成功
int executeUpdate() throws SQLException;

不推荐使用 执行SQL语句 返回是 true/false 代表返回的是不是查询语句
boolean execute() throws SQLException;

设置预编译参数值

//索引从1开始 
stmt.setString(int index,String value) / setInt(int index,int value)  .....

插入操作

@Test   //  一般都使用try-catch 这里为了代码简洁使用throws 下面同理
public void insert() throws SQLException {
    //获取连接
    Connection connection = JDBCUtils.getConnection();

    //预编译SQL语句  一个? 代表一个值  并获取PreparedStatement对象
    String sql = "insert into customers(name,email,birth) values(?,?,?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);

    //填充占位符
    //索引位置代表第几个? 从1开始 (不从0开始)
    preparedStatement.setString(1 ,"wsh");
    preparedStatement.setString(2,"wsh@gmail.com");
    preparedStatement.setDate(3, new Date(12, 12, 12));

    //执行SQL语句 使用executeUpdate返回的是对数据库中表的影响行数
    int i = preparedStatement.executeUpdate();
    System.out.println(i);

    //关闭资源 (要使用try-catch-finally)
     JDBCUtils.close(connection, preparedStatement);
}

修改操作

@Test
public void update() throws SQLException {
    //获取连接
    Connection connection = JDBCUtils.getConnection();
    
    //预编译SQL语句 并获取PreparedStatement对象
    String sql = "update customers set name = ? where name = ?";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    
    //设置预编译参数值
    preparedStatement.setString(1, "wsh123");
    preparedStatement.setString(2, "wsh");

    //执行SQL
    int nums = preparedStatement.executeUpdate();
    System.out.println(nums);
		
    //关闭连接
    JDBCUtils.close(connection, preparedStatement);
}

删除操作

   @Test
    public void delete() throws SQLException {
        //1、获取连接
        Connection connection = JDBCUtils.getConnection();
		
        //预编译SQL 获取PreparedStatement对象
        String sql = "delete from customers where name = ?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        
        //2、设置预编译参数值
        preparedStatement.setString(1,"wsh123");
        
        //3、执行SQL语句
        int i = preparedStatement.executeUpdate();
        
   
        //4、关闭资源
        JDBCUtils.close(connection, preparedStatement);

    }
}

1.2.2.3通用的增删改操作

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

public class BaseIDU {
	//                     SQL语句       必须和SQL语句中的?个数一致
    public static int carry(String sql, Object ... objs) {
        PreparedStatement preparedStatement = null;
        Connection connection = null;
        int result = 0;     
        try {
            connection = JDBCUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < objs.length; i++) {
                preparedStatement.setObject(i+1, objs[i]);
            }
            result = preparedStatement.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        JDBCUtils.close(connection, preparedStatement);
        return result;
    }
}

1.2.2.3查询操作ResultSet接口和ResultSetMetaData接口

查询的基本操作顺序

  • 1、获取PreparedStatementment对象,设置sql语句进行预编译
  • 2、设置预编译参数值 setObject(int index,Object obj)
  • 3、执行查询executeQuery() 返回ResultSet结果集对象
  • 4、遍历结果集中的每一行数据使用get()获取每一列数据

preparedStatement接口中的查询方法:

//返回一个ResultSet接口的实现类对象  一般都叫做结果集 里面封装了我们查出的所有数据
ResultSet executeQuery() throws SQLException;

取出结果集(ResultSet)中的每一行的数据

next()方法,跟Collection的迭代器类似,一开始指针也在数据行的前面,返回值是boolean,判断指针所在行是否有数据,有返回true否则返回false,而且也有指针下移的功能。

作用:指针下移判断当前指针行是否有数据

 boolean next() throws SQLException;

遍历结果集就可以使用while循环取出结果集的每一行数据

while(result.next()){ //指针下移判断当前行是否有数据,有的话取,没有的话直接循环结束
    
}

取出结果集中的一行数据中的每一列的值

使用get(索引)/get(列名) 取出对应的列中的数据, select 后的字段顺序就是get的索引

int getInt(int columnIndex)/getInt (String columnLable)
String getString(index columnIndex)/getString(String columnLable)
    ......

ResultSetMetaData的常用方法

当经过查询获取了结果集对象(ResultSet)对象时,通过ResultSet对象调用getMetaData()方法获取结果集的元数据对象(ResultSetMetaData),

  1. getColumnCount() 获得结果集的列数量
  2. getColumnLabel() 获取结果集的列的别名 如果没有别名默认还是真实列名(数据库表中的列名)
  3. getColumnName(int column) 获得指定编号列的真实名称(数据库表中的列名)
  4. getColumnType(int column) 获得指定编号列的类型

查询操作

@Test
public void select() throws SQLException {
    //获取连接
    Connection connection = JDBCUtils.getConnection();
    
  
    String sql = "select * from customers where id = ? ";
    //预编译SQL语句
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
   
    //设置预编译参数值
    preparedStatement.setInt(1, 1);
    
    //获取结果集对象
    ResultSet resultSet = preparedStatement.executeQuery();
    
    //遍历结果集(每一行)
    if (resultSet.next()) {
        //按照索引获取这一行的每一列的数据
        int id = resultSet.getInt(1);
        String name = resultSet.getString(2);
        String email = resultSet.getString(3);
        Date date = resultSet.getDate(4);
        Customer customer = new Customer(id, name, email, date);
        System.out.println(customer);
    }
	
    //查询多个
    List<Customer> customers = new ArrayList<>();
    String sql1 = "select * from customers";
    PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
    ResultSet resultSet1 = preparedStatement1.executeQuery();
    while (resultSet1.next()) {
        int id = resultSet1.getInt();
        String name = resultSet1.getString("name");
        String email = resultSet1.getString("email");
        Date date = resultSet1.getDate(4);
        Customer customer = new Customer(id, name, email, date);
        customers.add(customer);
    }
    System.out.println(customers.size());
}

1.2.2.4通用(不同表)查询方法(ResultSetMetaData+反射综合使用)

/*
    参数一 : 传入的SQL语句
    参数二 : 经过查询得出结果要映射的对象的类型
    参数三 : 预编译参数
    返回值 : 查询出的结果映射成的对象的一个集合
    
    一个泛型方法 Class<T>的泛型就是 指定类类型 Person ....
    
要求数据库中的列名要跟类的属性名和数据类型(数据类型必须一直)保持一致,如果不一致,在查询时要指定别名 别名要跟类属性名一致
	
 * */

public static  <T> List<T> query(String sql, Class<T> clazz, Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {
    
    //获取连接
    Connection connection = JDBCUtils.getConnection();
 
    //获取PreparedStatement对象 + 预编译SQL语句
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
	
    //设置预编译参数值 这里求稳判断一下这个参数是不是null 
    //正常情况下,使用不定参数时,当参数个数为0时,在调用这个方法时,可以不写这个参数
    //这里就是预防 当没有参数时别人调用时写了个null传进去了
    if (params!=null){       
        for (int i = 0; i < params.length; i++) {
            preparedStatement.setObject(i + 1, params[i]);
        }
    }

    //执行SQL语句 获取结果集
    ResultSet resultSet = preparedStatement.executeQuery();

    //获取结果集的元数据 ResultSetMetaData对象
    ResultSetMetaData metaData = resultSet.getMetaData();

    //获取结果集的列数
    int count = metaData.getColumnCount();

    List<T> result = new ArrayList<>();
		
    //遍历结果集
    while (resultSet.next()) {
        T t = clazz.newInstance();

        for (int i = 0; i < count; i++) {
            //获取一条数据每一列的值
            Object object = resultSet.getObject(i + 1);
            
            //获取一条数据每一列的列名(真实列名) 无法获取自定义的别名
            // String columnName = metaData.getColumnName(i + 1);
            
           
            //获取获取结果集一条数据每一列的别名 如果没有起别名 默认还是真实列名
            //所以要求如果数据库中的列名和我们的属性名不一致的话,查询时就要指定别名
            //和我们类中的属性名一致
           ----这里要求结果集的别名或者真实列名必须有一个跟我们类的属性名相同,
           ----否则下面反射获取属性对象是会找不到类中的属性  也就无法设置属性  
            String columnLabel = metaData.getColumnLabel(i + 1);
            
            //反射获取这个对象的指定属性
            Field field = clazz.getDeclaredField(columnLabel);
            field.setAccessible(true);
            //为这个属性赋值
            field.set(t, object);
        }
        result.add(t);
    }
	
   	// ...关闭连接 资源释放
    
    return result;
}

图解流程

基本步骤

  • 1、从数据库里查询出一个结果集(ResultSet)
  • 2、获取结果集的元数据(ResultSetMetaData) 列数,列的别名 等
  • 3、遍历结果集的每一行,然后取出这一行数据每一列的数据
  • 4、通过反射以及从元数据中获取的列名获取映射对象类(JavaBean)的属性(Field)对象
  • 5、通过属性对象为一个映射对象的属性赋值
    在这里插入图片描述

1.2.2.5PreparedStatement如何解决了SQL注入的问题(预编译)

我们先来了解一下SQL注入的原理(即如何实现SQL注入)

核心:改变SQL语句的执行逻辑

比如 经典的SQL注入问题:登录

select * from user where username = '' and password = ''

正常的 ,这条语句的执行逻辑就是从user表中找到username = xx 并且 password = xx 的用户,现在我们使用SQL注入,我们将用户名的值设为 1' or 密码设置为 or '1' = '1

select * from user where username =  '1' or 'and password = '  or '1' = '1'

这时我们发现 这条SQL语句的执行逻辑已经发生了改变 再次解读一下这条SQL:

从user表中找出username= '1' 或者 'and password =' 或者是'1' = '1' 显然 1肯定等于1 也就是说,这条语句一定可以执行成功,就是很简单的设置值就改变了SQL语句的执行逻辑,也就是发生了SQL注入。

通过上面的例子,我们发现,SQL注入的原理就是改变了我们原SQL语句的执行逻辑。

如何防止SQL注入

使用PreparedStatement接口执行SQL。

解决原理:

因为PreparedStatement使用了预编译技术,在我们创建出一个PreparedStatement对象时就要求我们必须把SQL语句传入,它会预编译这条语句(在数据库层面),提前就将分析好SQL语句的执行逻辑。保证SQL语句运行时无论你传任何参数都不会像上面一样改变这个逻辑

预编译

DBServer会对预编译语句提供性能优化,因为预编译语句有可能被重复利用,(直接填充占位符)所以语句再被DBServer的编译器编译后的执行代码被缓存下来,那么下次在调的时候只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句中就会得到执行

举例:还是上面的例子

PreparedStatement会使用站位符,? 来指明参数。 然后这条语句会被MySQL预先编译好,分析好执行逻辑,且这个执行逻辑不会改变。就是在user表中找到一个username = xx 并且 password = xx的。

然后你设置预编译参数的值,我不管你是啥值,我就把你的值传入,有就有没有就没有。因为我的执行逻辑已经确定好了而且不会变了,你传任何的值都不会改变我的执行逻辑,也就不会发生SQL注入了。

select * from user where username = ? and password = ?

所以说,只要一条SQL语句的执行逻辑预先被确定且不会改变,那么就不可以能发生SQL注入

好处(对比Statment接口):

  • 可以操作Blob的问题

  • 批量操作效率更高(预编译)

  • 防止SQL注入

  • 使用了占位符,简化SQL语句的编写

  • Statement会使数据库频繁的编译SQL,可能造成数据库缓冲区溢出

    PreparedStatement使用预编译技术,提高了数据库的执行效率

面向接口编程思想的体现

我们在代码中不会出现任何第三方的API,全部都是使用Java自身的API,有更好的可以执行,具体的就是实现类帮我们做的

1.3批量操作数据(Batch)

1.3.1概述

主要是针对insert(批量插入)

两种方式(只针对PreparedStatement)

  • 1、使用循环(不推荐)
  • 2、使用addBatch()

1.3.2使用addBatch()批量操作

1、修改连接url信息后添加

?rewriteBatchedStatements=true

基本使用

@Test
public void 批量插入数据() throws Throwable{
    Connection connection = JDBCUtils.getConnection();

    String sql = "insert into usertest values (?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setString(1, "213");
    for (int i = 0; i < 10000; i++) {
        preparedStatement.addBatch(); //攒着数据
        if (i%500==0){
            preparedStatement.executeUpdate();  //执行批量数据
            preparedStatement.clearBatch();  //清理
        }
    } 
    //。。。关闭连接。。。
}

第二章_事务处理

2.1MySQL事务概述

数据一旦提交就不能回滚!!!

关于Rollback和Commit的关系

事务的第一大特点,原子性A,说是在一个事务中要么所有操作一起成功,要么一起失败.

==但是这个前提是基于我们要手动的Rollback操作,而如果我们在事务中没有rollback,那么Commit时如果出现错误是不会帮我们回滚整个事物的==

也就是说,Commit只有一个作用:`将事务中成功的SQL语句写入到数据库中,且事务一旦提交就无法回滚(D持久性)`

见MySQL事务笔记

2.2JDBC控制事务

不管是JDBC还是Mybatis乃至各种ORM框架,想要控制事物,首先控制连接(Connection)

开启事务

connection.setAutoCommit(false); //默认是自动提交

设置回滚点

Savepoint savep1 = connection.setSavepoint("Name");

回滚事务

connection.rollback(); //此行代码前面对于数据库的操作全部回滚

---//具体回滚到某个点
connection.rollback(savep1);    

提交事务

connection.commit();  

设置当前连接的事务隔离级别

connection.getTransactionIsolation(); //获取当前连接的隔离级别

connection.setTransactionIsolation(下面的四种隔离级别);
	
	//不支持事务
    int TRANSACTION_NONE             = 0;

	//读未提交
    int TRANSACTION_READ_UNCOMMITTED = 1;

	//读已提交
    int TRANSACTION_READ_COMMITTED   = 2;
	
	//可重复读
    int TRANSACTION_REPEATABLE_READ  = 4;


	//串行化
    int TRANSACTION_SERIALIZABLE     = 8;

通用的处理事务步骤


   获取连接:connection
    
    connection.setAutoCommit(false); //取消自动提交(相当于开始事务)
    
	try{
	xxxx....业务
    }catch(Execption e){
        connection.rollback(); //如果业务出现异常 将事务回滚 
    }finally{
        connection.commit(); 
 //最后 一定要提交事务,不然数据无法写入DB ,并且可以防止一些问题
 //  如果有语句失败了,因为我们已经rollback了,即使执行commit也不会对DB有任何的操作
    }

第三章_DAO封装

第四章_数据库连接池

4.1概述

基本的开发模式

1、获取连接

2、进行SQL操作

3、关闭连接

存在的问题及连接池的基本理念

就跟使用new Thread的方式的方式开启线程和线程池开启线程类似,我们如果不使用连接池,那么也会频繁的对于获取连接和关闭连接会浪费大量的时间,并且不能实现重复利用。并且我们也不好控制连接数,来一个连接请求我们创建一个连接,如果同一时刻大量的连接请求进来,我们的DB会直接崩掉。所以我们引入数据库连接池,核心思想就类似于线程池的核心思想,提前在连接池里创建好一定数量的连接,然后你用的话直接从池里拿,不用了还交给连接池,就省去了创建连接销毁连接的步骤,并且实现了重复利用,大大的提高了效率,并且还可以有控制资源的效果,你来的多了,就去排队等待池里的连接释放,不让你去创建连接,导致DB崩溃。

工作原理

当应用程序启动时,创建一个连接池,在连接池中创建一定数量的连接,当应用程序获得这个连接时,连接的状态变为忙状态,当连接的数量小于设置的空闲连接数,将创建新的连接,但连接数量不能超过设置的最大连接数量。应用程序用完连接后,连接变成空闲状态。当没有空闲连接,应用程序还要获得连接时将等待。

4.2具体的连接池

JDBC的数据库连接池使用javax.sql.DataSource来表示DataSource只是一个接口,(相当于Driver接口,需要提供具体的驱动实现类)该接口通常有服务器(Tomcat,Weblogic,…)提供实现,也有一些开源的组织提供实现。

  • DBCP: Apache旗下的连接池,tomcat自带,速度快,但存在bug,弃用
  • C3P0: 速度较慢,稳定性可以
  • Druid:阿里的数据库连接池,常用,具有SQL拦截分析功能
  • Hikari:高性能的连接池、是SpringBoot框架默认的连接池

4.2.1C3P0连接池

4.2.2DBCP连接池

4.2.3Druid连接池

基本使用测试:

@Test
public void 德鲁伊连接池(){
    DruidDataSource dataSource = new DruidDataSource();
    //配置连接池中的连接信息
    dataSource.setUrl("");
    dataSource.setUsername("");
    dataSource.setPassword("");
    dataSource.setDriverClassName("");

    //配置连接池的具体信息 连接的数量等...
    dataSource.setInitialSize(8);
    ...
       
    //获取连接  获取连接
   Connection connection  = dataSource.getConnection();  
        
}

实战使用(配置文件)

@Test
public void test2() throws  Throwable{
		
    //将所有的配置信息全部写在配置文件中 使用Properties读取
    Properties properties = new Properties();
    FileInputStream fis = new FileInputStream("druid.properties");
    properties.load(fis);

    //提前将我们连接池的各个信息(包括连接信息和连接池的信息)都写在配置文件中 然后读到Properties中 使用DruidDataSourceFactory工厂创建连接池
  DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

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

参数信息

配置缺省值说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:“DataSource-” + System.identityHashCode(this)
url 属性是jdbcUrl 方法是setUrl连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName根据url自动识别这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1) Destroy线程会检测连接的间隔时间2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

第五章_总结

5.1JDBC的使用总流程

1、获取连接(Connection)

底层的流程本质
  • 1、加载驱动(Driver)
  • 2、填写数据库的连接信息 用户名 密码 url
  • 3、使用驱动连接(Connect)数据库
具体的三种实现
  • 第一种:使用原生的创建具体的数据库驱动Driver对象创建连接
  • 第二种:使用DriverManager创建连接
  • 第三种:使用数据库连接池创建连接

2、创建Java层运输SQL语句到DB的对象(PreparedStatement)

3、编写SQL语句,使用PreparedStatement对象预编译SQL语句

4、使用PreparedStatement对象设置预编译参数值(?)

5、使用PreparedStatement对象执行SQL语句 (executeUpdate/executeQuery)

6、如果是查询语句 使用ResultSet对象接受结果集,使用ResultSet对象获取ResultSetMetaData对象,使用ResultSetMetaData对象获取结果集的元数据

7、返回执行结果,查询语句使用反射+结果集+结果集的元数据将结果封装成对象

8、关闭连接

5.2Connection接口的方法

1.获取PreparedStatement对象

preparedStatement ps =connection.preparedStatement(String sql);

2.有关事务的方法

开启事务

connection.setAutoCommit(false);

回滚事务

connection.rollback();

提交事务

connection.commit()

3.关于隔离级别

获取当前连接的隔离级别

connection.getTransactionIsolation()   

设置当前连接的隔离级别

connection.setTransactionIsolation(int level)  //传入下面的隔离界别的表示数字

附:Java层面的隔离级别对象

//不支持事务
int TRANSACTION_NONE             = 0;

//读未提交
int TRANSACTION_READ_UNCOMMITTED = 1;

//读已提交
int TRANSACTION_READ_COMMITTED   = 2;

//可重复读
int TRANSACTION_REPEATABLE_READ  = 4;
//串行化
int TRANSACTION_SERIALIZABLE     = 8;

关闭连接

connection.close();

5.3PreparedStatement的方法

预编译SQL语句

//通过连接获取PreparedStatement对象并预编译SQL语句
connection.preparedStatement(SQL);   

填充预编译参数

preparedStatement.setInt(int index,xx)  //为索引为index的占位符赋值

preparedStatement.setString(int index,xx)

....

preparedStatement.setObject(int index,xx)

执行SQL语句

preparedStatement.executeUpdate();  //执行增删改 返回影响结果数

ResultSet rs = preparedStatement.executeQuery();   //执行查询 返回一个结果集

5.4ResultSet的方法

遍历结果集

resultSet.next()        //指针下移 判断是否有数据 返回值boolean

取出数据

resultSet.getInt(可以填列名String columnname,或者是索引 int index 按照结果集列顺序)
resultSet.getString()
....
resultSet.getObject()

获取结果集的元数据对象

ResultSetMetaData rsmd = resultSet.getMetaData()

5.5ResultSetMetaData的方法

获取结果集的列的别名,没有别名还是真实列名

rsmd.getColumnLable(int index)

获取结果集的列的数量

rsmd.getColumnCount()

获取结果集的真实列名

rsmd.getColumnName(int index)

获得指定编号列的类型

getColumnType(int index)   
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shstart7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值