JDBC进化
var code = "0c352920-80ec-468b-9148-348320a4a921"
明确jdbc的大概流程
- 注册驱动
- 创建连接
- 写sql
- 创建statement(分不同statement是否赋值还是直接写进去)
- 处理获取结果
- 关闭资源
注册驱动
大致有两种方法
- DriverManager.registerDriver(new Driver());
- Class.forName("com.mysql.cj.jdbc.Driver");
方法1会生成两个Driver,一个是registerDriver方法调用生成的驱动,还有一个是Driver自己的静态代码块会生成一个驱动,造成浪费
方法2是利用加载类来实现一次静态代码块的加载,推荐使用
创建连接
创建连接的方法是
DriverManager.getConnection();
分三种参数类型
//1.分开写
"jdbc:数据库类型://主机地址:端口/数据库名","用户名","密码"
//2.混在一块写
"jdbc:数据库类型://主机地址:端口/数据库名?user=用户名&password=密码"
//3.创建一个properties对象来装用户名和密码
Properties info = new Properties;
info.put("user","用户名");
info.put("password","密码");
"jdbc:数据库类型://主机地址:端口/数据库名?",info
//?后可以跟可选信息:
serverTimezone=Asia/Shanghai&
useUnicode=true&
characterEncoding=utf8&
useSSL=true这些8版本以后都是默认有的了
写sql
String sql = "具体的sql语句"
/*这里的主要差别在于后面是使用哪种statement
Statement就是纯静态*/
String sql = "select user_name from t_user where id = " + id + ";";
#有被注入攻击的风险,一般不使用
#假设我传入的id是"1 or 1 = 1";就是注入攻击
/*一般采用preparedStatement
sql语句中就用?来占位*/
String sql = "select user_name from t_user where id = ?";
创建statement
connection.statement;
connection.prepareStatement(sql);
/*大致为什么会有prepareStatement上面的sql已经介绍过了
这里大致说一下赋值方法*/
preparedStatement.setObject(第几个问号,赋的值);
//注意这里的问号是从1开始的,不是0开始
/*当我们需要插入大量数据时,可以发现花费的时间非常多
这是因为,我们每插入一条数据就发送一次,这种网络的传输非常的慢
,所以我们就想办法解决,一条数据配好了,我们先不发送,等待所有的
行都完成了我们再一次性发送,这样我们的传输时间就从原来的n次变
为了1次*/
//原始
for(int i = 0; i < 10000; i++){
preparedStatement.setObject(i,"name" + i);
preparedStatement.executeUpdata();
}
//改进
for(int i = 0; i < 10000; i++){
preparedStatement.setObject(i,"name" + i);
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
处理获取结果
/*处理结果分为两种executeUpdata,executeQuery*/
//executeUpdata是针对非DQL的,返回的是影响到表中几行数据
//executeQuery是针对DQL的,返回的是resultSet,使用next()方法来一行一行往下走
//这里谈到DQL就顺便回顾一下
/*
DQL(Data Query Language): 用于查询数据库中的数据
DDL(Data Definition Language): 用于定义数据库架构和结构
DML(Data Manipulation Language): 用于对数据库中的数据进行操作
TCL(Transaction Control Language): 用于管理数据库事务
*/
关闭资源
/*要将resultSet,preparedStatment,Statement,connection都使用close()方法来关闭*/
事务
我们在java中用try{}catch(){}来实现事务
try{
//关闭事务提交
connection.setAutoCommit(false);
//crud操作
//成功就提交
connection.commit();
}catch(Exception e){
//回滚
connection.rollback();
throw e;
}
主键回显
开发中,我们可能会遇到一张表插入后产生的主键是另外一行数据插入另外一张表的数据,所以我们在向表插入数据时可能需要主键回显来实现拿到产生的主键
//在创建preparedStatement时,要跟上Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.preparedStatement(sql,Statement.RETURN_GENERATED_KEYS);
//这个结果集是一行一列的
Result result = preparedStatement.getGeneratedKeys();
result.next();
int id = result.getInt(1);
druid
用来做连接池的
每次创建连接的花费时间太长,并且一次性用完就销毁,我们就想有一个连接池,在程序运行的开始就将我们后面所需的连接都创建出来,并且能够反复利用,不用频繁创建
前面我们一直都是硬连接,就是将connection的参数都直接写在代码里,我们想能够将配置参数单拉出去,这样我们的业务在进行变动时能够不修改代码只修改配置参数就能继续维护下去
我们希望将基本的获取连接,回收连接写在底层,这样,后面我们需要时就只用调用某个方法就行了
考虑到将连接池对象只初始化一次就好,后面不要再有重复的声明连接池对象,所以将初始化连接池对象就写在类的静态代码块里
/*数据库URL(url): 指定数据库的连接地址,它通常以"jdbc:mysql://"开头,后面跟着主机名、端口号和数据库名称。
用户名(user): 指定连接数据库时使用的用户名。
密码(password): 指定连接数据库时使用的密码。注意,密码通常不建议明文存储在属性文件中,出于安全性考虑,可以采用加密或其他安全手段。
驱动程序类名(driver): 指定用于与MySQL数据库通信的JDBC驱动程序类的名称。
字符集(characterEncoding): 指定与数据库通信时使用的字符集编码,通常用于处理特殊字符和国际化。
连接池配置: 如果您使用连接池来管理数据库连接,您可以配置连接池的相关属性,如连接池的最大连接数、空闲连接数、连接超时等。
SSL配置: 如果需要通过SSL安全连接到MySQL服务器,您可以配置SSL相关的属性,如SSL证书和密钥文件的位置。*/
jdbc:mysql://localhost:3306/mydatabase
user=myuser
password=mypassword
driver=com.mysql.cj.jdbc.Driver
characterEncoding=UTF-8
maxActive=100
maxIdle=30
maxWait=10000
useSSL=true
trustCertificateKeyStoreUrl=file:/path/to/truststore
trustCertificateKeyStorePassword=yourpassword
package com.jdbc.api.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* ClassName: JdbcUtils
* Package: com.jdbc.api.utils
* @Create 2023/9/24 19:52
* @Version 1.0
*/
public class JdbcUtilsV2 {
//连接池对象
private static DataSource dataSource = null;
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
static {
//初始化连接池对象
//创建一个配置文件对象
Properties properties = new Properties();
//用类加载器获取druid.properties的资源并将其作为输入流返回给resourceAsStream
InputStream resourceAsStream = JdbcUtilsV2.class.getClassLoader().getResourceAsStream("druid.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//获取连接
public static Connection getConnection() throws SQLException {
Connection connection = tl.get();
if (connection == null) {
connection = dataSource.getConnection();
tl.set(connection);
}
return connection;
}
//关闭连接
public static void freeConnection() throws SQLException {
Connection connection = tl.get();
if (connection != null) {
//清空线程本地变量
tl.remove();
//事务状态回到false
connection.setAutoCommit(false);
connection.close();
}
}
}
package com.jdbc.api.utils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.time.temporal.ValueRange;
import java.util.ArrayList;
import java.util.List;
/**
* ClassName: BaseDao
* Package: com.jdbc.api.utils
* @Create 2023/9/24 20:22
* @Version 1.0
*/
public abstract class BaseDao {
//非DQL
public static int executeUpdate(String sql,Object... params) throws SQLException {
//获取连接
Connection connection = JdbcUtilsV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
preparedStatement.setObject(i +1,params[i]);
}
int i = preparedStatement.executeUpdate();
preparedStatement.close();
//判断是否开启事务,开启了就不暂时不能关闭
if (connection.getAutoCommit()) {
JdbcUtilsV2.freeConnection();
}
return i;
}
//DQL
public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params) throws NoSuchFieldException, IllegalAccessException, InstantiationException, SQLException, NoSuchMethodException, InvocationTargetException {
//获取连接
Connection connection = JdbcUtilsV2.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
if (params != null && params.length != 0) {
for (int i = 1; i <= params.length; i++) {
preparedStatement.setObject(i,params[i - 1]);
}
}
ResultSet resultSet = preparedStatement.executeQuery();
ArrayList<T> list = new ArrayList<>();
ResultSetMetaData metaData = preparedStatement.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()){
//调用类的无参构造函数实例化对象
T t = clazz.getDeclaredConstructor().newInstance();
for (int i = 0; i < columnCount; i++) {
//列名
String columnLabel = metaData.getColumnLabel(i);
//值
Object value = resultSet.getObject(i);
//使用反射给对象的属性赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,value);
}
list.add(t);
}
//关闭资源
resultSet.close();
preparedStatement.close();
if (connection.getAutoCommit()) {
JdbcUtilsV2.freeConnection();
}
return list;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通