JDBC基础 -获取连接的方式、结果集、批处理、事务处理、连接池、Apache-DBUtils

文章目录

  • 概述
  • 快速入门(增删改)
  • 获取数据库的五种方式
    • 方式一:获取Driver实现类对象
    • 方式二:反射
    • 方式三:使用DriverManager代替Driver
    • 方式四:Class.forName自动完成注册驱动(推荐)
    • 方式五:使用properties
  • statement、PreparedStatament、CallableStatement
    • statement存在的sql注入问题
    • PreparedStatement的使用
  • ResultSet结果集(查)
  • 封装JDBCUtils
  • 事务处理
  • 批处理
  • 数据库连接池
    • 传统获取Connection的弊端
    • 连接池简介
    • C3P0
    • Druid(德鲁伊)
  • Apache-DBUtils
  • BasicDao

概述

  • jdbc是为了访问不同的数据库提供的统一的接口,为使用者屏蔽了细节问题
  • 使用jdbc可以连接任何提供了jdbc驱动程序的数据库系统,从而完成对数据库的各种操作

原理图

如果不同的数据库,我们的方法不统一,不利于程序管理

在这里插入图片描述

java厂商使用以下的规则:使用一套接口规范,不同的数据库厂商实现,在java程序中调用接口的方法

在这里插入图片描述

快速入门(增删改)

步骤如下

  1. 注册驱动:加载Dirver类
  2. 获取连接:得到Connection
  3. 执行增删改查:发送SQL给mysql执行
  4. 释放资源:关闭相关连接
package jdbc;import com.mysql.jdbc.Driver;import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;public class JDBC1 {public static void main(String[] args) throws Exception{//首先,将jar包加入库//1.注册驱动 com.mysql.jdbc.DriverDriver driver = new Driver();//2.得到连接/** jdbc:mysql:// 规定的协议,通过jdbc的方式连接mysql* localhost 主机 也可以是ip地址* 3306 端口* testdb 连接到mysql dbms的具体的数据库* MySQL的连接本质就是socket连接* */String url = "jdbc:mysql://localhost:3306/testdb";//将用户名和密码放到Properties对象中Properties properties = new Properties();properties.setProperty("user","root");properties.setProperty("password","yb0os1");//connect 网络连接Connection connect = driver.connect(url, properties);//3.执行sql语句String sql = "insert into actor values(null,'刘德华'),(null,'王恬心');";//statement 用于执行静态SQL语句并返回其生成的结果的对象Statement statement = connect.createStatement();//返回的是影响的行数int rows = statement.executeUpdate(sql);System.out.println(rows>0?"成功":"失败");//4.注册驱动statement.close();connect.close();}
}

获取数据库的五种方式

方式一:获取Driver实现类对象

属于静态加载,灵活性差,依赖性强(因为直接new的Dirver,在前面已经导入好确定的包了)

Dirver driver = new Driver();
string url = "xxx";
Properties info = new Properties();
info.setProperties("user","xxx");
info.setProperties("password","xxx");
Connection conn = driver.connect(url,info);

方式二:反射

Class<Driver> driverClass = com.mysql.jdbc.Driver.class;
Driver driver = driverClass.newInstance();
String url = "jdbc:mysql://localhost:3306/testdb";
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","yb0os1");
Connection connect = driver.connect(url, properties);
System.out.println(connect);

方式三:使用DriverManager代替Driver

        Class<Driver> driverClass = com.mysql.jdbc.Driver.class;Driver driver = driverClass.newInstance();String url = "jdbc:mysql://localhost:3306/testdb";String user = "root";String password = "yb0os1";DriverManager.registerDriver(driver);Connection connection = DriverManager.getConnection(url, user, password);System.out.println(connection);

方式四:Class.forName自动完成注册驱动(推荐)

        //使用反射加载了Driver类/** 看看源码* 静态代码块 在类加载的时候执行一次 static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}* */Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/testdb";String user = "root";String password = "yb0os1";Connection connection = DriverManager.getConnection(url, user, password);System.out.println(connection);

注意

mysqL驱动5.1.6可以无需CLass .forName(“com.mysql.jdbc.Driver”);
从jdk1.5以后使用了jdbc4,不再需要显示调用class.forName()注册驱动而是自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册

方式五:使用properties

        Properties properties = new Properties();properties.load(new FileInputStream("./info.properties"));String user = properties.getProperty("user");String password = properties.getProperty("password");String database = properties.getProperty("database");String url = properties.getProperty("url");String driver = properties.getProperty("driver");Class.forName(driver);Connection connection = DriverManager.getConnection(url + database, user, password);Statement statement = connection.createStatement();

statement、PreparedStatament、CallableStatement

是一个接口

用于执行静态SQL语句并返回其生成的结果的对象

在连接建立完成之后,对数据库的访问、执行命令或者SQL语句,可以通过:

  • Statement:存在SQL注入问题
  • PreparedStatament:预处理
  • CallableStatement:存储过程

SQL注入就是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,恶意攻击数据库。

在JDBC中防范SQL注入,只要使用PreparedStatement就OK了

statement存在的sql注入问题

package jdbc.statementSQL;import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;public class testsql {public static void main(String[] args) throws Exception {Properties properties = new Properties();properties.load(new FileInputStream("./info.properties"));String user = properties.getProperty("user");String password = properties.getProperty("password");String driver = properties.getProperty("driver");String url = properties.getProperty("url");String database = properties.getProperty("database");Class.forName(driver);Connection connection = DriverManager.getConnection(url + database, user, password);Statement statement = connection.createStatement();//在数据库中root账户对应的密码不是输入的内容//execute 执行任意sql语句 返回布尔值ResultSet resultSet = statement.executeQuery("select * from actor where id = 'root' and name = ''or '1'='1';");if (resultSet.next())System.out.println("登录成功");resultSet.close();statement.close();connection.close();}
}

PreparedStatement的使用

  • 在SQL语句中可以使用?代表参数,调用setxxx方法为这些?的位置赋值,该方法是有两个参数,第一个参数为?参数的索引(从1开始),第二个参数是?参数所代表的具体的值
  • 调用executeQuery返回ResultSet结果集
  • 调用executeUpdate进行增加、删除、修改操作,返回被影响的行数

好处就是:不需要拼接sql语句、有效解决sql注入安全问题、减少编译次数 提高效率

防止sql注入的原理:预处理

在执行sql语句之前,数据库服务前先将sql语句进行编译,确定sql语句的基本“结构”,放在缓存区,当真正执行sql语句时,直接从缓存区中去拿取,而不会进行再次编译。这样,即使在执行sql语句时,发现用户传入的参数中带有sql关键字,也不会被识别编译,只会被当成参数替换占位符,拼接在sql语句中,这样就很好的避免的sql注入。

package jdbc.preparedStatement;import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;public class demo {public static void main (String[] args) throws Exception {Properties properties = new Properties();properties.load(new FileInputStream("./info.properties"));String user = properties.getProperty("user");String password = properties.getProperty("password");String driver = properties.getProperty("driver");String url = properties.getProperty("url");String database = properties.getProperty("database");Class.forName(driver);Connection connection = DriverManager.getConnection(url + database, user, password);//需要直接使用sql语句与preparedStatement关联起来String sql = "select * from actor where id=? and name=?;";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1,"root");
//        preparedStatement.setString(2,"'or '1'='1");//万能密码不可以用了preparedStatement.setString(2,"yb0os1");//执行//这里不要填sql语句 因为前面已经关联起来了//如果还是加了sql语句 那么使用的带有?的 执行的没有被预处理的sql语句ResultSet resultSet = preparedStatement.executeQuery();if (resultSet.next())System.out.println("登录成功");resultSet.close();preparedStatement.close();connection.close();}
}

ResultSet结果集(查)

是一个接口

表示数据库结果集的数据表,一般通过执行查询数据库语句生成

该对象保持一个光标指向其当前的数据行,最初光标位于第一行之前,next方法可以使光标移动到下一行,当resultset对象中没有更多行时返回false(可以使用while循环遍历结果集)

        Properties properties = new Properties();properties.load(new FileInputStream("./info.properties"));String user = properties.getProperty("user");String password = properties.getProperty("password");String driver = properties.getProperty("driver");String url = properties.getProperty("url");String database = properties.getProperty("database");Class.forName(driver);Connection connection = DriverManager.getConnection(url + database, user, password);Statement statement = connection.createStatement();// 获取结果集 一开始在第一行之前 先进行next到第一行 然后继续 到最后一行继续next返回了fasleResultSet resultSet = statement.executeQuery("select id,name from actor;");//还可以previous 就是向前移动一行 向前的一行不存在 就是返回falsewhile (resultSet.next()){//对于每一行的每一列进行获取 也可以根据列名来获取// getXxx(列的索引||列名)System.out.println(resultSet.getInt(1)+"-"+resultSet.getString(2));}resultSet.close();statement.close();connection.close();

在这里插入图片描述

rows是一个ArrayList,存储着每行数据

封装JDBCUtils

package jdbc;import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;//工具类
public class JDBCUtils {private static String user;private static String password;private static String driver;private static String url;// 静态代码块:为了个上述的属性赋值 读取文件static {try {Properties properties = new Properties();properties.load(new FileInputStream("./mysql.properties"));user = properties.getProperty("user");password = properties.getProperty("password");driver = properties.getProperty("driver");url = properties.getProperty("url");Class.forName(driver);//这里可以不用这样加载类 因为自动加载类} catch (Exception e) {//将编译异常转换为运行异常//调用者可以选择捕获该异常,或者选择默认处理该异常的方式throw new RuntimeException(e);}}// 连接public static Connection getConnection() {try {return DriverManager.getConnection(url,user,password);} catch (SQLException e) {//同理 抛出 如果处理交给调用者throw new RuntimeException(e);}}// 关闭连接/** 这里涉及到 ResultSet、Statement、Connection* 先判断是否存在 再选择是否要释放* null代表不释放* */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);}}
}

事务处理

  • JDBC程序中,当一个Connection对象创建时,默认情况下会自动提交事务:每次执行一个SQL语句,如果执行成功就会向数据库提交,不能回滚。

  • 需要多个SQL语句作为一个整体执行的时候需要事务,可以使用Connection的setAutoCommit(false)取消自动提交事务

  • commit()方法提交事务; rollback()方法回滚事务

package jdbc;import org.junit.Test;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;public class Transaction {Connection connection = null;PreparedStatement preparedStatement=null;String sql1="update account set balance = balance - 100 where name='马云';";String sql2="update account set balance = balance + 100 where name='马化腾';";@Testpublic void noTransaction(){try {connection = JDBCUtils.getConnection();//默认情况下 connection默认自动提交preparedStatement = connection.prepareStatement(sql1);preparedStatement.executeUpdate();int i = 1/0; // 抛出异常 使后续的修改不可达preparedStatement = connection.prepareStatement(sql2);preparedStatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);}finally {try {connection.close();preparedStatement.close();} catch (SQLException e) {throw new RuntimeException(e);}}}@Testpublic void useTransaction(){try {connection = JDBCUtils.getConnection();connection.setAutoCommit(false);//不让他自动提交 相当于开始的事务preparedStatement = connection.prepareStatement(sql1);preparedStatement.executeUpdate();int i = 1/0; // 抛出异常 使后续的修改不可达preparedStatement = connection.prepareStatement(sql2);preparedStatement.executeUpdate();connection.commit();//提交} catch (Exception e) {try {//抛出以上进入这里 可以进行回滚 撤销执行的SQL//默认回滚到事务开始的状态 也就是connection.setAutoCommit(false);这里System.out.println("发生异常,进行回滚");connection.rollback();throw new RuntimeException(e);} catch (SQLException ex) {ex.printStackTrace();}}finally {try {connection.close();preparedStatement.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
}

批处理

  • 当需要一次性处理多条记录的时候,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理,效率up
  • JDBC批量处理语句的方法:
    • addBatch():添加需要批处理的SQL语句或者参数
    • executeBatch():执行批量处理语句
    • clearBatch():清空批处理包的语句
  • JDBC连接MySQL时,要使用批处理的话要在url中加上?rewriteBatchedStatements=true
  • 批处理往往结合preparedStatement使用,既减少编译次数,又减少运行次数,大大提高了效率
package jdbc;import org.junit.Test;import java.sql.Connection;
import java.sql.PreparedStatement;public class Batch {@Testpublic void noBatch() throws Exception {/** 不使用批处理添加五千条数据* */Connection connection = JDBCUtils.getConnection();String sql = "insert into actor values (?,?)";PreparedStatement preparedStatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();for (int i = 0; i < 5000; i++) {preparedStatement.setString(1, i + "");preparedStatement.setString(2, "tom" + i);preparedStatement.executeUpdate();}long end = System.currentTimeMillis();System.out.println("不使用批处理耗时" + (end - start) + "毫秒");//10502毫秒JDBCUtils.close(null, preparedStatement, connection);}@Testpublic void useBatch() throws Exception {/** 使用批处理添加五千条数据* 记得再url上加入 ?rewriteBatchedStatements=true* */Connection connection = JDBCUtils.getConnection();String sql = "insert into actor values(?,?)";PreparedStatement preparedStatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();for (int i = 0; i < 5000; i++) {preparedStatement.setString(1, i + "");preparedStatement.setString(2, "tom" + i);// 这里做了什么工作?preparedStatement.addBatch();//当加入1000条sql语句的时候 批量处理一下if ((i + 1) % 1000 == 0) {preparedStatement.executeBatch();//清空preparedStatement.clearBatch();}}long end = System.currentTimeMillis();System.out.println("使用批处理耗时" + (end - start) + "毫秒");//64毫秒JDBCUtils.close(null, preparedStatement, connection);}
}

我们来看看addBatch

public void addBatch() throws SQLException {synchronized(this.checkClosed().getConnectionMutex()) {//进来先判断这个ArrayList数组是否存在 存在就跳过这个if 否则新建if (this.batchedArgs == null) {this.batchedArgs = new ArrayList();}//this.parameterValues.length就是?的个数 //检查每一个参数是否设置 也就是是否预处理了for(int i = 0; i < this.parameterValues.length; ++i) {this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);}//将预处理之后的sql语句加入数组之中this.batchedArgs.add(new BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));}
}

在这里插入图片描述

数据库连接池

传统获取Connection的弊端

  • 传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证IP、用户名、密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将会占用很多的系统资源,容易导致服务器崩溃。
  • 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将会导致数据库内存泄漏,MySQL崩溃
  • 传统获取连接的方式,不能控制创建的连接数量,如果一下子连接数量过多也可能导致内存泄露,MySQL崩溃
  • 解决传统开发中的数据库连接问题,采用数据库连接池
    // 建立5000连接public void noUsePoll(){long start = System.currentTimeMillis();for (int i = 0; i < 5000; i++) {Connection connection = JDBCUtils.getConnection();//进行sql处理//先假设没有关闭 "Too many connections"  这就是一下子涌进太多的sql连接//进行关闭测试时间JDBCUtils.close(null,null,connection);}long end = System.currentTimeMillis();System.out.println("传统方式5000次连接,所需时间:"+(end-start)+"ms"); //5839ms 这是一个非常耗时的}

连接池简介

  • 预先在缓冲池中放入一定数量的连接,当需要建立连接时,只需要从“缓冲池”取出一个,使用完毕之后放回去(这里不是关闭仅仅时还给缓冲池)
  • 数据库连接池负责分配、管理和释放数据库连接,允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个新连接
  • 当应用程序向连接池请求的连接数量超过池内最大的连接数量时,这些请求将被加入到等待队列之中
  • 大白话讲:我们建立连接的时候是把连接池里面的连接“拿过来”,释放连接的时候是把“拿过来”的连接“还回去”在这里插入图片描述

DataSource只是一个接口,是让第三方进行实现的

  • C3P0 数据库连接池:速度相对较慢,稳定性不错
  • DBCP 数据库连接池:速度较c3p0快,不稳定
  • Proxool 数据库连接池:有监控连接池状态的功能,稳定性不如c3p0
  • BoneCP 数据库连接池:速度快
  • Druid(德鲁伊):集DBCP、Proxool 、C3P0优点于一身的数据库连接池(推荐

C3P0

package jdbc;import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;import java.io.FileInputStream;
import java.lang.annotation.Target;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;public class C3P0using {@Test// 方式1:相关参数在程序中指定 user url passwordpublic void testC3P0_01() throws Exception {// 1、创建一个数据源对象ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();//2、通过配置文件获取相关连接信息Properties properties = new Properties();properties.load(new FileInputStream("./mysql.properties"));String user = properties.getProperty("user");String password = properties.getProperty("password");String driver = properties.getProperty("driver");String url = properties.getProperty("url");//给数据源设置相关参数comboPooledDataSource.setDriverClass(driver);//连接是由comboPooledDataSource管理comboPooledDataSource.setUser(user);comboPooledDataSource.setPassword(password);comboPooledDataSource.setJdbcUrl(url);//初始化数据源的连接数:初始化的连接池里面有多少个连接,可以增加到maxPoolSizecomboPooledDataSource.setInitialPoolSize(10);//数据源的最大连接数:第51个进入等待队列comboPooledDataSource.setMaxPoolSize(50);long start = System.currentTimeMillis();for (int i = 0; i < 5000; ++i) {//从 DataSource 接口实现Connection connection = comboPooledDataSource.getConnection();connection.close();}long end = System.currentTimeMillis();System.out.println("c3p0 5000次连接时间:"+(end-start)+"ms");//1108ms}@Test// 方式2:使用配置文件模板来完成// 1、将 c3p0-config.xml 放到src下面public void testC3P0_02() throws SQLException {// 2、填入的是xml中设置的连接池的名称ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("yb0os1");Connection connection = comboPooledDataSource.getConnection();System.out.println("连接成功");connection.close();}
}

c3p0-config.xml

<c3p0-config>
<!--数据源的名称 也就是 连接池--><named-config name="yb0os1">
<!-- 驱动类 --><property name="driverClass">com.mysql.jdbc.Driver</property><!-- url--><property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/testdb</property><!-- 用户名 --><property name="user">root</property><!-- 密码 --><property name="password">yb0os1</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>

Druid(德鲁伊)

druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb?rewriteBatchedStatements=true
username=root
password=yb0os1
#初始化的连接数
initialSize=10
#最小连接数
minIdle=5
#最大连接数
maxActive=50
#最大等待时间 在等待队列的最长等待时间 ms
maxWait=5000
package jdbc;import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.Test;import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;public class Druidusing {@Testpublic void useDruid() throws Exception {//1、加入jar包//2、配置文件 druid.properties//3、创建properties对象 读取配置文件Properties properties = new Properties();properties.load(new FileInputStream("./src/druid.properties"));//4、创建一个指定参数的数据库连接池  基于properties进行配置DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);long start = System.currentTimeMillis();for (int i = 0; i < 500000; ++i) {Connection connection = dataSource.getConnection();connection.close();}long end = System.currentTimeMillis();System.out.println("druid 500000 次连接时间:"+(end-start)+"ms");//349ms}
}

基于德鲁伊的封装

package jdbc;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;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(){try {return ds.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}}//数据库连接池close不是真正的释放 而是将connection放回连接池public static void close(ResultSet resultSet, Statement statement,Connection connection){try {//这里的close和JDBCUtils的close是不一样的 和JDBCUtils运行类型是不一样的//connection只是一个接口 他实现类是不一样的 一个是实现的德鲁伊 一个实现的是mysql的if (resultSet!=null)resultSet.close();if (statement!=null)statement.close();if (connection!=null)connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}
}

Apache-DBUtils

分析 JDBCUtilsByDruid 存在的问题

  • 关闭connection后,结果集resultSet无法使用
  • resultSet不利用数据的管理

自己的方法解决

在这里插入图片描述

package jdbc;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class useUtilsByDruid {private static final List<Actor> mess = new ArrayList<>();public static void main(String[] args) throws Exception {Connection connection = JDBCUtilsByDruid.getConnection();String sql = "select * from actor where id <=?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setInt(1,10 );ResultSet resultSet = preparedStatement.executeQuery();while (resultSet.next()){mess.add(new Actor(resultSet.getString("id"),resultSet.getString("name")));
//            System.out.println(resultSet.getString("id")+"-"+resultSet.getString("name"));}JDBCUtilsByDruid.close(resultSet,preparedStatement,connection);//关闭了之后还是可以使用数据for (Actor actor : mess) {System.out.println(actor.getId()+"-"+actor.getName());}}
}

Apache-DBUtils

  • commons-dbutils是Apache组织提供的一个开源的JDBC工具类库,他是对JDBC的封装,使用dbutils能极大的简化jdbc编码的工作量
  • DbUtils类
    • QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增删改查、批处理
    • ResultSetHandler接口:该接口用于处理 java.sql.ResultRet ,将数据按要求转换成另一种形式

在这里插入图片描述

package jdbc;import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;public class UseDbUtils {Connection connection = null;//查询:返回的是多行多列记录@Testpublic void testQueryMany() {try {Properties properties = new Properties();properties.load(new FileInputStream("./src/druid.properties"));DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);connection = dataSource.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "select * from actor where id <= ?";/*query的参数:连接、sql语句、存放的容器、参数* 1、query方法 就是执行sql语句 得到 ResultSet 封装到ArrayList集合中* 2、返回集合* 3、connection:连接* 4、sql:执行的sql语句* 5、new BeanListHandler<>(Actor.class):将ResultSet->Actor对象->封装到ArrayList*   底层使用反射机制 获取Actor的属性* 6、 10:给?赋值 是一个可变形参* 7、底层得到的 ResultSet 会在query关闭;创建的preparedStatement也在底层关闭* */List<Actor> list =queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 10);for (Actor actor : list) {System.out.println(actor.getId() + "-" + actor.getName());}} catch (IOException e) {throw new RuntimeException(e);} catch (Exception e) {throw new RuntimeException(e);} finally {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}//查询:返回的是单行多列记录@Testpublic void testQuerySingle() {try {connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "select * from actor where id = ?";Actor actor =queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 10);if (actor != null) System.out.println(actor.getId() + "-" + actor.getName());} catch (Exception e) {throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null, null, connection);}}//查询:返回的是单行单列记录@Testpublic void testQueryScaler() {try {connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "select `name` from actor where id = ?";Object obj =queryRunner.query(connection, sql, new ScalarHandler(), 10);System.out.println(obj);} catch (Exception e) {throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null, null, connection);}}//dml操作 增删改@Testpublic void testDML() {try {connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "update actor set name=? where id =?";//返回值是受影响的行数//dml都是updateint affectedRow = queryRunner.update(connection, sql, "张三丰", "100");System.out.println(affectedRow > 0 ? "执行成功" : "执行未影响到表");} catch (SQLException e) {throw new RuntimeException(e);}}
}

query函数

public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {PreparedStatement stmt = null;ResultSet rs = null;T result = null;try {stmt = this.prepareStatement(conn, sql);this.fillStatement(stmt, params);rs = this.wrap(stmt.executeQuery());result = rsh.handle(rs);} catch (SQLException var33) {this.rethrow(var33, sql, params);} finally {try {this.close(rs);} finally {this.close((Statement)stmt);}}return result;}

BasicDao

apache-dbutils+druid还有一些不足

  • SQL语句是固定的,不能通过参数传入,通用性不足
  • 对于select操作,如果有返回值,返回值类型不能固定需要使用泛型
  • 将来的表有很多,业务很复杂,不可能只靠一个java类完成

DAO:data access object 访问数据的对象

这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。在BaiscDao 的基础上,实现一张表对应一个Dao,更好的完成功能,比如 Customer表-Customer.java类(iavabean)-CustomerDao.java

在这里插入图片描述

BasicDAO.java

package dao_.dao;import dao_.utils.JDBCByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;public class BasicDAO<T> {private QueryRunner qr = new QueryRunner();/**** dml操作 也就是增删改的操作* @param sql 要执行的sql语句* @param params sql语句中的参数* @return 受影响的行数*/public int update(String sql,Object...params){Connection connection = JDBCByDruid.getConnection();try {return qr.update(connection,sql,params);} catch (SQLException e) {throw new RuntimeException(e);}finally {JDBCByDruid.close(null,null,connection);}}/**** 查询多行多列* @param sql 执行的sql语句* @param clazz 类的class对象,反射用到* @param params sql语句中的参数* @return 返回的数据*/public List<T> queryMulti(String sql,Class<T>clazz,Object...params){Connection connection = JDBCByDruid.getConnection();try {return qr.query(connection,sql,new BeanListHandler<>(clazz),params);} catch (SQLException e) {throw new RuntimeException(e);}finally {JDBCByDruid.close(null,null,connection);}}/**** 查询单行多列* @param sql 执行的sql语句* @param clazz 类的class对象,反射用到* @param params sql语句中的参数* @return 返回的数据*/public T querySingle(String sql,Class<T>clazz,Object...params){Connection connection = JDBCByDruid.getConnection();try {return qr.query(connection,sql,new BeanHandler<>(clazz),params);} catch (SQLException e) {throw new RuntimeException(e);}finally {JDBCByDruid.close(null,null,connection);}}/**** 查询单行单列* @param sql 执行的sql语句* @param params sql语句中的参数* @return 返回的数据*/public Object queryScalar(String sql,Object...params){Connection connection = JDBCByDruid.getConnection();try {return qr.query(connection,sql,new ScalarHandler(),params);} catch (SQLException e) {throw new RuntimeException(e);}finally {JDBCByDruid.close(null,null,connection);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/46477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

请你谈谈:BeanDefinition类作为Spring Bean的建模对象,与BeanFactoryPostProcessor之间的羁绊

那么&#xff0c;我们如何理解Spring Bean的建模对象呢&#xff1f;简而言之&#xff0c;它是指用于描述和配置Bean实例化过程的模型对象。有人可能会提出疑问&#xff0c;既然只需要Class&#xff08;类&#xff09;就可以实例化一个对象&#xff0c;Class作为类的元数据&…

springboot websocket 知识点汇总

以下是一个详细全面的 Spring Boot 使用 WebSocket 的知识点汇总 1. 配置 WebSocket 添加依赖 进入maven官网, 搜索spring-boot-starter-websocket&#xff0c;选择版本, 然后把依赖复制到pom.xml的dependencies标签中 配置 WebSocket 创建一个配置类 WebSocketConfig&…

mysql不初始化升级

1、下载mysql&#xff0c;下载地址&#xff1a;MySQL :: Download MySQL Community Server 2、解压下载好的mysql&#xff0c;修改配置文件的datadir指定目录为当前数据存储的目录 3、通过管理员cmd进入新版本mysql的bin目录&#xff0c; 然后执行命令安装mysql服务&#xff…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(七)-通过无人机实现无线接入的独立部署

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

JVM:垃圾回收器

文章目录 一、介绍二、年轻代-Serial垃圾回收器三、老年代-SerialOld垃圾回收器四、年轻代-ParNew垃圾回收器五、老年代-CMS&#xff08;Concurrent Mark Sweep&#xff09;垃圾回收器六、年轻代-Parllel Scavenge垃圾回收器七、Parallel Old垃圾回收器八、G1垃圾回收器 一、介…

仅在少数市场发售?三星Galaxy Z Fold 6 Slim折叠屏手机更轻更薄

在智能手机的创新之路上&#xff0c;三星一直是行业的领跑者之一。随着Galaxy Z Fold系列的不断进化&#xff0c;三星再次突破技术边界&#xff0c;推出了更为轻薄的Galaxy Z Fold 6 Slim。 这款新型折叠屏手机以其独特的设计和卓越的性能&#xff0c;为用户带来了全新的使用体…

护眼台灯真的护眼吗?要注意学生如何正确使用台灯!

孩子们面临着越来越多的视力挑战&#xff0c;在近视学生中&#xff0c;近10%为高度近视&#xff0c;且占比随年级升高而增长。幼儿园6岁儿童中有1.5%为高度近视&#xff0c;而高中阶段则达到了17.6%。青少年是国家的未来和希望&#xff0c;而他们的视力健康却面临着前所未有的挑…

一篇讲清楚怎么选算力租赁平台

选择算力租赁平台时&#xff0c;需要考虑多个因素以确保找到最适合自己需求的服务。以下是一些关键点&#xff0c;可以帮助您做出明智的选择&#xff1a; 明确需求&#xff1a;首先&#xff0c;确定您的项目需要哪种类型的计算资源&#xff0c;比如CPU、GPU或FPGA&#xff0c;以…

Cadence23学习笔记(二)

原理图设计界面中就可以直接新建PCB: 亲测&#xff1a;需要画完原理图&#xff0c;并且DRC通过之后才可以&#xff01; 放置完元器件之后要规定元件的Footprint &#xff0c;注意PCB封装名要和库文件中的名字对应&#xff1a; DRC按钮&#xff1a; 点击图标 N, 生成第一网表&…

车载音视频MediaPlayer优化方案

媒体播放现状 从手机到车载&#xff0c;在很多地方还是有很大的不同。针对多媒体的场景Android车机目前大部分结构大致结构如下图&#xff1a; 从以上图看出的问题&#xff1a; 各个音视频APP单独实现播控界面&#xff0c;播放链路不一致&#xff0c;使用的底层播放器和音频焦…

基于Spring Boot的高校后勤餐饮管理系统

1 项目介绍 1.1 研究背景 “互联网”时代的到来&#xff0c;既给高校后勤管理发展带来了机遇&#xff0c;也带来了更大的挑战。信息化应用已经开始普及&#xff0c;传统的高校后勤餐饮管理模式往往存在着效率低下、信息不透明、资源浪费等问题&#xff0c;已经难以满足现代高…

Linux系统之部署经典魔塔小游戏

Linux系统之部署经典魔塔小游戏 一、魔塔小游戏介绍1.1 魔塔小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看apa…

STM32第十八课:SPIFlash

目录 需求一、SPI概要二、SPI配置1.开时钟2.配置IO3.配置&使能SPI 三、FLash操作函数1.SPI发送数据2.FLASH写使能3.FLASH等待操作完成4.FLASH页写操作5.FLASH读操作6.FLASH扇区擦除 四、需求实现 需求 通过SPI控制FLash进行数据的保存和删除。 一、SPI概要 在我们使用UA…

【python】OpenCV—European Article Number

参考学习来自&#xff1a;OpenCV基础&#xff08;25&#xff09;条码和二维码扫的生成与识别 1 条形码介绍 EAN-13是欧洲物品编码&#xff08;European Article Number&#xff09;的缩写&#xff0c;是一种广泛使用的条形码标准&#xff0c;特别是在超级市场和其它零售业中。…

OpenCV解决验证码(数字和字母)识别(Python)

文章目录 前言一、准备验证码图片 前言 OpenCV是一个基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习软件库。它支持Windows、Linux、Mac OS、Android和iOS等多个操作系统&#xff0c;提供了丰富的图像处理和计算机视觉功能&#xff0c;包括但…

RPC与服务的注册发现

文章目录 1. 什么是远程过程调用(RPC)?2. RPC的流程3. RPC实践4. RPC与REST的区别4.1 RPC与REST的相似之处4.2 RPC与REST的架构原则4.3 RPC与REST的主要区别 5. RPC与服务发现5.1 以zookeeper为服务注册中心5.2 以etcd为服务注册中心 6. 小结参考 1. 什么是远程过程调用(RPC)?…

(自用)网络编程

OSI七层协议模型 (open system interconnection) 应用层————为应用数据提供服务 表示层————数据格式转化&#xff0c;数据加密 会话层————建立、维护和管理会话 传输层————建立、维护和管理端到端的链接&#xff0c;控制数据传输的方式 网络层————数据…

手机删除的文件能恢复吗?删除不等于永别,3个技巧助你找回

安卓手机中的文件&#xff0c;就像是数字世界里的繁星&#xff0c;记录着我们的点点滴滴。然而&#xff0c;有时我们可能会不小心删除了某些重要的文件&#xff0c;让我们感到惋惜和困惑。删除的文件能恢复吗&#xff1f;别担心&#xff0c;删除并不等于永别&#xff0c;我们也…

CentOS 停服后,服务器 OS 路在何方?

2024 年 6 月 30 日&#xff0c;CentOS Linux 7 终止其生命周期&#xff08;EOL&#xff09;&#xff0c;至此 CentOS 全系列版本也已停止维护&#xff0c;属于 CentOS 的时代彻底终结。CentOS 停止维护后&#xff0c;用户将无法获得包括问题修复和功能更新在内的任何软件维护和…

小程序里面使用vant ui中的vant-field组件,如何使得输入框自动获取焦点

//.wxml <van-fieldmodel:value"{{ userName }}"placeholder"请输入学号"focus"{{focusUserName}}"/>// .js this.setData({focusUserName: true});vant-field