java_Jdbc
Jdbc原理说明
jdbc 本质上是一系列的接口,对于mysql 数据库厂商而言,它需要设计一个
类来实现接口的所有方法,以完成这个方法的功能(即数据库驱动)。
因为不同的数据库实现相同的功能,可能需要不同的操作去实现,如果把这些问题
全都留给程序员来解决无疑是很大的负担(使用也不方便,不利于维护),极大影响了
开发效率和维护成本。所以便有了 Jdbc,它规定了一系列接口和接口实现的功能(抽象),
具体功能的实现就交由对应的厂商来负责。
这样对于不同的数据库,都可以运用一套API来操作,极大的遍历的程序员使用数据库。
Jdbc使用入门
1.注册驱动 - 加载Driver类
2.获取连接 - 加载Connection类
3.执行增删改查 - 发送SQL语句给mysql执行(如果有返回结果,则接收返回结果)
4.释放资源 - 关闭相关连接
简单举例
package Jdbc;
import com.mysql.cj.jdbc.Driver;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @author WJ
* @date 2022/7/26 17:59
* @version 1.0
* 入门使用 jdbc 连接数据库(知识梳理)
*/
public class connection1 {
public static void main(String[] args) throws SQLException {
//1 初始化驱动
Driver driver = new Driver();
//2 建立连接
// jdbc:mysql:// 表示使用的是什么数据库
// 127.0.0.1:3306 连接的数据库所在的主机IP以及Port
// test 连接的数据库的名字
String url = "jdbc:mysql://127.0.0.1:3306/test";
//配置用户名和密码
Properties properties = new Properties();
// 用户名的键值必须为 user
properties.setProperty("user","root");
// 密码的键值为 password
properties.setProperty("password","123456");
//拿到连接
Connection connect = driver.connect(url, properties);
//3 执行sql语句
String sql = "select * from websites";
//创建sql语句发送以及拿到返回结果的类
Statement statement = connect.createStatement();
//拿到返回结果
ResultSet resultSet = statement.executeQuery(sql);
System.out.println("id\tname\turl\tage\tcountry");
while (resultSet.next()){
System.out.println(resultSet.getString("id")+"\t" +
resultSet.getString("name")+"\t" +
resultSet.getString("url")+"\t" +
resultSet.getString("age")+"\t"+resultSet.getString("country"));
}
//4释放资源
resultSet.close();
statement.close();
connect.close();
}
}
/*
结果展示:
id name url age country
1 Google https://www.google.cm/ 1 USA
2 淘宝 https://www.taobao.com/ 13 CN
3 菜鸟教程 http://www.runoob.com 5892 CN
4 微博 http://weibo.com/ 20 CN
5 Facebook https://www.facebook.com/ 3 USA
*/
连接数据库的5种方式
前言
mysql 驱动5.1.6可以无需 Class.forName(“com.mysql.cj.jdbc.Driver”);
从jdk1.5以后使用jdbc4,不需要再显式的调用Class.forName()注册驱动而是自动调用驱动jar包下
META-INF\services\java.sql.Driver 文本中的类名去注册。
方式1
//就是上述使用的方式:
//1 初始化驱动
Driver driver = new Driver();
//2 建立连接
// jdbc:mysql:// 表示使用的是什么数据库
// 127.0.0.1:3306 连接的数据库所在的主机IP以及Port
// test 连接的数据库的名字
String url = "jdbc:mysql://127.0.0.1:3306/test";
//配置用户名和密码
Properties properties = new Properties();
// 用户名的键值必须为 user
properties.setProperty("user","root");
// 密码的键值为 password
properties.setProperty("password","123456");
//拿到连接
Connection connect = driver.connect(url, properties);
connect.close();
方式2
//方式2连接数据库
public void connect2() throws Exception
{
//通过反射动态加载Driver 类
Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
Driver dirver = (Driver) cls.newInstance();
// 余下步骤与方式1一样
}
方式3
//方式3连接数据库
public void connect3() throws Exception {
//通过DriverManager来统一管理连接
//通过反射动态加载Driver 类
Class<?> cls = Class.forName("com.mysql.cj.jdbc.Driver");
Driver dirver = (Driver) cls.newInstance();
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "123456";
//注册驱动
DriverManager.registerDriver(dirver);
//通过DriverManager来获取连接
Connection connection = DriverManager.getConnection(url, user, password);
/*
与方式一一样
*/
//注意断开连接释放资源
connection.close();
}
方式4
//方式4
public void connect4() throws Exception{
//在源码里面,存在静态方法自动注册一个Driver
/*
源码:
static {
try {
//自动注册一个Driver(默认注册)
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
*/
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "123456";
//通过DriverManager来获取连接
Connection connection = DriverManager.getConnection(url, user, password);
/*
与方式一一样
*/
//注意断开连接释放资源
connection.close();
}
方式5
//方式5
//通过配置文件来管理
//配置文件详情如下:
url=jdbc:mysql://127.0.0.1:3306/test
user=root
password=123456
driver=com.mysql.cj.jdbc.Driver
public void connect5() throws Exception{
Properties properties = new Properties();
properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
String url = properties.get("url").toString();
String user = properties.get("user").toString();
String password = properties.get("password").toString();
String driver = properties.get("driver").toString();
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
connection.close();
}
ResultSet
基本介绍
- 表示数据库结果集的数据表,通常由数据库查询语句生成
- ResultSet对象保持一个光标指向当前的数据行,最初光标指向第一行的前一行。(类似于迭代器)
- next 方法将光标移动到下一行,如果不存在下一行,则返回false。因此可以使用此方法配合while遍历结果集。
底层介绍
ResultSet 类里面有一个 rowData 对象,rowdata里面的rows对象就存放着结果。
PreparedStatament(预处理)
预处理好处分析
- 不再使用 + 拼接sql语句,减少语法错误。
- 有效的解决了Sql注入问题。
- 大大减少了编译次数,提高效率。
代码演示
public void preparedstatement() throws Exception{
Properties properties = new Properties();
properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
String url = properties.get("url").toString();
String user = properties.get("user").toString();
String password = properties.get("password").toString();
String driver = properties.get("driver").toString();
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
String sql = "select * from person where id = ? && password=?";
//拿到预处理接口
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//为 ? 赋值
// 第一个参数为?的位置,1:代表第一个?,以此类推.
//执行此方法时会解决Sql注入问题
preparedStatement.setString(1,"1");
preparedStatement.setString(2,"1");
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登陆失败");
}
resultSet.close();
preparedStatement.close();
connection.close();
}
批处理
引言
批处理就是一批一批的处理,而不是一个一个的处理。
当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这么做效率上很差!处理的方案是使用批处理,即一次向服务器发送多条SQL语句,然后由服务器一次性处理。
批处理只针对更新(增、删、改)语句,不可以用于查询。
我们首先要打开Mysql的批处理,添加参数:rewriteBatchedStatements=true,也就是把我们的URL变成以下这种形式:jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true
statement批处理
可以多次调用Statement类的addBatch(String sql)方法,把需要执行的所有SQL语句添加到一个“批”中,然后调用Statement类的executeBatch()方法来执行当前“批”中的语句。
- void addBatch(String sql):添加一条语句到“批”中;
- int[] executeBatch():执行“批”中所有语句。返回值表示每条语句所影响的行数据;
- void clearBatch():清空“批”中的所有语句;
for(int i = 0; i < 10; i++) {
String number = "S_10" + i;
String name = "stu" + i;
int age = 20 + i;
String gender = i % 2 == 0 ? "male" : "female";
String sql = "insert into stu values('" + number + "', '" + name + "', " + age + ", '" +
gender + "')";
stmt.addBatch(sql);
}
stmt.executeBatch();
preparedstatement批处理
PreparedStatement的批处理有所不同,因为每个PreparedStatement对象都绑定一条SQL模板。所以向PreparedStatement中添加的不是SQL语句,而是给“?”赋值。
con = JdbcUtils.getConnection();
String sql = "insert into stu values(?,?,?,?)";
pstmt = con.prepareStatement(sql);
for(int i = 0; i < 10; i++) {
pstmt.setString(1, "S_10" + i);
pstmt.setString(2, "stu" + i);
pstmt.setInt(3, 20 + i);
pstmt.setString(4, i % 2 == 0 ? "male" : "female");
pstmt.addBatch();
}
pstmt.executeBatch();
批处理源码分析
引言
- 在调用addBatch();方法后,Statement或PreparedStatement对象,会调用自身的addBatch()方法,
该方法会检查sql语句的语法。- 然后调用(ClintPreapredQuery)query对象的addBatch()方法,将sql语句放入query的batchedArgs
(ArrayList)数组。- 调用executeBatch();会统一处理batchedArgs里面的sql语句。
- 所以调用后需调用clearBatch(),清空query.batchedArgs数组。
源码
PrepareedStatement.addBatch:
public void addBatch() throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
QueryBindings queryBindings = ((PreparedQuery)this.query).getQueryBindings();
queryBindings.checkAllParametersSet();
this.query.addBatch(queryBindings.clone());
}
} catch (CJException var6) {
throw SQLExceptionsMapping.translateException(var6, this.getExceptionInterceptor());
}
}
this.query.addBatch:
public void addBatch(Object batch) {
if (this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
this.batchedArgs.add(batch);
}
Jdbc Utils(工具类,传统连接方法)开发
引言
问题引出:对于这样的三个类A,B,C。他们都需要获取mysql服务,但是对于JDBC而言,获取mysql服务
都有同样的三个步骤:
- 注册驱动(由于mysql 5.1.6版本后会自动注册,所以此步骤可去除,即严格意义上说只有两个步骤)
- 创建连接
- 释放资源
显而易见的是这会导致代码的冗余,增大了后期维护的成本。此种情况下,就使得Jdbc工具类的开发显得尤为重要。
实例代码
package Jdbc;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author WJ
* @date 2022/7/27 14:56
* @version 1.0
* 用于简化Jdbc操作的工具类
*/
public class JDBCUtils {
/*
定义四个相关变量,由于是工具类,所以相关结构设置成静态
*/
private static String user;
private static String password;
private static String url;
private static String driver;
//由静态代码块进行初始化操作
static {
try {
Properties properties = new Properties();
//加载配置文件
properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
//读取相关属性
url = properties.get("url").toString();
user = properties.get("user").toString();
password = properties.get("password").toString();
driver = properties.get("driver").toString();
} catch (FileNotFoundException e) {
//实际开发处理时
//选择将编译异常编程运行异常
//这时调用者可以捕获该异常,也可以选择默认处理,比较方便
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//获取连接
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
//选择将编译异常编程运行异常
throw new RuntimeException(e);
}
}
//资源关闭
public static void close(ResultSet set, Statement statement,Connection connection){
try {
if(set != null){
set.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
//选择将编译异常编程运行异常
throw new RuntimeException(e);
}
}
}
简单应用
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
// 执行sql语句
String sql = "select * from websites";
//创建sql语句发送以及拿到返回结果的类
statement = connection.createStatement();
//拿到返回结果
resultSet = statement.executeQuery(sql);
System.out.println("id\tname\turl\tage\tcountry");
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + "\t" +
resultSet.getString("name") + "\t" +
resultSet.getString("url") + "\t" +
resultSet.getString("age") + "\t" + resultSet.getString("country"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally{
//释放资源
JDBCUtils.close(resultSet,statement,connection);
}
Jdbc事务
基本介绍
- JDBC程序中当一个Connection对象创建时,默认是自动提交事务:每执行一个SQL语句,
如果执行成功,就会向数据库自动提交,而不能回滚。 - JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务。
- 调用Connection的setAutoCommit(flse);可以取消自动提交事务。
- 在所有的SQL语句执行成功后调用Commit();方法提交事务。
- 在某个操作失败或异常时调用rollback();方法可以回滚事务。
应用实例
问题引出:即存在两个人A,B。如果A要给B转账100块,那么在数据库里面的操作就是:
将A的钱减少100,B的钱增加100。显然这是两个操作,而且是两个必须都成功或都失败的操作,
如果两个操作只成功一个就会出现错误:即凭空多出或少了100块。显然这是不被允许的。
//事务演示
public void affairs(){
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);//开启事务
// 执行sql语句
String sql1 = "update affairs set count = count - ? where id = ?";
//创建sql语句发送以及拿到返回结果的类
statement = connection.prepareStatement(sql1);
statement.setString(1,"100");
statement.setInt(2,1);
statement.executeUpdate();
int i = 1/0;//产生异常
String sql2 = "update affairs set count = count + ? where id = ?";
statement.setString(1,"100");
statement.setInt(2,2);
statement.executeUpdate();
connection.commit();//提交事务
} catch (Exception e) {
System.out.println("发生了异常,事务回滚!!!");
//发生了异常,撤销执行的Sql语句
try {
//无参数则默认回滚到事务开启的地方
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
e.printStackTrace();
} finally{
//释放资源
JDBCUtils.close(resultSet,statement,connection);
}
}
Jdbc传统连接弊端分析
- 传统的Jdbc连接使用DriverManager来获取,每次向数据库建立连接的时候,
都要将Connection加载到内存中,再验证ip地址,用户名和密码(0.05s ~ 1s)。
需要数据库连接的时候,就像数据库请求一个连接,频繁的进行数据库连接操作会
占用大量的系统资源,容易造成服务器崩溃。 - 每一次使用完数据库都得断开,如果程序出现异常未断开连接,将导致数据库内存
泄露,最终导致重启数据库。 - 传统连接的方式,不能控制连接的数量,如果连接过多,也可能造成数据库内存泄露
并崩溃。
由此便引出了连接池技术
Jdbc连接池
基本介绍
- 预先在缓冲池里面放入一定数量的连接,当需要进行数据库连接时,"取"出一个连接,
使用完后"放"回去即可。- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的
连接,而不是重新创建一个。- 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被放入等待队列。
数据库连接池种类
- JDBC连接池使用javax.sql.DataSource来表示,DataSource知识一个接口,该接口通常
由第三方提供实现。 - C3P0 数据库连接池。速度相对较慢,稳定性不错(hibernate,spring)。
- DBCP 数据库连接池,速度相对C3P0较快,但不稳定。
- Proxool 数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点。
- BoneCP 数据库连接池,速度快。
- Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池。
C3P0连接池技术
经典,需掌握
- 毫无疑问,首先需要拿到对应的jar包(到官网下载),配置到项目依赖。
- 代码演示
使用方式1
*方式1(手动设置相关属性)
package Jdbc;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @author WJ
* @date 2022/7/28 12:04
* @version 1.0
* 演示数据库连接池技术的使用
*/
@SuppressWarnings("all")
public class ConnectPool_c3p0 {
//c3p0连接池技术演示
public static void main(String[] args) throws Exception{
//1 初始化相关信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\Jdbc\\mysql.properites"));
String url = properties.get("url").toString();
String user = properties.get("user").toString();
String password = properties.get("password").toString();
String driver = properties.get("driver").toString();
//2 生成连接池对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//设置相关属性
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接池里连接个数
comboPooledDataSource.setInitialPoolSize(10);
//设置最大连接个数
comboPooledDataSource.setMaxPoolSize(20);
//测试连接五十万次的时间
long start = System.currentTimeMillis();
for(int i=0;i<500000;i++) {
//从连接池里拿到一个连接
Connection connection = comboPooledDataSource.getConnection();
//释放资源
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0连接池耗时: "+(end-start));//c3p0连接池耗时: 3384
}
}
使用方式2
- 方式2(配置文件方式)
- 首先要创建一个xml(在src目录下),其名字一定是c3p0-config.xml(代码中自动寻找)
- 格式说明
<c3p0-config>
<!-- 配置名称 下面的代码会根据给定的名称寻找相关配置 -->
<named-config name="wj">
<!-- 连接参数 -->
<!-- 驱动类-->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<!-- 连接url -->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">123456</property>
<!-- 连接池参数 -->
<!--初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!-- 每次增长的连接数量 -->
<property name="acquireIncrement">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间(毫秒)-->
<property name="checkoutTimeout">3000</property>
<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>
<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
- 实例代码
//即配置文件里的<named-config>标签
ComboPooledDataSource wj = new ComboPooledDataSource("wj");
Connection connection = wj.getConnection();
System.out.println("ok");
connection.close();
Druid(德鲁伊连接池使用)
使用最广,性能最优,需掌握
- 导入jar包
- 创建配置文件.properties后缀,内容(格式)如下:
driverClassName=com.mysql.cj.jdbc.Driver
#URL连接数据库的URL,其中travel(以下面例子来说)为连接的数据库,后面的参数可不改但不删
url=jdbc:mysql://localhost:3306/test
characterEncoding=utf-8
#安装mysql时候设置的用户与密码
username=root
password=123456
#初始化物理连接的个数
initialSize=10
#最小连接数
minIdle=5
#最大连接池数量
maxActive=30
#获取连接时最大等待时间(毫秒)
maxWait=3000
#用来检测连接是否有效的sql
validationQuery=SELECT 1
#保证安全性!
testWhileIdle=true
- 示例代码
package Jdbc;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
public class ConnectPool_Druid{
public static void main(String[] args) throws Exception{
Properties properties = new Properties();
//读取配置文件
properties.load(new FileInputStream("src\\druid.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("德鲁伊连接池耗时: "+(end-start));//德鲁伊连接池耗时: 517
}
}
Jdbc Utils(德鲁伊连接池方式实现)
示例代码
package Jdbc;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
@SuppressWarnings("all")
public class JDBCUtilsByDruid {
private static DataSource dataSource;
static {
try {
Properties properties = new Properties();
//读取配置文件
properties.load(new FileInputStream("src\\druid.properties"));
//通过配置文件创造一个指定的连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//获取连接
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//释放资源
public static void close(ResultSet set, Statement statement, Connection connection){
try {
if(set != null){
set.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
//选择将编译异常编程运行异常
throw new RuntimeException(e);
}
}
}
Apache-DBUtils
问题引出
- ResultSet结果集与Connetion绑定了,即连接断开以后,结果集就不能用了;
- ResultSet不利于数据管理,体现在结果集只能遍历一次;
- ResultSet获取结果也不方便,体现在通过getString(“字段名”)来获取,不利于阅读和使用。
基本介绍
实例代码
数据库字段
- id
- name
- url
- age
- country
javaBean类
package Jdbc;
public class website {
private String id;
private String name;
private String url;
private String age;
private String country;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(id);
builder.append("\t");
builder.append(name);
builder.append("\t");
builder.append(url);
builder.append("\t");
builder.append(age);
builder.append("\t");
builder.append(country);
return builder.toString();
}
public website() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
演示查询代码
返回多行多列
package Jdbc;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class DBUtils {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
String sql = "select * from websites where id > ?";
//生成一个执行增删改查的对象
/**
* @return ArrayList 集合
* connection: 数据库连接
* sql: 执行的sql语句
* BeanListHandler :泛型类:用于创建保存数据的集合(反射机制)
* 后面是可变参数,用于给sql语句里面的 ? 赋值
*/
QueryRunner queryRunner = new QueryRunner();
//(1) query 方法就是执行sql语句,得到的resultset -> website 对象 -> ArrayList 对象
List<website> websites =
queryRunner.query(connection, sql, new BeanListHandler<>(website.class), 1);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
for(website w:websites){
System.out.println(w);
}
}
}
返回一行多列
// 演示返回一行结果
@Test
public void onerow() throws Exception{
Connection connection = JDBCUtilsByDruid.getConnection();
String sql = "select * from websites where id = ?";
QueryRunner queryRunner = new QueryRunner();
website w = queryRunner.query(connection, sql, new BeanHandler<>(website.class), 1);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
System.out.println(w);
}
返回一行一列
// 演示返回一行一列结果
@Test
public void onerowandcol() throws Exception{
Connection connection = JDBCUtilsByDruid.getConnection();
String sql = "select name from websites where id = ?";
QueryRunner queryRunner = new QueryRunner();
Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 1);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
System.out.println(obj);
}
演示DML(增删改查)代码
//测试增删改查
@Test
public void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
String sql = "update websites set url = ? where name = ?";
QueryRunner queryRunner = new QueryRunner();
//返回受影响的行数,update函数执行增删改操作
int update = queryRunner.update(connection, sql,"无法访问","Google");
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
if(update > 0){
System.out.println("ok");
}else{
System.out.println("error");
}
}
源码解析
- wrap()
protected ResultSet wrap(ResultSet rs) {
return rs;
}
- query()
public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
return this.query(conn, false, sql, rsh, (Object[])null);
}
private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
} else if (sql == null) {
if (closeConn) {
this.close(conn);
}
throw new SQLException("Null SQL statement");
} else if (rsh == null) {
if (closeConn) {
this.close(conn);
}
throw new SQLException("Null ResultSetHandler");
} else {
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);//通过反射机制,将每一行的值赋给一个实例的website对象,最后拼接成数组返回
} catch (SQLException var33) {
this.rethrow(var33, sql, params);
} finally {
try {
this.close(rs);//这里可以看到,底层已经关闭了结果集
} finally {
this.close(stmt);//也关闭了指令发送器
if (closeConn) {
this.close(conn);//在debug里面可以看到 closeConn默认是 false,即不关闭连接。
//且这是私有方法,即我们无法干涉,所以仍需手动关闭。
}
}
}
return result;
}
}
反射机制简单说明
下面的代码就是通过反射机制给一个实例化的对象赋值
package Jdbc;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Refelect {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
user u = new user();
Class c = user.class;
Method[] declaredMethods = c.getDeclaredMethods();//获取方法数组
for (Method m :declaredMethods){
//根据方法名做出相应动作:赋值
if(m.getName().equals("setId")){
m.invoke(u,"root");
}
if(m.getName().equals("setPassword")){
m.invoke(u,"123456");
}
}
System.out.println("id:"+u.id);
System.out.println("password:"+u.password);
}
}
class user{
String id;
String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
BasicDAO
引言
类似前面的Jdbc-Utils,对于数据库操作,存在大量的重复语句,除了获取连接和断开
连接外,基本的增删改查在函数里的不同只是sql语句的不同,以及返回类型的不同。
由此便引发的BasicDAO的开发,即将基本的操作封装在这个类里面,而对于不同的表则在
这个类的基础上开发其特有的子类,这样:一个子类就对应一张表的查询。
实例代码
BasicDAO
package dao_;
import Jdbc.JDBCUtilsByDruid;
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 queryRunner = new QueryRunner();
/**
*
* @param sql 执行的sql语句
* @param params 填充的参数
* @return 受影响的行数
*/
public int updateData(String sql,Object... params){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = queryRunner.update(connection, sql, params);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
*
* @param sql 执行的sql语句
* @param cls 返回的表对应的JavaBean 类
* @param params 填充的参数
* @return 查询到的数据(多行)
*/
public List<T> querryRows(String sql,Class<T>cls,Object... params){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new BeanListHandler<T>(cls),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
* @param sql 执行的sql语句
* @param params 填充的参数
* @return 返回查询到的数据(单行单列)
*/
public Object querryScalar(String sql,Object... params){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new ScalarHandler(),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
*
* @param sql 执行的sql语句
* @param cls 返回的表对应的JavaBean 类
* @param params 填充的参数
* @return 查询到的数据(单行)
*/
public T querrySingle(String sql,Class<T>cls,Object... params){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection,sql,new BeanHandler<T>(cls),params);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
websiteDao
package dao_;
import Jdbc.website;
public class websiteDao extends BasicDAO<website>{
//根据业务,编写特有的方法
}
website(JavaBean对象(Domain))
package Jdbc;
public class website {
private String id;
private String name;
private String url;
private String age;
private String country;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(id);
builder.append("\t");
builder.append(name);
builder.append("\t");
builder.append(url);
builder.append("\t");
builder.append(age);
builder.append("\t");
builder.append(country);
return builder.toString();
}
public website() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
测试代码
package dao_;
import Jdbc.website;
import java.util.List;
public class TestDao {
public static void main(String[] args) {
websiteDao websiteDao = new websiteDao();
String sql;
//多行查询
sql = "select * from websites";
List<website> websites = websiteDao.querryRows(sql, website.class);
System.out.println("=====多行查询=====");
for (website w:websites){
System.out.println(w);
}
System.out.println("=====单行单列查询=====");
//单行单列查询
sql = "select name from websites where id = ?";
Object o = websiteDao.querryScalar(sql, 3);
System.out.println(o);
System.out.println("=====单行查询=====");
//单行查询
sql = "select * from websites where id = ?";
website w = websiteDao.querrySingle(sql, website.class, 2);
System.out.println(w);
}
}
测试结果
=====多行查询=====1 Google 无法访问 1 USA
2 淘宝 https://www.taobao.com/ 13 CN
3 菜鸟教程 http://www.runoob.com 5892 CN
4 微博 http://weibo.com/ 20 CN
5 Facebook https://www.facebook.com/ 3 USA
=====单行单列查询=====
菜鸟教程
=====单行查询=====
2 淘宝 https://www.taobao.com/ 13 CN