jdbc万字长文总结回炉重造(代码片段)

ZSYL ZSYL     2022-12-28     161

关键词:

1. 概述

在Java中,数据库存取技术可分为如下几类:

  • JDBC直接访问数据库
  • JDO技术(Java Data Object)
  • 第三方O/R工具,如Hibernate, Mybatis 等

JDBC是java访问数据库的基石,JDO, Hibernate等只是更好的封装了JDBC。

1.1 什么是JDBC

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统(DBMS)、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这个类库可以以一种标准的方法、方便地访问数据库资源。

JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。

JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。

如果没有JDBC,那么Java程序访问数据库时是这样的:


JDBC是SUN公司提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。

不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

1.2 JDBC API

JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQL语句,并到得到返回结果等各类操作。声明在java.sql与javax.sql包中。

1.3 JDBC 可以做什么


我们可以做以下事情:增删改查

①查询女神们的基本信息
②查询女神们的详细信息
③查询心仪女神的电话号码 ,噢耶
④添加心仪的女神
⑤删除不符合要求的女神
⑥修改女神的信息
⑦查询管理员的所有信息

1.4 JDBC程序编写步骤

  1. 注册驱动
  2. 获取连接
  3. 执行增删改查
  4. 释放资源

2. 演示完整步骤

2.1 引入JDBC驱动程序

准备工作:引入JDBC驱动程序

驱动程序由数据库提供商提供下载。 MySQL的驱动下载地址:http://dev.mysql.com/downloads/

如何在Java Project项目应用中添加数据库驱动jar:


1.把上图.jar包拷贝到项目中一个目录中:

2.添加到项目的类路径下

在驱动jar上右键–>Build Path-->Add to Build Path

注意:如果是Dynamic Web Project(动态的web项目)话,则是把驱动jar放到WebContent(有的开发工具叫WebRoot)目录中的WEB-INF目录中的lib目录下即可

2.2 加载并注册驱动

加载并注册驱动:
加载驱动,把驱动类加载到内存
注册驱动,把驱动类的对象交给DriverManager管理,用于后面创建连接等使用。

2.2.1 Class.forName( )

因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例,所以可以换一种方式来加载驱动。(即只要想办法让驱动类的这段静态代码块执行即可注册驱动类,而要让这段静态代码块执行,只要让该类被类加载器加载即可)

调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名:

//通过反射,加载与注册驱动类,解耦合(不直接依赖)
Class.forName("com.mysql.jdbc.Driver");

2.2.2 服务提供者框架

服务提供者框架(例如:JDBC的驱动程序)自动注册(有版本要求)

符合JDBC 4.0规范的驱动程序包含了一个文件META-INF/services/java.sql.Driver,在这个文件中提供了JDBC驱动实现的类名。

例如:mysql-connector-java-5.1.40-bin.jar文件中就可以找到java.sql.Driver文件,用文本编辑器打开文件就可以看到:com.mysql.jdbc.Driver类。

JVM的服务提供者框架在启动应用时就会注册服务,例如:MySQL的JDBC驱动就会被注册,而原代码中的Class.forName("com.mysql.jdbc.Driver")仍然可以存在,但是不会起作用。

但是注意mysql-connector-java-5.0.8-bin.jar版本的jar中没有,如下:

2.3 获取数据库链接

可以通过 DriverManager 类建立到数据库的连接Connection

DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。

  • public static Connection getConnection(String url)
  • public static Connection getConnection(String url,String user, String password)
  • public static Connection getConnection(String url,Properties info)其中Properties info通常至少应该包括 “user” 和 “password” 属性

JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。JDBC URL的标准由三部分组成,各部分间用冒号分隔。 jdbc:<子协议>:<子名称>

  • 协议:JDBC URL中的协议总是jdbc
  • 子协议:子协议用于标识一个数据库驱动程序
  • 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息

例如:

MySQL的连接URL编写方式:

  • jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值
  • jdbc:mysql://localhost:3306/testdb
  • jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)
  • jdbc:mysql://localhost:3306/testdb?user=root&password=123456
//1、加载与注册驱动
Class.forName("com.mysql.jdbc.Driver");

//2、获取数据库连接
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url, "root", "root");

2.4 操作或访问数据库

数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。

其实一个数据库连接就是一个Socket连接。

在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

  • Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
  • PrepatedStatement :SQL语句被预编译并存储在此对象中,然后可以使用此对象多次高效地执行该语句。
  • CallableStatement:用于执行 SQL 存储过程

2.4.1 Statement

通过调用 Connection 对象的 createStatement() 方法创建该对象

该对象用于执行静态的 SQL 语句,并且返回执行结果

Statement 接口中定义了下列方法用于执行 SQL 语句:

  • int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
  • ResultSet excuteQuery(String sql):执行查询操作SELECT

2.4.2 ResultSet

通过调用 Statement 对象的 excuteQuery() 方法创建该对象

ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现

ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行

ResultSet 接口的常用方法:

boolean next()
getXxx(String columnLabel):columnLabel使用 SQL AS 子句指定的列标签。如果未指定 SQL AS 子句,则标签是列名称
getXxx(int index) :索引从1开始

增删改查示例代码:

public class TestConnection 
	//增删改
	@Test
	public void testUpdate() throws  Exception 
		//步骤1:注册驱动(为了让mysql的实现类加载到内存可以使用)
		
//		DriverManager.registerDriver(new Driver());
		Class.forName("com.mysql.jdbc.Driver");
	
		//步骤2:获取连接
		
		Properties properties  = new Properties();//配置文件要求里面必须有连接参数和配置参数
		
		properties.load(new FileInputStream("src\\\\druid.properties"));
		DataSource ds =	DruidDataSourceFactory.createDataSource(properties);
		
		
		Connection connection = ds.getConnection();
		
		//步骤3:执行增删改查操作
		//①获取执行sql语句的命令对象
		Statement statement = connection.createStatement();
		
		
		//②执行sql语句
//		statement.executeQuery(sql);//执行查询语句
//		statement.executeUpdate(sql)//执行增删改语法,返回受影响行数
//		statement.execute(sql)//执行任何sql语句
		
		int update = statement.executeUpdate("delete from admin where id =1");
		
		//③处理结果
		System.out.println(update>0?"成功":"失败");
		
		//步骤4:关闭连接(释放资源)
		
		statement.close();
		connection.close();
		
	
	//查询
	@Test 
	public void testQuery() throws Exception 

		//1.注册驱动
		Class.forName("com.mysql.jdbc.Driver");		
		//2.获取连接
		Properties  pro = new Properties();
		pro.load(new FileInputStream("src\\\\druid.properties"));
		
		DataSource ds = DruidDataSourceFactory.createDataSource(pro);
		
		Connection connection = ds.getConnection();
		
		//3.执行查询
		
		//①获取执行sql的命令对象
		Statement statement = connection.createStatement();
		
		//②执行sql
		
		ResultSet set = statement.executeQuery("select name bname,sex gender,borndate born from beauty");
		while(set.next()) 
			
			String name = set.getString("bname");
			char gender = set.getString("gender").charAt(0);
			Date date = set.getDate("born");
	//		String date = set.getString("born");
			
			System.out.println(name+"\\t"+gender+"\\t"+date);
		
		
		
		//4.关闭
		set.close();
		statement.close();
		connection.close();
		
	

2.5 释放资源

Connection、Statement、ResultSet都是应用程序和数据库服务器的连接资源,使用后一定要关闭,可以在finally中关闭

演示未关闭后果:

package com.atguigu.conn;

import java.sql.Connection;
import java.sql.DriverManager;

public class TestConnectionClose 
	public static void main(String[] args) throws Exception
		//1、加载与注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		
		//2、获取数据库连接
		String url = "jdbc:mysql://localhost:3306/test";
		
		//my.ini中max_connections=10
		for (int i = 0; i < 15; i++) 
			Connection conn = DriverManager.getConnection(url,"root", "123456");
			System.out.println(conn);
			//没有关闭,资源一直没有释放
		
	

3. 封装JDBCUtils

封装JDBCTools

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

/*
 * 获取连接或释放连接的工具类
 */
public class JDBCTools 
	// 1、数据源,即连接池
	private static DataSource dataSource;
	
	// 2、ThreadLocal对象
	private static ThreadLocal<Connection> threadLocal;

	static 
		try 
			//1、读取druip.properties文件
			Properties pro = new Properties();
			pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
			
			//2、连接连接池
			dataSource = DruidDataSourceFactory.createDataSource(pro);

			//3、创建线程池
			threadLocal = new ThreadLocal<>();
		 catch (Exception e) 
			e.printStackTrace();
		
	

	/**
	 * 获取连接的方法
	 * 
	 * @return
	 * @throws SQLException
	 */
	public static Connection getConnection() 
		// 从当前线程中获取连接
		Connection connection = threadLocal.get();
		if (connection == null) 
			// 从连接池中获取一个连接
			try 
				connection = dataSource.getConnection();
				// 将连接与当前线程绑定
				threadLocal.set(connection);
			 catch (SQLException e) 
				e.printStackTrace();
			
		
		return connection;
	

	/**
	 * 释放连接的方法
	 * 
	 * @param connection
	 */
	public static void releaseConnection() 
		// 获取当前线程中的连接
		Connection connection = threadLocal.get();
		if (connection != null) 
			try 
				connection.close();
				// 将已经关闭的连接从当前线程中移除
				threadLocal.remove();
			 catch (SQLException e) 
				e.printStackTrace();
			
		
	

4. PreparedStatement

4.1 Statement的不足

(1)SQL拼接
(2)SQL注入
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法。对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就可以了。
(3)处理Blob类型的数据
BLOB (binary large object),二进制大对象,BLOB常常是数据库中用来存储二进制文件的字段类型。
插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。

MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)

如果还是报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数:
max_allowed_packet=16M
 
注意:修改了my.ini文件,一定要重新启动服务

实际使用中根据需要存入的数据大小定义不同的BLOB类型。

需要注意的是:如果存储的文件过大,数据库的性能会下降。

4.2 PreparedStatement概述

可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象

PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句

PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值

ResultSet executeQuery()执行查询,并返回该查询生成的 ResultSet 对象。

int executeUpdate():执行更新,包括增、删、该

4.3 PreparedStatement vs Statement

代码的可读性和可维护性. Statement的sql拼接是个难题。

PreparedStatement 可以防止 SQL 注入

PreparedStatement 可以处理Blob类型的数据

PreparedStatement 能最大可能提高性能:(Oracle和PostgreSQL8是这样,但是对于MySQL不一定比Statement高)

DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。

4.4 JDBC 取得数据库自动生成的主键

获取自增长的键值:

(1)在创建PreparedStatement对象时
原来:

PreparedStatement pst = conn.preparedStatement(sql);
现在:
PreparedStatement pst = conn.prepareStatement(orderInsert,Statement.RETURN_GENERATED_KEYS);

(2)原来执行更新
原来:

int len = pst.executeUpdate();	
 现在:
int len = pst.executeUpdate();	
ResultSet rs = pst.getGeneratedKeys();
if(rs.next())
	 Object key = rs.getObject(第几列);//获取自增长的键值
	

5. 事务

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

JDBC程序中为了让多个 SQL 语句作为一个事务执行:(重点)

调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务

在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务

在其中某个操作失败或出现异常时,调用 rollback(); 方法回滚事务

若此时 Connection 没有被关闭, 则需要恢复其自动提交状态 setAutoCommit(true);

注意:

如果多个操作,每个操作使用的是自己单独的连接,则无法保证事务。即同一个事务的多个操作必须在同一个连接下.

package com.atguigu.transaction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestTransaction 
	public static void main(String[] args)
		Connection conn = null;
		try 
			//1、连接数据库
			Class.forName("com.mysql.jdbc.Driver");

			String url = "jdbc:mysql://localhost:3306/test";
			String user = "root";
			String password = "123456";
			conn = DriverManager.getConnection(url, user, password);
			//设置手动提交
			conn.setAutoCommit(false);

			String sql1 = "update t_department set description = ? where did = ?";
			PreparedStatement pst1 = conn.prepareStatement(sql1);
			pst1.setObject(1, "挣大钱的");
			pst1.setObject(2, 4);
			int len1 = pst1.executeUpdate();
			System.out.println(len1>0?"更新部门信息成功":"更新部门信息失败");
			pst1.close();
			
			String sql2 = "update t_employee set salary = salary + ? where did = ?";
			PreparedStatement pst2 = conn.prepareStatement(sql2);
			pst2.setObject(1, 20000);
			pst2.setObject(2, 4);
			int len2 = pst2.executeUpdate();
			System.out.println(len2>0?"更新部门信息成功":"更新部门信息失败");
			pst2.close();
			
			conn.commit();
		catch (Exception e) 
			try 
				if(conn!=null)
					conn.rollback();
				
			 catch (SQLException e1) 
				e1.printStackTrace();
			
		 finally
			try 
				if(conn!=null)
					//恢复自动提交
					conn.setAutoCommit(true);
					//释放连接
					conn.close();
				
			 catch (SQLException e) 
				e.printStackTrace();
			
		
	

6. 批处理

当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。

JDBC的批量处理语句包括下面两个方法:

addBatch():添加需要批量处理的SQL语句或参数
executeBatch():执行批量处理语句;
clearBatch():清空批处理包的语句

通常我们会遇到两种批量执行SQL语句的情况:

多条SQL语句的批量处理;


一个SQL语句的批量传参;


注意

JDBC连接MySQL时,如果要使用批处理功能,请再url中加参数?rewriteBatchedStatements=true

PreparedStatement作批处理插入时使用values(使用value没有效果)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

import org.junit.Test;

public class TestBatch 
	@Test
	public void noBatch()throws Exception
		Class.forName("com.mysql.jdbc.Driver");
		
		String url = "jdbc:mysql://localhost:3306/test";
		String user = "root";
		String password = "123456";
		Connection conn = DriverManager.getConnection(url, user, password);
		
		
        String sql = "INSERT INTO t_department(dname,description) VALUES(?,?)";
        PreparedStatement st = conn.prepareStatement(sql);
        
        for(int i=0; i<1000; i++)
        	st.setString(1, "测试部门" + i);
        	st.setString(2, "测试部门描述"  + i);
     

回炉重造mysql基础知识(代码片段)

MySQL知识总结学习目标1.数据库的好处2.数据库相关概念3.数据库存储数据的特点3.MySQL产品的介绍和安装3.1MySQL服务的启动和停止3.2MySQL服务的登录和退出3.3MySQL的常见命令3.4MySQL的语法规范4.SQL的语言分类4.1SQL的常见命令4.2DQL语言... 查看详情

宝藏级全网最全的pandas详细教程(2万字总结)(代码片段)

【回炉重造】Python之Pandas详细教程前言为什么要学习Pandas?什么是Pandas?1.Pandas的索引操作1.Series和DataFrame中的索引都是Index对象2.索引对象不可变,保证了数据的安全3.常见的Index种类3.1Series索引1.index指定行索引名2.行索引3.切片... 查看详情

宝藏级全网最全的pandas详细教程(2万字总结)(代码片段)

【回炉重造】Python之Pandas详细教程前言为什么要学习Pandas?什么是Pandas?1.Pandas的索引操作1.Series和DataFrame中的索引都是Index对象2.索引对象不可变,保证了数据的安全3.常见的Index种类3.1Series索引1.index指定行索引名2.行索引3.切片... 查看详情

强烈推荐一文带你搞定python基础篇(回炉重造)(代码片段)

...实现列表推导式7.2字典推导式2.1快速体验7.3集合推导式7.4总结8.函数8.1变量作用域8.2多函数 查看详情

收藏|五万字长文总结c/c++(代码片段)

...程序员链接:https://github.com/huihut/interview这是一篇五万字的C/C++知识点总结,长文预警。能滑到留 查看详情

机器学习线性回归(回炉重造)(代码片段)

机器学习---线性回归1.简单线性回归2.多元线性回归3.线性回归的正规方程解4.衡量线性回归的性能指标MSERMSEMAER-Squared5.scikit-learn线性回归实践-波斯顿房价预测LinearRegression线性回归是属于机器学习里面的监督学习,与分类问题... 查看详情

熬夜整理,五万字长文总结c/c++知识点(代码片段)

这是一篇五万字的C/C++知识点总结,长文预警。能滑到留言区的,都是麒麟臂!学C/C++的同学,收藏起来哦~C/C++知识总结目录C/C++STL数据结构算法Problems操作系统计算机网络网络编程数据库设... 查看详情

四万字长文总结多线程,一篇就够了!(代码片段)

多线程目录多线程1.认识线程及线程的创建<1>.线程的概念<2>.线程的特性<3>.线程的创建方式(1)继承Thread类(2)实现Runnable接口(3)实现Callable接口2.线程的常用方法<1>构造方法和属性的获取方法<2>常用方法(1)run()... 查看详情

四万字长文总结多线程,一篇就够了!(代码片段)

多线程目录多线程1.认识线程及线程的创建<1>.线程的概念<2>.线程的特性<3>.线程的创建方式(1)继承Thread类(2)实现Runnable接口(3)实现Callable接口2.线程的常用方法<1>构造方法和属性的获取方法<2>常用方法(1)run()... 查看详情

java万字长文基础知识总结(下)-王者笔记《收藏版》(代码片段)

上一篇Java基础知识学习总结之(上)   下一篇Java集合容器篇面试题 (上)                                          目录三、计算机原理和操作系统 内存、CPU、硬盘? Linux基本命令... 查看详情

javascript回炉重造(代码片段)

【JavaScript】回炉重造(三)循环语句1.循环语句的介绍2.for循环3.while循环4.do-while循环5.小结字符串拼接1.字符串拼接小结定时器1.定时器的介绍2.定时器的使用:3.清除定时器4.小结循环语句学习目标能够写出2种循环语... 查看详情

java万字长文基础知识总结(下)-王者笔记《收藏版》(代码片段)

上一篇Java基础知识学习总结之(上)                                             目录三、计算机原理和操作系统 内存、CPU、硬盘? Linux基本命令线程和进程的区别?四、数据库基础关系... 查看详情

回炉重造之重读windows核心编程-026-窗口消息(代码片段)

26窗口消息本章介绍Microsoft的消息系统是如何支持带有图形界面的应用程序的。首先设计Win2K以后的消息系统时,有两个主要目标:尽可能保持与过去16位Windows兼容,偏于移植现有的16位Windows程序。使窗口系统强壮,一个线程不... 查看详情

javascript回炉重造(代码片段)

【回炉重造】JavaScript函数定义和调用1.函数定义2.函数调用3.定义有参数有返回值的函数4.小结变量作用域1.变量作用域的介绍2.局部变量3.全局变量4.小结条件语句1.条件语句的介绍2.条件语句语法3.比较运算符4.逻辑运算符5.小结函... 查看详情

干货总结!kafka面试大全(万字长文,37张图,28个知识点)(代码片段)

关注并标星 Kafka面试的连环问题,保证你看完后,对Kafka有了更深层次的了解。全文总结的题目之间的关联性很强,本文将通过问答+图解的形式由浅入深帮助大家进一步学习和理解Kafka分布式流式处理平台。全文总计负责保... 查看详情

回炉重造带你搞懂什么是机器学习?(代码片段)

带你搞懂什么是机器学习?机器学习介绍什么是机器学习:机器学习应用案例:机器学习程序开发流程:1.获取数据:2.清洗数据:3.训练模型:4.测试模型:5.投入使用:开发环境:Scikit-Learn... 查看详情

回炉重造带你搞懂什么是机器学习?(代码片段)

带你搞懂什么是机器学习?机器学习介绍什么是机器学习:机器学习应用案例:机器学习程序开发流程:1.获取数据:2.清洗数据:3.训练模型:4.测试模型:5.投入使用:开发环境:Scikit-Learn... 查看详情

❤️万字长文总结❤️一篇学会redis高可用✔集群✔搭建详细教程(代码片段)

大家好,我是Lex喜欢欺负超人那个Lex擅长领域:python开发、网络安全渗透、Windows域控Exchange架构今日重点:今天总结一下Redis集群高可用的搭建流程【惊喜推荐+优质资源见文末,建议收藏!!!】建... 查看详情