Mysql&JDBC
在学习JDBC操作前,最好复习一下Mysql的操作,以免学着前面的,忘记后面的,最后什么都学不到。
在博客的最后我给出了一个小练习,最好看完这个博客,你感觉自己都会了,就去把那个小练习写了,不要觉得浪费时间;这个练习考验了你封装的思想。
Mysql-复习
DDL
(Data Definition Language)是用于定义和管理数据库结构的SQL语言的一部分。MySQL支持许多DDL命令,这些命令用于创建、修改和删除数据库对象(例如表、索引、视图等)。以下是一些常用的MySQL DDL命令:
CREATE TABLE: 用于创建表格。例如:
CREATE TABLE employees (
id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
birth_date DATE
);
ALTER TABLE: 用于修改现有表的结构。例如,添加列:
ALTER TABLE employees
ADD COLUMN email VARCHAR(100);
DROP TABLE: 用于删除表。例如:
DROP TABLE employees;
CREATE INDEX: 用于创建索引,以提高检索效率。例如:
CREATE INDEX idx_last_name ON employees (last_name);
DROP INDEX: 用于删除索引。例如:
DROP INDEX idx_last_name ON employees;
CREATE DATABASE: 用于创建数据库。例如:
CREATE DATABASE mydatabase;
DROP DATABASE: 用于删除数据库。例如:
DROP DATABASE mydatabase;
USE: 用于选择要使用的数据库。例如:
USE mydatabase;
DML
DML(Data Manipulation Language)是SQL的一部分,用于处理数据库中的数据。DML命令用于查询、插入、更新和删除数据库中的数据。以下是一些常用的MySQL DML命令:
SELECT: 用于查询数据库中的数据。例如:
SELECT * FROM employees WHERE department = 'IT';
INSERT INTO: 用于向表中插入新的行。例如:
INSERT INTO employees (first_name, last_name, birth_date)
VALUES ('John', 'Doe', '1990-01-15');
UPDATE: 用于更新表中的数据。例如:
UPDATE employees
SET department = 'HR'
WHERE last_name = 'Doe';
DELETE: 用于删除表中的数据。例如:
DELETE FROM employees
WHERE last_name = 'Doe';
INSERT INTO ... SELECT: 将查询结果插入到另一个表中。例如:
INSERT INTO new_employees (first_name, last_name, birth_date)
SELECT first_name, last_name, birth_date
FROM old_employees
WHERE department = 'IT';
DCL
DCL(Data Control Language)是SQL的一部分,用于授予或撤销访问数据库对象的权限。DCL命令用于管理数据库的安全性和访问控制。以下是两个主要的MySQL DCL命令:
GRANT: 用于授予用户或用户组对数据库对象(如表、视图)的特定权限。例如:
GRANT SELECT, INSERT ON employees TO 'user1'@'localhost';
上述命令授予了'user1'@'localhost'用户对employees表的SELECT和INSERT权限。
REVOKE: 用于撤销用户或用户组对数据库对象的权限。例如:
REVOKE SELECT ON employees FROM 'user1'@'localhost';
上述命令撤销了'user1'@'localhost'用户对employees表的SELECT权限。
DQL
DQL(Data Query Language)是SQL的一部分,专门用于执行查询操作以检索数据库中的数据。DQL命令的主要目的是从数据库中选择数据,而不对数据进行更改。以下是一个主要的MySQL DQL命令:
SELECT: 用于从一个或多个表中选择数据。例如:
SELECT first_name, last_name, birth_date
FROM employees
WHERE department = 'IT';
上述命令选择了employees表中部门为'IT'的员工的姓名和出生日期。
JDBC
什么是JDBC?
JDBC(Java Database Connectivity)是Java的数据库连接API,用于连接和操作关系型数据库,如MySQL、Oracle等。它包括驱动管理器(DriverManager)、驱动程序(Driver)、连接(Connection)、语句(Statement)、结果集(ResultSet)等组件,提供了统一的方式让Java应用程序与不同数据库交互。
在学习JDBC前的准备
安装Mysql数据库,和下载JDBC驱动包
JDBC连接数据库的流程-查询
public class JDBCConnection {
/**
* 数据库连接的信息,这里我使用了final,主要是这些是常量,我不想这些值改变。
* 在学到后面,这些有关数据连接的信息一般存放在Jdbc.properties文件下
*/
private final String URL = "jdbc:mysql://localhost:3306/learn";
private final String USER = "learn";
private final String PASSWORD = "888888";
private final String DRIVER = "com.mysql.cj.jdbc.Driver";
}
//数据库查询数据
public void selectShow() throws ClassNotFoundException, SQLException {
//加载数据库驱动程序
Class.forName(DRIVER);
/**
* 注册驱动,建立数据库连接
* 其中的三个参数的作用
* url:jdbc:mysql://localhost:3306/learn
* jdbc:mysql:jdbc连接Mysql数据库的协议前缀
* localhost:服务器的主机名或ip地址
* 3306:Mysql数据库服务端的默认端口号
* learn:数据库名
* user:数据库的用户名
* password:对应用户的密码
*/
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
/**
* 创建statement对象
* 这个对象有两个,一个是createStatement(),还有一个是prepareStatement()
*/
Statement statement = connection.createStatement();
//要执行的sql语句
String selectSql ="SELECT * FROM user";
/**
* 执行sql语句
* 这里使用的是executeQuery(String sql)方法,返回的是一个迭代器,
* 使用resultSet.next();
* statement.executeUpdate(selectSql);
*/
ResultSet resultSet = statement.executeQuery(selectSql);
while(resultSet.next()){
//通过列名或索引获取数据
int id = resultSet.getInt(1);
String username = resultSet.getString(2);
String password = resultSet.getString(3);
//打印数据
System.out.println("id:" + id + "&" + "username:" + username +"&" + "password:" + password);
}
//关闭资源
resultSet.close();
statement.close();
connection.close();
}
prepareStatement()&createStatement()-添加
让我们来看看两者的区别
createStatement()
:
-
使用
createStatement()
方法创建的Statement
对象用于执行简单的 SQL 语句,这些语句在执行时不包含参数。例如,执行一条简单的 SELECT 语句或更新语句。 -
不支持参数绑定,因此对于包含变量的 SQL 语句,你需要手动拼接字符串。这可能导致安全性问题(例如 SQL 注入)。
-
Statement statement = connection.createStatement(); String sql = "SELECT * FROM user WHERE username = 'zhangsan'"; ResultSet resultSet = statement.executeQuery(sql);
prepareStatement()
:
-
使用
prepareStatement()
方法创建的PreparedStatement
对象用于执行预编译的 SQL 语句,这些语句在执行时可以包含参数。这种方式更安全,能够防止 SQL 注入攻击。 -
支持参数绑定,可以通过
setXXX()
方法为 SQL 语句的参数传递值。这使得 SQL 语句的执行更加灵活和可维护。 -
String sql = "SELECT * FROM user WHERE username = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, "zhangsan"); ResultSet resultSet = preparedStatement.executeQuery();
//数据库添加数据
public void addShow() throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName(DRIVER);
//注册驱动
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//预编译的sql语句,?是一个占位符
String addSql = "INSERT INTO user(username,password) VALUES(?,?);";
//获取预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(addSql);
//设置占位符的参数值(可以通过索引或参数名)
preparedStatement.setString(1,"test");
preparedStatement.setString(2,"444444");
/**
* 返回sql语句执行的结果(sql语句执行的影响行数)
* 在执行删除sql语句的时候,其返回的值的就不可靠了
*
*/
int executeUpdate = preparedStatement.executeUpdate();
//判断sql语句的执行情况
if (executeUpdate > 0){
System.out.println("success!");
}else{
System.out.println("error!");
}
//关闭资源
preparedStatement.close();
connection.close();
}
数据库修改操作
//数据库修改数据
public void updateShow() throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName(DRIVER);
//获取连接
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//updateSql语句的编写
String updateSql = "UPDATE user SET password = ? WHERE id = ?";
//获取预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(updateSql);
//参数赋值
preparedStatement.setString(1,"testPassword");
preparedStatement.setInt(2,4);
//开始执行,获取结果
int executeUpdate = preparedStatement.executeUpdate();
//解析结果
if (executeUpdate != 0){
System.out.println("success!");
}else{
System.out.println("error!");
}
//关闭资源
preparedStatement.close();
connection.close();
}
数据库删除操作
//数据库删除数据
public void deleteShow() throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName(DRIVER);
//获取连接
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//deleteShow语句编写
String deleteSql = "DELETE FROM user WHERE id = ?";
//获取预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(deleteSql);
//设置参数
preparedStatement.setInt(1,4);
//执行sql语句,返回结果
int executeUpdate = preparedStatement.executeUpdate();
//解析结果
if (executeUpdate != 0){
System.out.println("success!");
}else{
System.out.println("error!");
}
}
主键回显-
一般都是在数据添加的时候会有要求,要回显主键
//数据库添加数据
public void addShow() throws ClassNotFoundException, SQLException {
//加载驱动
Class.forName(DRIVER);
//注册驱动
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//预编译的sql语句,?是一个占位符
String addSql = "INSERT INTO user(username,password) VALUES(?,?);";
//获取预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(addSql,Statement.RETURN_GENERATED_KEYS);
//设置占位符的参数值(可以通过索引或参数名)
preparedStatement.setString(1,"test");
preparedStatement.setString(2,"444444");
/**
* 返回sql语句执行的结果(sql语句执行的影响行数)
* 在执行删除sql语句的时候,其返回的值的就不可靠了
*
*/
int executeUpdate = preparedStatement.executeUpdate();
//判断sql语句的执行情况
if (executeUpdate > 0){
System.out.println("success!");
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
generatedKeys.next();
int id = generatedKeys.getInt(1);
System.out.println("添加user返回的编号id:" + id);
}else{
System.out.println("error!");
}
//关闭资源
preparedStatement.close();
connection.close();
}
多次添加数据优化
注意在对于addBatch()sql语句的添加,就是添加(domei,domei)到原来sql语句values的后面
本质就是添加数据VALUES(?,?),(?,?),(?,?),(?,?),(?,?)…
想使用这个功能,需要在url后面添加这个rewriteBatchedStatement=true
开启多值写入的功能
//添加缓冲区(多次添加数据)
public void bufferShow() throws SQLException, ClassNotFoundException {
//加载驱动
Class.forName(DRIVER);
//注册驱动,获取连接
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//编写sql语句
String insertSql = "INSERT INTO user(username,password) VALUES(?,?)";
//获取预编译器
PreparedStatement preparedStatement = connection.prepareStatement(insertSql);
//记录开始时间
long start = System.currentTimeMillis();
//添加1000条数据
for (int i = 0; i < 1000; i++) {
//设置参数
preparedStatement.setString(1, "dome" + i);
preparedStatement.setString(2,"dome" + i);
//sql语句的添加
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
//记录结束时间
long end = System.currentTimeMillis();
//输出用时
System.out.println("添加10000条数据一共用时:"+ (end - start) + "毫秒");
//关闭资源
preparedStatement.close();
connection.close();
}
事务
/**
*事务回滚(删除数据库里数据的时候,如果代码执行错误的话,可以将原来执行代码的效果取消)
* 案例:在转账的时候,如果对方用户不存在,你原来进行转账执行的sql效果就要取消,这个时候就需要回滚操作
*/
public void affairShow() throws SQLException, ClassNotFoundException {
//加载驱动
Class.forName(DRIVER);
//注册驱动,获取连接
Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
//编写sql语句
String deleteSql = "DELETE FROM user WHERE id = ?";
try{
//关闭自动提交事务
connection.setAutoCommit(false);
//获取编译器对象
PreparedStatement preparedStatement = connection.prepareStatement(deleteSql);
//参数赋值
preparedStatement.setInt(1,41001);
//发送sql语句(执行sql语句)
int executeUpdate = preparedStatement.executeUpdate();
if (executeUpdate > 0){
System.out.println("success!");
}else {
System.out.println("error!");
}
//关闭资源
preparedStatement.close();
connection.close();
}catch (Exception e){
//有异常,就会进行回滚
connection.rollback();
}
}
连接池
变量的声明
private final String URL = "jdbc:mysql://localhost:3306/learn";
private final String USER = "learn";
private final String PASSWORD = "888888";
private final String DRIVER = "com.mysql.cj.jdbc.Driver";
显性创建连接池
public void druidShow1() throws SQLException {
//获取连接池对象
DruidDataSource dataSource = new DruidDataSource();
//设置参数
dataSource.setDriverClassName(DRIVER);//加载驱动的参数
dataSource.setUrl(URL);//注册驱动的参数
dataSource.setUsername(USER);//注册驱动的参数
dataSource.setPassword(PASSWORD);//注册驱动的参数
dataSource.setInitialSize(5);//初始化连接数量
dataSource.setMaxActive(10);//最大的数量
//获取连接
Connection connection = dataSource.getConnection();
//数据库操作
//回收连接
connection.close();//在连接池中connection.close()是回收连接
}
隐性创建连接池
//隐性操作创建连接池
public void druidShow2() throws Exception {
//去读外部配置文件Properties
Properties properties = new Properties();
//src下的文件,可以使用类加载器提供的方法
InputStream ips = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
//使用连接池的工具类的工程模式,创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
//数据库操作
//回收操作
connection.close();
}
封装
首先我来了解对简单的工具类封装
public class DruidUtil {
/**
* 连接池工具类的封装(就是创建一个DruidUtil类把使用这需要的方法,放到类中)
* 获取连接池对象
* 关闭连接
*/
//创建连接池对象
private static DataSource druidDataSource= null;
static {
//获取外部配置文件
Properties properties = new Properties();
//加载外部配置文件
InputStream inputStream = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
try {
//将配置文件信息加载到properties
properties.load(inputStream);
//通过配置文件信息创建连接池对象
druidDataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//外部获取连接对象
public static Connection getConnection() throws SQLException {
//返回连接对象
return druidDataSource.getConnection();
}
//外部回收连接对象
public static void freeConnection(Connection connection) throws SQLException {
connection.close();
}
}
在上面的封装类中,你可以发现,在外部获取连接对象的时候,本质还是new了一个连接对象,我们想要的是我们每次获取连接对象的时候,不是直接new一个而是看看本地有没有,下面给出优化代码。
public class DruidUtil {
//创建连接池对象
private static DataSource druidDataSource= null;
//获取线程本地变量,来存放我们的连接对象
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
static {
//获取外部配置文件
Properties properties = new Properties();
//加载外部配置文件
InputStream inputStream = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
try {
//将配置文件信息加载到properties
properties.load(inputStream);
//通过配置文件信息创建连接池对象
druidDataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//外部获取连接对象
public static Connection getConnection() throws SQLException {
/**
* 在向外部提供连接对象的时候,我要先判断本地线程对象中是否有空闲的,
* 如果有的话,我们直接向外部提供已经创建好的对象,
* 会节省很多资源
*/
Connection connection = threadLocal.get();
//进行判断
if (connection == null){
//重新创建一个连接对象
connection= druidDataSource.getConnection();
//并把新创建的对象存放到本地线程变量里
threadLocal.set(connection);
}
//返回连接对象
return connection;
}
//外部回收连接对象
public static void freeConnection() throws SQLException {
//从线程本地变量里获取连接对象
Connection connection = threadLocal.get();
//判断连接对象是否存在,
if (connection != null){
//连接对象存在,清空线程本地变量
threadLocal.remove();
//事务状态回滚
connection.setAutoCommit(true);
//关闭连接
connection.close();
}
}
}
在最后我给出一个小练习,使用上面学过的知识,把JDBC对数据库的增、删、改、查这四个操作封装成一个工具类;这里给出一点小提示:请注意增、删、改这个三个操作高度相似。