package com.song.jdbc.myjdbc;
/**
* 我们规定的 jdbc 接口(方法)
*/
public interface JdbcInterface {
//连接
public Object getConnection();
//crud
public void crud();
//关闭连接
public void close();
}
package com.song.jdbc.myjdbc;
/**
* mysql数据库实现了jdbc接口
* 仅仅是模拟,实际的没那么简单
* 实际是mysql厂商开发
*/
public class MysqlJdbcImpl implements JdbcInterface{
@Override
public Object getConnection() {
System.out.println("得到 mysql 的连接");
return null;
}
@Override
public void crud() {
System.out.println("完成 mysql 的增删改查");
}
@Override
public void close() {
System.out.println("关闭 mysql 的连接");
}
}
package com.song.jdbc.myjdbc;
public class TestJDBC {
public static void main(String[] args) {
//完成对mysql的操作
JdbcInterface jdbcInterface = new MysqlJdbcImpl();
//通过接口来调用实现类[动态绑定]
jdbcInterface.getConnection();
jdbcInterface.crud();
jdbcInterface.close();
}
}
package com.song.jdbc.myjdbc;
/**
* 模拟 oracle 数据库实现 jdbc
*/
public class OracleJdbcImpl implements JdbcInterface{
@Override
public Object getConnection() {
System.out.println("得到对 Oracle 的连接");
return null;
}
@Override
public void crud() {
System.out.println("完成 Oracle 的增删改查");
}
@Override
public void close() {
System.out.println("关闭 Oracle 的连接");
}
}
package com.song.jdbc.myjdbc;
public class TestJDBC {
public static void main(String[] args) {
//完成对mysql的操作
JdbcInterface jdbcInterface = new MysqlJdbcImpl();
//通过接口来调用实现类[动态绑定]
jdbcInterface.getConnection();
jdbcInterface.crud();
jdbcInterface.close();
System.out.println("=====================");
//完成对Oracle的操作
//只需要改一下 对象即可,比较方便
jdbcInterface = new OracleJdbcImpl();
//通过接口来调用实现类[动态绑定]
jdbcInterface.getConnection();
jdbcInterface.crud();
jdbcInterface.close();
}
}
JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQI语句,并到得到返回结果等各类操作, 相关类和接口在 java.sql与javax.sql包中
通过jidbc 对 表 actor 进行 添加,删除和修改操作
先在SQLyog中创建一个演员表
use sys_db02;
create table actor( -- 演员表
id int primary key auto_increment,-- 主键自增长
name varchar(32) not null default'',
sex char(1) not null default '女',
borndate datetime,-- 生日
phone varchar(12));
//前置工作: 1.在项目下创建一个文件夹(Directory)比如 libs
// 2.将 mysql.jar 拷贝到该目录下,
// 3.右击mysql.jar文件,点击 add to project ..加入到项目中
//1.注册驱动
Driver driver = new Driver();//创建一个Driver对象
//2.得到连接
//解读
//1.jdbc:mysql:// 这段是规定好的,通过jdbc的连接方式是mysql
//2.localhost的位置可以是ip地址,我这边正好在主机上操作
//3.3306表示我们mysql监听的端口
//4.sys_db02 表示连接到mysql的哪个数据库
//mysql的连接本质就是前面学过的socket连接
String url = "jdbc:mysql://localhost:3306/sys_db02";
//将用户名和密码放入到 Properties 对象
Properties properties = new Properties();
//说明
//这里的 user 和 password 是规定好的,
//后面的值根据实际情况填写,比如 root 和 sys
properties.setProperty("user","root");//用户
properties.setProperty("password","sys");//密码
//尝试给数据库连接到指定的 url
Connection connect = driver.connect(url, properties);
//3.执行 sql
//String sql = "insert into actor values(null,'周杰伦','男','1979-01-18','110')";
//String sql = "insert into actor values(null,'林俊杰','男','1981-03-27','120')";
String sql = "update actor set phone = '119' where name = '林俊杰'";
//statement 用于执行静态SQL语句并返回其生成的结果的对象
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql);//如果是 dml语句,返回的就是影响行数
//提示
System.out.println(rows>0?"成功":"失败");
//4.关闭连接
statement.close();
connect.close();
// 获取Driver实现类对象
Driver driver = new com.mysql.jdbc.Driver();
String url = "jdbc:mysql://localhost:3306/jdbc_db";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","sys");
Connection conn = driver.connect(url, info);
System.out.println(conn);
package com.song.jdbc;
import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 分析java连接mysql的5种方式
*/
public class JdbcConn {
//方式一
@Test
public void connect01() throws SQLException {
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/sys_db02";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "sys");
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
}
//方式1 会直接使用 com.mysql.jdbc.Driver(),属于静态加载,灵活性差,依赖强
//---于是我们推出---> 方式2
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
String url = "jdbc:mysql://localhost:3306/jdbc_db";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password", "abc123");
Connection conn = driver.connect(url, info);
System.out.println(conn);
package com.song.jdbc;
import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 分析java连接mysql的5种方式
*/
public class JdbcConn {
//方式二
@Test
public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载类
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/sys_db02";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "sys");
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
}
// 使用DriverManager替换Driver
Class clazz = Class.forName("com.mysqljdbc.Driver")
Driver driver = (Driver) clazz.newlnstance();
String url = "jdbc:mysql://localhost:3306/jdbc_db";
String user = "root";
String password = "sys";
DriverManager.registerDriver(driver);
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
package com.song.jdbc;
import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 分析java连接mysql的5种方式
*/
public class JdbcConn {
//方式三
@Test
public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/sys_db02";
String user = "root";
String password = "sys";
DriverManager.registerDriver(driver);//注册驱动
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("第三种方式:" + connection);
}
}
// 使用 Class.forName 自动完成注册驱动,简化代码 => 分析源码
Class.forName("com.mysal.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc_db";
String user = "root";
String password = "hsp"
Connection conn = DriverManager.getConnection(url, user, password);.
System.out.println(conn);
package com.song.jdbc;
import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 分析java连接mysql的5种方式
*/
public class JdbcConn {
//方式四:使用Class.forName自动完成注册驱动,简化代码
@Test
public void connect04() throws ClassNotFoundException, SQLException {
//使用反射加载Driver类
//在加载Driver类时,完成注册
/*
源码:com.mysql.jdbc.Driver
1.静态代码块,在类加载时,会执行一次
2.DriverManager.registerDriver(new Driver());
3.因此注册driver的工作已经完成
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
*/
//代码Class> aClass = Class.forName("com.mysql.jdbc.Driver");不写也可以
//原因:
// 1.mysqL驱动5.1.6可以无需CLass,forName(“com.mysql.jdbc.Driver”);
// 2.从idk1.5以后使用了jdbc4,不再需要显示调用class.forName()注册驱动而是
// 自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册
// 3.不过建议还是写上 CLassforName(“com.mysql.jdbc.Driver”),更加明确
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/sys_db02";
String user = "root";
String password = "sys";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("第四种方式:" + connection);
}
}
//使用配置文件,连接数据库更灵活
1.Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root""root”);
//中的字符串 各个值,比如端口,数据库,用户名,密码为了方便,我们可以将信息写入到.properties 文件中,方便操作
2.jdbc.properties
user=root
password=sys
url=jdbc:mysql://localhost:3306/girls
driver=com.mysql.jdbc.Driver
案例演示:
user=root
password=sys
url=jdbc:mysql://localhost:3306/sys_db02
driver=com.mysql.jdbc.Driver
package com.song.jdbc;
import com.mysql.jdbc.Driver;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 分析java连接mysql的5种方式
*/
public class JdbcConn {
//第五种方式
@Test
public void connect05() throws IOException, ClassNotFoundException, SQLException {
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("第五种方式:"+connection);
}
}
使用方式5完成下面数据库的连接
思路分析:
-- 1. 创建 news 表
CREATE TABLE news(
id INT PRIMARY KEY AUTO_INCREMENT,-- 主键自增长
content VARCHAR(64)NOT NULL DEFAULT'')
-- 查询
SELECT * FROM news;
user=root
password=sys
url=jdbc:mysql://localhost:3306/sys_db02
driver=com.mysql.jdbc.Driver
package com.song.jdbc;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
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 JdbcExercise {
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
//使用方式5完成下面数据库的连接
//1. 创建 news 表
//在MySQL中创建一张news表
/**
* CREATE TABLE news(
* id INT,
* content VARCHAR(64)NOT NULL DEFAULT'')
*/
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//2. 使用 jdbc 添加 5条数据
//添加 sql 数据
String sql1 = "insert into news values(null,'上海新闻')";
String sql2 = "insert into news values(null,'北京新闻')";
String sql3 = "insert into news values(null,'深圳新闻')";
String sql4 = "insert into news values(null,'广州新闻')";
String sql5 = "insert into news values(null,'江苏新闻')";
//statement 用于执行静态SQL语句并返回其生成的结果的对象
Statement statement = connection.createStatement();
int i1 = statement.executeUpdate(sql1);
int i2 = statement.executeUpdate(sql2);
int i3 = statement.executeUpdate(sql3);
int i4 = statement.executeUpdate(sql4);
int i5 = statement.executeUpdate(sql5);
//添加提示信息
System.out.println(i1>0?"i1执行成功":"i1执行失败");
System.out.println(i2>0?"i2执行成功":"i2执行失败");
System.out.println(i3>0?"i3执行成功":"i3执行失败");
System.out.println(i4>0?"i4执行成功":"i4执行失败");
System.out.println(i5>0?"i5执行成功":"i5执行失败");
//3. 修改 id = 1的记录,将content 改成 一个新的消息
String sql6 = "update news set content = 'sys学java' where id = 1";
int i6 = statement.executeUpdate(sql6);
System.out.println(i6>0?"i6执行成功":"i6执行失败");
//4. 删除id = 3 的记录
String sql7 = "delete from news where id = 3";
int i7 = statement.executeUpdate(sql7);
System.out.println(i7>0?"i7执行成功":"i7执行失败");
}
}
执行idea显示
接下来打开mysql中的news表,看到已经有相应的数据了
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
ResultSet对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前
next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,
因此可以在while循环中使用循环来遍历结果集
取出前面创建的演员表里面的数据
package com.song.jdbc.resultset_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* 演示select语句返回Resultset,并取出结果
*/
public class Resultset_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个statement
Statement statement = connection.createStatement();
//4. 组织一个sql
String sql = "select id,name,sex,borndate from actor";
//执行给定的SQL语句,该语句返回单个 Resultset 对象
/*前面创建的actor表
mysql> select id,name,sex,borndate from actor;
+----+-----------+-----+---------------------+
| id | name | sex | borndate |
+----+-----------+-----+---------------------+
| 1 | 周杰伦 | 男 | 1979-01-18 00:00:00 |
| 2 | 林俊杰 | 男 | 1981-03-27 00:00:00 |
+----+-----------+-----+---------------------+
*/
ResultSet resultSet = statement.executeQuery(sql);
/*进行源码阅读
具体看示意图
*/
//5. 使用while循环取出数据
// resultSet.next() 让光标向后移动,如果没有更多行,则返回 false
while (resultSet.next()){
//获取该行的第 1 列
//通过列名来获取值, 推荐
int id = resultSet.getInt(1);
//获取该行的第 2 列
String name = resultSet.getString(2);
//获取该行的第 3 列
String sex = resultSet.getString(3);
//获取该行的第 4 列
Date borndate = resultSet.getDate(4);
//输出相应数据
System.out.println(id+"\t"+name+"\t"+sex+"\t"+borndate);
}
//6. 关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
代码运行后结果显示:
resultset源码阅读的示意图
Statement对象 用于执行静态SQL语句并返回其生成的结果的对象
在连接建立后,需要对数据库进行访问,执行 命名或是SQL 语句,可以通过
Statement对象执行SQL 语句,存在SQL注入风险
SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输数据中注入非法的 SQL 语句段或命令,恶意攻击数据库。
//创建表
CREATE TABLE admin (-- 管理员表
`name` VARCHAR(32) NOT NULL UNIQUE,
pwd VARCHAR(32) NOT NULL DEFAULT'')
CHARACTER SET utf8
-- 添加一个数据
INSERT INTO admin VALUES('tom', '123');
-- 我们正常查询某条表格信息
SELECT *
FROM admin
WHERE `name` = 'tom' AND pwd = '123';
-- SQL注入
-- 用户名 1'or
-- 万能密码 or'1'='1
SELECT *
FROM admin
WHERE `name` = '1'OR' AND pwd = 'OR'1'='1'
-- 这样where后面的语句最后一条'1' = '1'恒成立
package com.song.jdbc.statement_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
/**
* 演示statement的注入问题
*/
public class Stetement {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
//如果希望看到SQL注入,这里需要用nextline
//因为next输入空格或 ' 就表示结束了
System.out.println("请输入管理员的名字:");
String admin_name = scanner.nextLine();
System.out.println("请输入管理员的密码");
String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个statement
Statement statement = connection.createStatement();
//4. 组织一个sql
String sql = "select name,pwd from admin where name='"+
admin_name+"' and pwd ='"+admin_pwd+"'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()){
System.out.println("恭喜,登陆成功");
}else {
System.out.println("对不起,登陆失败");
}
//关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
代码执行过后,我们先输入正确的用户名tom和密码123,成功登陆
但是当我们输入用户名 1’or 和万能密码 or’1’='1,发现也成功登陆了
package com.song.jdbc.preparedstatemenet_;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;
/**
* 演示preparedstatement的select使用
*/
public class PreparedStatement_ {
public static void main(String[] args) throws Exception {
//看一下PreparedStatement类图
Scanner scanner = new Scanner(System.in);
System.out.println("请输入管理员名字");
String admin_name = scanner.nextLine();
System.out.println("请输入管理员密码");
String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个PreparedStatement
// 3.1 组织sql语句 , sql语句中的 ? 相当于占位符
String sql = "select name,pwd from admin where name = ? and pwd = ?";
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//4. 执行select语句 使用 executeQuery()
// 如果执行的是 dml(update,insert,delete) 使用 execyteUpdate()
// 这里执行 executeQuery() 语句不用再填写 sql
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){//如果查询到一条记录,则说明该管理存在
System.out.println("恭喜,登陆成功");
}else {
System.out.println("对不起,登陆失败");
}
//关闭连接
resultSet.close();
preparedStatement.close();
connection.close();
}
}
执行代码后,先输入正确的用户名和密码,发现可以正常登录
然后进行SQL注入,发现万能密码不管用了,大大提高了安全性
package com.song.jdbc.preparedstatemenet_;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.Scanner;
/**
* 演示preparedstatement的dml使用
*/
public class PreparedStatementDML {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入管理员名字");
String admin_name = scanner.nextLine();
System.out.println("请输入管理员密码");
String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个PreparedStatement
// 3.1 组织sql语句 , sql语句中的 ? 相当于占位符
String sql = "insert into admin values(?,?) ";
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//4. 执行select语句 使用 executeQuery()
// 如果执行的是 dml(update,insert,delete) 使用 execyteUpdate()
// 这里执行 executeQuery() 语句不用再填写 sql
int rows = preparedStatement.executeUpdate();
//显示提示
System.out.println(rows>0?"执行成功":"执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
执行代码
查看mysql中的admin表格
package com.song.jdbc.preparedstatemenet_;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.Scanner;
/**
* 演示preparedstatement的dml使用
*/
public class PreparedStatementDML {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入管理员名字");
String admin_name = scanner.nextLine();
System.out.println("请输入管理员新密码");
String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个PreparedStatement
// 3.1 组织sql语句 , sql语句中的 ? 相当于占位符
//String sql = "insert into admin values(?,?) ";
String sql = "update admin set pwd = ? where name = ? ";
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1,admin_pwd);
preparedStatement.setString(2,admin_name);
//4. 执行select语句 使用 executeQuery()
// 如果执行的是 dml(update,insert,delete) 使用 execyteUpdate()
// 这里执行 executeQuery() 语句不用再填写 sql
int rows = preparedStatement.executeUpdate();
//显示提示
System.out.println(rows>0?"执行成功":"执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
执行代码
查看mysql中的admin表格
package com.song.jdbc.preparedstatemenet_;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.Scanner;
/**
* 演示preparedstatement的dml使用
*/
public class PreparedStatementDML {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要删除的管理员名字");
String admin_name = scanner.nextLine();
//System.out.println("请输入管理员新密码");
//String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个PreparedStatement
// 3.1 组织sql语句 , sql语句中的 ? 相当于占位符
//String sql = "insert into admin values(?,?) ";
//String sql = "update admin set pwd = ? where name = ? ";
String sql = "delete from admin where name = ? ";
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1,admin_name);
//preparedStatement.setString(2,admin_name);
//4. 执行select语句 使用 executeQuery()
// 如果执行的是 dml(update,insert,delete) 使用 execyteUpdate()
// 这里执行 executeQuery() 语句不用再填写 sql
int rows = preparedStatement.executeUpdate();
//显示提示
System.out.println(rows>0?"执行成功":"执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
执行代码
查看mysql中的admin表格
参考上面的代码
代码如下:
表格就用上面创建好的admin表格了
然后进行dml操作
package com.song.jdbc.preparedstatemenet_;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Properties;
import java.util.Scanner;
/**
* 1. 创建 admin表
* 2. 使用PreparedStatement 添加 5条数据
* 3. 修改 tom 的记录,将name 改成 king
* 4. 删除 一条 的记录
* 5. 查询全部记录,并显示在控制台
*/
public class PreparedStatementExercise {
public static void main(String[] args) throws Exception {
// 1. 创建 admin表
//就用已经创建好的admin表
/*
mysql> select * from admin;
+------+-----+
| name | pwd |
+------+-----+
| tom | 123 |
+------+-----+
1 row in set (0.00 sec)
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要删除的名字");
String admin_name = scanner.nextLine();
//System.out.println("请输入新的名字");
//String admin_name1 = scanner.nextLine();
//System.out.println("请输入要修改的名字");
//String admin_name2 = scanner.nextLine();
//System.out.println("请输入管理员密码");
//String admin_pwd = scanner.nextLine();
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//1. 注册驱动,建立一个网络连接,建议写上
Class.forName(driver);
//2. 尝试给数据库连接到指定的 url
Connection connection = DriverManager.getConnection(url, user, password);
//3. 得到一个PreparedStatement
// 3.1 组织sql语句 , sql语句中的 ? 相当于占位符
//2. 使用PreparedStatement 添加 5条数据
//String sql = "insert into admin values(?,?) ";
//3. 修改 tom 的记录,将name 改成 king
//String sql = "update admin set name = ? where name = ? ";
//4. 删除 一条 的记录
String sql = "delete from admin where name = ? ";
//5. 查询全部记录,并显示在控制台
/*
mysql> select * from admin;
+------+-----+
| name | pwd |
+------+-----+
| jack | 222 |
| king | 123 |
| lily | 666 |
| lucy | 333 |
| sys | abc |
+------+-----+
5 rows in set (0.00 sec)
*/
// 3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 3.3 给 ? 赋值
preparedStatement.setString(1,admin_name);
//preparedStatement.setString(1,admin_name1);
//preparedStatement.setString(2,admin_name2);
//4. 执行select语句 使用 executeQuery()
// 如果执行的是 dml(update,insert,delete) 使用 execyteUpdate()
// 这里执行 executeQuery() 语句不用再填写 sql
int rows = preparedStatement.executeUpdate();
//显示提示
System.out.println(rows>0?"执行成功":"执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
在jdbc 操作中,获取连接和 释放资源 是经常使用到,可以将其封装
JDBC连接的工具类JDBCUtils
package com.song.jdbc.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* 这是一个工具类,完成mysql的连接和关闭资源
*/
public class JDBCUtils {
//定义相关的属性(4个),因为只需要一份,
// 因此我们做成静态的static
private static String user;
private static String password;
private static String url;
private static String driver;
//在static代码块去初始化
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//读取相关的属性值
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (IOException e) {
//在实际开发中,我们可以这样处理
//1. 编译异常转成运行异常
//2.调用者可以选择捕获该异常,也可以选择默认处理,比较方便
throw new RuntimeException(e);
}
}
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
//1. 编译异常转成运行异常
//2.调用者可以选择捕获该异常,也可以选择默认处理,比较方便
throw new RuntimeException(e);
}
}
//关闭资源
/*可能会需要关闭的
1.Resultset 结果集
2.Statement 或者 PreparedStatement
3.Connection
4.如果需要关闭资源,就传入对象,否则传入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);
}
}
}
使用上面创建的工具类进行增删改查
这里就只弄一个select和insert演示
package com.song.jdbc.utils;
import org.junit.jupiter.api.Test;
import java.sql.*;
/**
* 该类演示如何使用JDBCUtils工具类,完成dml和select
*/
public class JDBCUtils_Use {
//查询 select
@Test
public void testSelect(){
//提取出来初始化,扩大作用范围
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql = "select * from actor";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1.得到连接
connection = JDBCUtils.getConnection();
//3.创建PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
//执行
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date borndate = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
System.out.println(id+"\t"+name+"\t"+sex+"\t"+borndate+"\t"+phone);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(resultSet,preparedStatement,connection);
}
}
//插入 insert
@Test
public void testDML(){//insert,update,delete
//提取出来初始化,扩大作用范围
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql = "insert into actor values(?,?,?,?,?)";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
try {
//1.得到连接
connection = JDBCUtils.getConnection();
//3.创建PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
//给占位符赋值
preparedStatement.setInt(1,3);
preparedStatement.setString(2,"王心凌");
preparedStatement.setString(3,"女");
preparedStatement.setDate(4, Date.valueOf("1982-09-05"));
preparedStatement.setString(5,"12566");
//执行
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(null,preparedStatement,connection);
}
}
}
模拟经典的转账业务
-- 创建表格,用于测试java中的事物
CREATE TABLE `account2`(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL DEFAULT '',
balance DOUBLE NOT NULL DEFAULT 0) CHARACTER SET utf8;
INSERT INTO `account2` VALUES(NULL,'马云', 3000);
INSERT INTO `account2` VALUES(NULL,'马化腾',10000);
SELECT * FROM `account2`
//没有使用事务.
@Test
public void noTransaction() {
//操作转账业务
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql1 = "update account2 set balance = balance - 100 where id = 1";
String sql2 = "update account2 set balance = balance + 100 where id = 2";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
try {
//在默认情况下,connection是默认自动提交的
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql1);
//执行
preparedStatement.executeUpdate();
int i = 1 / 0;//抛出异常
preparedStatement = connection.prepareStatement(sql2);
//执行
preparedStatement.executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
//使用事物来解决
@Test
public void transaction() {
//操作转账业务
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql1 = "update account2 set balance = balance - 100 where id = 1";
String sql2 = "update account2 set balance = balance + 100 where id = 2";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
try {
//在默认情况下,connection是默认自动提交的
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) {
//这里我们可以进行回滚,即撤销执行的 SQL
//默认回滚到事务开始的状态.
System.out.println("执行发生了异常,撤销执行的 sql");
try {
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
演示向admin2表中添加5000条数据,看看使用批处理耗时多久
注意:需要修改 配置文件jdbc.properties
代码演示:
-- 创建表格
CREATE TABLE admin2(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) NOT NULL ,
PASSWORD VARCHAR(32) NOT NULL)
-- 检查表格数据
SELECT * FROM admin2;
-- 统计表格行数
SELECT COUNT(*) FROM admin2;
//传统方法,添加5000条数据到admin2
@Test
public void noBatch() throws Exception {
//1.连接数据
Connection connection = JDBCUtils.getConnection();
//2.sql语句
String sql = "insert into admin2 values(null,?,?)";
//3.preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
//开始时间
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jack"+i);
preparedStatement.setString(2,"666");
preparedStatement.executeUpdate();
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("传统方法消耗时间:"+(end-start));
//输出 : 传统方法消耗时间:265847
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
//批处理,添加5000条数据到admin2
@Test
public void batch() throws Exception {
//1.连接数据
Connection connection = JDBCUtils.getConnection();
//2.sql语句
String sql = "insert into admin2 values(null,?,?)";
//3.preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
//开始时间
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jack"+i);
preparedStatement.setString(2,"666");
//添加需要批量处理的SQL语句或参数
preparedStatement.addBatch();
if (i+1%1000==0){//每1000条批处理
//执行批量处理语句
preparedStatement.executeBatch();
//清空批处理包的语句
preparedStatement.clearBatch();
}
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("批处理方法消耗时间:"+(end-start));
//输出 : 批处理方法消耗时间:13
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
url=jdbc:mysql://localhost:3306/sys_db02?rewriteBatchedStatements=true
preparedStatement.addBatch();
//将SQL语句加入到批处理包中 --> 源码
/*
//1. 第一就创建 ArrayList - elementData => Object[]
//2. elementData => Object[] 就会存放我们预处理的 sql 语句
//3. 当 elementData 满后,就按照 1.5 扩容
//4. 当添加到指定的值后,就 executeBatch
//5. 批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高
public void addBatch() throws SQLException {
if (this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
this.batchedArgs.add(new BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
*/
发现我们在代码运行的过程中就是在一个名字为batchedArgs的ArrayList的集合中不停地放数据,放在elementData的一个对象数组里面,一开始有十个位置,当放满以后按照1.5倍扩容
编写程序完成连接MySQL5000次的操作
看看有什么问题,耗时又是多久.=> 数据库连接池
当我们不关闭连接的时候,会发现系统会报异常 Too many connections
我们现在关闭连接,查看连接mysql 5000次耗费多少时间
package com.song.jdbc.datasource;
import com.song.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
public class ConQuestion {
//代码 连接mysql 5000次
@Test
public void testConnect(){
//看看连接-关闭 connection 会耗用多久
long start = System.currentTimeMillis();
System.out.println("正在连接中...");
//连接数据库5000次
for (int i = 0; i < 5000; i++) {
//使用传统的 jdbc 方式,得到连接
Connection connection = JDBCUtils.getConnection();
//sql
//处理一些事,比如 preparedStatement
//关闭连接
JDBCUtils.close(null,null,connection);
}
long end = System.currentTimeMillis();
System.out.println("连接5000次mysql 耗时= "+(end-start));
//连接5000次mysql 耗时= 14938
}
}
使用代码实现c3p0数据库连接池,配置文件放src目录下 C3P0 .java
然后我们同样使用c3p0连接mysql 5000次查看效率
//方式1:相关参数在程序里面指定,user,url,password等
@Test
public void testC3P0_01() throws Exception {
//1.创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2.通过配置文件mysql.properties获取相关连接的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
//读取相关的属性值
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
//3.给数据源 comboPooledDataSource 设置相关参数
//注意:连接管理是由 comboPooledDataSource 来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接
comboPooledDataSource.setInitialPoolSize(10);
//最大连接数
comboPooledDataSource.setMaxPoolSize(50);
//测试连接池的效率,测试对mysql 5000次的操作
//开始时间
long start = System.currentTimeMillis();
System.out.println("开始连接...");
for (int i = 0; i < 5000; i++) {
//这个方法就是从 DataSource 接口实现的
Connection connection = comboPooledDataSource.getConnection();
//System.out.println("连接成功...");
connection.close();
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("c3p0连接5000次 耗费时间 = "+(end-start));
//c3p0连接5000次 耗费时间 = 725
}
使用配置文件模板来完成
//方式二:使用配置文件模板来完成
//1.将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下
//2.该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("sys_java");
//测试5000次连接mysql
System.out.println("开始执行...");
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
//System.out.println("连接ok");
//关闭连接
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0第二种方式 耗时 = "+(end-start));
//c3p0第二种方式 耗时 = 540
}
由此可见,使用数据库连接池效率提升特别大。
package com.song.jdbc.datasource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties;
/**
* 测试 Druid 的使用
*/
public class Druid_ {
@Test
public void testDruid() throws Exception {
//1.加入 Druid jar包
//2.加入 配置文件 druid.properties ,将文件拷贝到项目的src目录下
//3.创建 properties 对象,读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
//4.创建一个指定参数的数据库连接池,Druid 连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//测试使用 Druid 连接五千次
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = dataSource.getConnection();
//System.out.println("连接成功!");
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("使用Druid 耗时 = "+(end-start));
//使用Druid 耗时 = 590
}
}
以上数据看来好像c3p0和Druid没多大区别
但是当我们操作的数据量变大的时候,就会看出差距
我们把连接5000次改为5000000次可以看到
c3p0耗时14497ms;
Druid(德鲁伊)耗时1630ms;
由此可见Druid(德鲁伊)连接池的效率特别高
//方式二:使用配置文件模板来完成
//1.将c3p0 提供的 c3p0.config.xml 拷贝到 src目录下
//2.该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("sys_java");
//测试5000次连接mysql
System.out.println("开始执行...");
long start = System.currentTimeMillis();
for (int i = 0; i < 5000000; i++) {
Connection connection = comboPooledDataSource.getConnection();
//System.out.println("连接ok");
//关闭连接
connection.close();
}
long end = System.currentTimeMillis();
//System.out.println("c3p0第二种方式 耗时 = "+(end-start));
//c3p0第二种方式 耗时 = 540
System.out.println("c3p0第二种方式(5000000) 耗时 = "+(end-start));
//c3p0第二种方式(5000000) 耗时 = 14497
}
/**
* 测试 Druid 的使用
*/
public class Druid_ {
@Test
public void testDruid() throws Exception {
//1.加入 Druid jar包
//2.加入 配置文件 druid.properties ,将文件拷贝到项目的src目录下
//3.创建 properties 对象,读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
//4.创建一个指定参数的数据库连接池,Druid 连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//测试使用 Druid 连接五千次
long start = System.currentTimeMillis();
for (int i = 0; i < 5000000; i++) {
Connection connection = dataSource.getConnection();
//System.out.println("连接成功!");
connection.close();
}
long end = System.currentTimeMillis();
//System.out.println("使用Druid 耗时 = "+(end-start));
//使用Druid 耗时 = 590
System.out.println("使用Druid(5000000) 耗时 = "+(end-start));
//使用Druid(5000000) 耗时 = 1630
}
}
我们基于原先的JDBC数据库,把他改为Druid(德鲁伊)实现
package com.song.jdbc.datasource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 基于Druid数据库连接池的工具类
*/
public class JDBCUtilsByDruid {
private static DataSource ds;
//用静态代码块完成 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);
}
}
//编写getConnection方法
public static Connection getConncteion() throws SQLException {
return ds.getConnection();
}
//关闭连接
//注意:这里的关闭连接,在数据库连接池技术中,不是真的断掉连接
//而是把使用的Connection对象放回连接池
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);
}
}
}
这里我们使用一下Druid(德鲁伊)实现的工具类JDBCUtilsByDruid
package com.song.jdbc.datasource;
import com.song.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.*;
/**
* 该类演示如何使用JDBCUtilsByDruid工具类
*/
public class JDBCUtilsByDruid_Use {
//查询 select
@Test
public void testSelect(){
//提取出来初始化,扩大作用范围
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql = "select * from actor";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1.得到连接
connection = JDBCUtilsByDruid.getConncteion();
//查看一下运行类型,可以看到运行类型是DruidPooledConnection
//com.alibaba.druid.pool.DruidPooledConnection
System.out.println(connection.getClass());
//3.创建PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
//执行
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date borndate = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
System.out.println(id+"\t"+name+"\t"+sex+"\t"+borndate+"\t"+phone);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(resultSet,preparedStatement,connection);
}
}
}
我们先分析一个问题:
//使用土方法来解决 ResultSet =封装=> Arraylist
//这里把返回值写成 ArrayList,
// 可以让其他对象直接调用这个方法
@Test
//public ArrayList testSelectToArraylist(){
public void testSelectToArraylist(){
//提取出来初始化,扩大作用范围
//1.得到连接
Connection connection = null;
//2.组织一个sql
String sql = "select * from actor where id >= 1";
//3.创建PreparedStatement 对象
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
//创建一个ArrayList对象,存放actor对象
ArrayList<Actor> list = new ArrayList<Actor>();
try {
//1.得到连接
connection = JDBCUtilsByDruid.getConncteion();
//查看一下运行类型,可以看到运行类型是DruidPooledConnection
//com.alibaba.druid.pool.DruidPooledConnection
System.out.println(connection.getClass());
//3.创建PreparedStatement 对象
preparedStatement = connection.prepareStatement(sql);
//执行
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date borndate = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
//把得到的 Resultset 的记录,封装到 Actor 对象,放入到 List集合
list.add(new Actor(id,name,sex,borndate,phone));
}
//System.out.println("List集合数据 = "+list);
//现在我们可以直接从ArrayList集合中取出我们想要的数据
//增强for取出数据
for (Actor actor:list){
System.out.println("id ="+actor.getId()+"\t"+actor.getName());
//id =1 周杰伦
//id =2 林俊杰
//id =3 王心凌
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(resultSet,preparedStatement,connection);
}
//因为 ArrayList 和 connection 没有任何关联,所以该集合可以复用
//return list;
}
ArrayHandler: 把结果集中的第一行数据转成对象数组。
ArrayListHandler: 把结果集中的每一行数据都转成一个数组,再存放到List中
BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler: 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里.
ColumnListHandler: 将结果集中某一列的数据存放到List中。
KevedHandler(name): 将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key
MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
MapListHandler: 将结果集中的每一行数据都封装到一个Map里,然后再存放到List
使用DBUtils+数据连接池(德鲁伊)方式,完成对表actor的crud
//使用 apache-DBUtils 工具类 + druid 完成对表的 crud 操作
@Test
public void testQueryMany() throws SQLException {//返回的是多行的情况
//1.得到连接
Connection conncteion = JDBCUtilsByDruid.getConncteion();
//2.使用DBUTils类和接口,
// 2.1 先引入DBUtils相关的jar,加入到本Project
//3.创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4.然后执行相关的方法,返回ArrayList结果集
//String sql = "select * from actor where id >= ?";
// 注意: sql 语句也可以查询部分列
String sql = "select id,name from actor where id >= ?";
/*
下面这行代码解读
(1) query 方法就是执行 sql 语句,得到 resultset ---封装到 --> ArrayList 集合中
(2) 返回集合
(3) connection:连接
(4) sql:执行SQL语句
(5) new BeanListHandler<>(Actor.class): 在将 resultset -> Actor 对象 -> 封装到 ArrayList
底层使用反射机制 去获取 Actor 类的属性,然后进行封装
(6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数 Object... params
(7) 底层得到的 resultset ,会在 query 关闭, 关闭 PreparedStatment
*/
List<Actor> list =
queryRunner.query(conncteion, sql, new BeanListHandler<>(Actor.class), 1);
/**
* 分析 queryRunner.query 方法:
* public T query(Connection conn, String sql, ResultSetHandler rsh, Object... params) throws SQLException {
* PreparedStatement stmt = null;//定义 PreparedStatement
* ResultSet rs = null;//接收返回一个 ResultSet 结果集
* T result = null;//返回 ArrayList
*
* try {
* stmt = this.prepareStatement(conn, sql);//创建 PreparedStatement
* this.fillStatement(stmt, params);//将 sql 中的 ? 进行赋值
* rs = this.wrap(stmt.executeQuery());//执行sql,返回 Resultset
* result = rsh.handle(rs);//返回 resultset ---> arrayList
* } catch (SQLException var33) {
* this.rethrow(var33, sql, params);
* } finally {
* try {
* this.close(rs);//关闭 resultset对象
* } finally {
* this.close((Statement)stmt);//关闭 statement对象
* }
* }
*
* return result;
* }
*/
System.out.println("输出集合的信息");
for (Actor actor:list){
System.out.println(actor);
}
//释放资源
//上面的queryrunner中已经自动关闭了Resultset和statement
//所以这里只需要关闭connection就可以了
JDBCUtilsByDruid.close(null,null,conncteion);
}
//演示apache-dbutils + druid 完成 返回的结果是单行记录(单个对象)
@Test
public void testQuerySingle() throws SQLException {
//1.得到连接
Connection conncteion = JDBCUtilsByDruid.getConncteion();
//2.使用DBUTils类和接口,
// 2.1 先引入DBUtils相关的jar,加入到本Project
//3.创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4.然后执行相关的方法,返回单个结果
String sql = "select * from actor where id = ?";
//因为我们返回的是单行记录<--->单个对象,使用的 Handler 是 BeanHandler
Actor actor =
queryRunner.query(conncteion, sql, new BeanHandler<>(Actor.class), 1);
System.out.println(actor);
//释放资源
JDBCUtilsByDruid.close(null,null,conncteion);
}
//演示 apache + druid 完成查询结果是单行单列 -- 返回的就是 object
@Test
public void testScalsar() throws SQLException {
//1.得到连接
Connection conncteion = JDBCUtilsByDruid.getConncteion();
//2.使用DBUTils类和接口,
// 2.1 先引入DBUtils相关的jar,加入到本Project
//3.创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4.然后执行相关的方法,返回单个结果
String sql = "select name from actor where id = ?";
//因为返回的是一个对象,使用的 handler 就是 ScalarHandler
Object obj =
queryRunner.query(conncteion, sql, new ScalarHandler(), 1);
System.out.println(obj);
//释放资源
JDBCUtilsByDruid.close(null,null,conncteion);
}
//演示 apache + druid 完成 dml 操作(update,insert,delete)
@Test
public void testDml() throws SQLException {
//1.得到连接
Connection conncteion = JDBCUtilsByDruid.getConncteion();
//2.使用DBUTils类和接口,
// 2.1 先引入DBUtils相关的jar,加入到本Project
//3.创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4.然后执行相关的方法,返回单个结果
//添加数据 insert
//String sql = "insert into actor values(null,'蔡依林','女','1980-9-15','123')";
//修改数据 update
//String sql = "update actor set name = '昆凌' where id = ?";
//删除数据 delete
String sql = "delete from actor where id = ? ";
//(1) 执行 dml 操作是 queryRunner.update()
//(2) 返回的值是受影响的行数(affected:受影响)
int affectedRow = queryRunner.update(conncteion, sql, 4);
System.out.println(affectedRow>0?"执行成功":"执行没有影响到表");
}
我们先分析一个问题:
apache-dbutils+Druid 简化了JDBC开发,但还有不足之处:
DAO: data access object数据访问对象
这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作
在BaiscDao 的基础上,实现一张表 对应一个Dao,更好的完成功能,
比如 Customer表Customeriava类(javabean)-CustomerDao.java
完成一个简单设计
先创建一个包com.song.dao_
然后在这个包下创建一些子包
-- 创建表格
CREATE TABLE new_goods(
id INT PRIMARY KEY AUTO_INCREMENT,
goods_name VARCHAR(10),
price DOUBLE);
com.song.dao_ .utils // 工具类
#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/sys_db02?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/sys_db02
username=root
password=sys
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)
maxWait=5000
package com.song.dao_.utils;
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;
/**
* 基于Druid数据库连接池的工具类
*/
public class JDBCUtilsByDruid {
private static DataSource ds;
//用静态代码块完成 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);
}
}
//编写getConnection方法
public static Connection getConncteion() throws SQLException {
return ds.getConnection();
}
//关闭连接
//注意:这里的关闭连接,在数据库连接池技术中,不是真的断掉连接
//而是把使用的Connection对象放回连接池
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);
}
}
}
com.song.dao_ .domain // javabean
package com.song.dao_.domain;
public class Goods {
private Integer id;
private String name;
private Double price;
public Goods() {
}
public Goods(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "\nGoods{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
com.song.dao_ .dao // 存放XxXDAO 和BasicDAO
package com.song.dao_.dao;
import com.song.jdbc.datasource.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;
/**
* 开发 BasicDAO,是其他 DAO 的父类
*/
public class BasicDAO<T> {//泛型指定具体类型
private QueryRunner qr = new QueryRunner();
//开发通用的 dml 方法,针对任意的表
public int update(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
*
* @param sql sql语句,可以有 ?
* @param clazz 传入一个类的Class对象,比如Actor.class
* @param parameters 传入 ? 的具体的值,可以是多个
* @return 根据Actor.class 返回对应的 ArrayList 集合
*/
//返回多个对象(即查询结果是多行的),针对任意表
public List<T> queryMulti(String sql,Class<T> clazz,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new BeanListHandler<>(clazz),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行结果的通用方法
public T querySingle(String sql,Class<T> clazz,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new BeanHandler<T>(clazz),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行单列结果的通用方法,即返回单值的方法
public Object queryScalar(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new ScalarHandler(),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
package com.song.dao_.dao;
public class GoodsDAO extends BasicDAO{
//1.有BasicDAO的方法
//2.根据业务需求,可以添加自己特有的方法
}
com.song.dao_ .test // 写测试类
package com.song.dao_.test;
import com.song.dao_.dao.ActorDAO;
import com.song.dao_.domain.Actor;
import org.junit.jupiter.api.Test;
import java.util.List;
public class TestDAO {
//测试 ActorDAO 对 actor 表 crud 操作
@Test
public void testDAO(){
ActorDAO actorDAO = new ActorDAO();
//1.查询多行
List<Actor> actors =
actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 1);
System.out.println("====查询多行显示====");
for (Actor actor : actors) {
System.out.println(actor);
}
//2.查询单行
Object o =
actorDAO.querySingle("select * from actor where id = ?", Actor.class, 1);
System.out.println("====查询单行显示====");
System.out.println(o);
//3.查询单行单列
Object o2 =
actorDAO.queryScalar("select name from actor where id = ?", 1);
System.out.println("====查询单行单列====");
System.out.println(o2);
//4.进行dml操作 update,insert,delete
//insert
/*
int update = actorDAO.update("insert into actor values(null,?,?,?,?)",
"蔡依林", "女", "1980-9-15", "123");
System.out.println(update>0?"执行成功":"执行没有影响表");
*/
//update
/*
int update =
actorDAO.update("update actor set name = '昆凌' where id = ?", 4);
System.out.println(update>0?"执行成功":"执行没有影响表");
*/
//delete
int update =
actorDAO.update("delete from actor where id = ?", 4);
System.out.println(update>0?"执行成功":"执行没有影响表");
}
}
开发GoodsDao和Goods, 完成对goods表的crud.
表格示意图如下:
-- 创建演员表测试
CREATE TABLE actor( -- 演员表
id INT PRIMARY KEY AUTO_INCREMENT,-- 主键自增长
`name` VARCHAR(32) NOT NULL DEFAULT'',
sex CHAR(1) NOT NULL DEFAULT '女',
borndate DATETIME,-- 生日
phone VARCHAR(12));
com.song.dao_ .utils // 工具类
#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/sys_db02?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/sys_db02
username=root
password=sys
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=20
#max wait time (5000 mil seconds)
maxWait=5000
package com.song.dao_.utils;
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;
/**
* 基于Druid数据库连接池的工具类
*/
public class JDBCUtilsByDruid {
private static DataSource ds;
//用静态代码块完成 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);
}
}
//编写getConnection方法
public static Connection getConncteion() throws SQLException {
return ds.getConnection();
}
//关闭连接
//注意:这里的关闭连接,在数据库连接池技术中,不是真的断掉连接
//而是把使用的Connection对象放回连接池
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);
}
}
}
com.song.dao_ .domain // javabean
package com.song.dao_.domain;
public class Goods {
private Integer id;
private String goods_name;
private Double price;
public Goods() {
}
public Goods(Integer id, String goods_name, Double price) {
this.id = id;
this.goods_name = goods_name;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGoods_name() {
return goods_name;
}
public void setGoods_name(String goods_name) {
this.goods_name = goods_name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "\nGoods{" +
"id=" + id +
", goods_name='" + goods_name + '\'' +
", price=" + price +
'}';
}
}
com.song.dao_ .dao // 存放XxXDAO 和BasicDAO
package com.song.dao_.dao;
import com.song.jdbc.datasource.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;
/**
* 开发 BasicDAO,是其他 DAO 的父类
*/
public class BasicDAO<T> {//泛型指定具体类型
private QueryRunner qr = new QueryRunner();
//开发通用的 dml 方法,针对任意的表
public int update(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
*
* @param sql sql语句,可以有 ?
* @param clazz 传入一个类的Class对象,比如Actor.class
* @param parameters 传入 ? 的具体的值,可以是多个
* @return 根据Actor.class 返回对应的 ArrayList 集合
*/
//返回多个对象(即查询结果是多行的),针对任意表
public List<T> queryMulti(String sql,Class<T> clazz,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new BeanListHandler<>(clazz),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行结果的通用方法
public T querySingle(String sql,Class<T> clazz,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new BeanHandler<T>(clazz),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行单列结果的通用方法,即返回单值的方法
public Object queryScalar(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConncteion();
return qr.query(connection,sql,new ScalarHandler(),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
package com.song.dao_.dao;
public class GoodsDAO extends BasicDAO{
//1.有BasicDAO的方法
//2.根据业务需求,可以添加自己特有的方法
}
com.song.dao_ .test // 写测试类
package com.song.dao_.test;
import com.song.dao_.dao.GoodsDAO;
import com.song.dao_.domain.Goods;
import org.junit.jupiter.api.Test;
import java.util.List;
public class TestDAO2 {
//测试 GoodsDAO 对 new_goods 表 crud 操作
//先进行 dml 操作
@Test
public void testDAO(){
GoodsDAO goodsDAO = new GoodsDAO();
//insert
/*
int update1 = goodsDAO.update
("insert into new_goods values(null,?,?)", "小米手机", 1999);
int update2 = goodsDAO.update
("insert into new_goods values(null,?,?)", "魅族手机", 1999);
int update3 = goodsDAO.update
("insert into new_goods values(null,?,?)", "三星手机", 3999);
int update4 = goodsDAO.update
("insert into new_goods values(null,?,?)", "苹果手机", 8999);
int update5 = goodsDAO.update
("insert into new_goods values(null,?,?)", "华为手机", 9999);
int update6 = goodsDAO.update
("insert into new_goods values(null,?,?)", "华为手机", 9999);
System.out.println(update1>0?"第一条执行成功":"执行没有影响表");
System.out.println(update2>0?"第二条执行成功":"执行没有影响表");
System.out.println(update3>0?"第三条执行成功":"执行没有影响表");
System.out.println(update4>0?"第四条执行成功":"执行没有影响表");
System.out.println(update5>0?"第五条执行成功":"执行没有影响表");
System.out.println(update6>0?"第五条执行成功":"执行没有影响表");
*/
//update
/*
int update = goodsDAO.update
("update new_goods set goods_name = 'OPPO' where id = ?", 6);
System.out.println(update>0?"修改执行成功":"执行没有影响表");
*/
//delete
int update = goodsDAO.update
("delete from new_goods where id = ?", 6);
System.out.println(update>0?"删除执行成功":"执行没有影响表");
}
//查找
@Test
public void testSelect(){
GoodsDAO goodsDAO = new GoodsDAO();
//查找多行
List<Goods> goods =
goodsDAO.queryMulti("select * from new_goods where id >= ?", Goods.class, 1);
System.out.println("====查询多行显示====");
for (Goods good : goods) {
System.out.println(good);
}
//查找单行
Object single = goodsDAO.querySingle
("select * from new_goods where id = ?", Goods.class, 1);
System.out.println("====查询单行显示====");
System.out.println(single);
//查找单行单列显示
Object scalar= goodsDAO.queryScalar
("select goods_name from new_goods where id = ?", 1);
System.out.println("====查询单行单列显示====");
System.out.println(scalar);
}
}
最终显示结果如下: