JDBC的学习(01)

2023-05-16

关于JDBC的学习(1)

JDBC的简介

JDBC (Java DataBase Connectivity)是由Sun公司为简化java程序访问数据库而制定的一套面向对象的应用程序的接口。它规定了一套访问数据库的API,该API对应的类和接口都位于JDK的java.sql包中。

ps:目前我所知道的数据库有:MySQL, Oracle, SQLServer,DB2等。

JDBC和驱动的关系

我的理解:JDBC是将操作数据库的应用方法建立了一个统一的规范,即接口,但是不同的数据库,增删改查以及其他操作的代码都是不太一样的,所以各大数据库厂商,根据Java的JDBC接口编写一套专门的实现类----驱动程序。

​ 类似于我们电脑上的USB接口,当我们插入键盘,鼠标以及U盘等,我们的电脑是可以识别出来并提供相应的操作,当然识别这些设备的驱动都是在我们电脑中的。
​ 所以我们是在面向JDBC(接口)编程对数据库操作的。如下图:](https://img-blog.csdnimg.cn/20200407210235508.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NjY3NDI0,size_16,color_FFFFFF,t_70#pic_center)
​ 所以当我们使用不同的数据时,就要选用不同的驱动软件,并且还要根据数据库的版本来选择相应的驱动。
​ JDBC的体系接口是:1、面向应用程序的API 2、面向数据的API 。

JDBC的编程步骤

直接上图:
在这里插入图片描述
对上述解释一下为什么使用PrepareStatement 类,而不使用Statement类以下两处优点:
(1)可以解决Statement的拼串问题。
(2)可以解决SQL注入的问题。

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

下面我们根据步骤开始操作:

1. 导入Jar包

首先下载所需要驱动包,这里提供一个连接是专门下载MySQL的驱动,在工程下边创建一个lib的文件,将驱动文件赋值粘贴进去

https://downloads.mysql.com/archives/c-j/
在这里插入图片描述

然后右击Jar包,如下图:
在这里插入图片描述

那些报错的地方是因为我把这个库给删除又重新导入的,另外Junit4 是一个测试包,在方法上添加一个@Test注解,就可以直接运行啦,不用再写main方法了。

2、加载并注册驱动和创建Connection连接

这里提供了3种方法,分别是迭代的过程,其实可以迭代五次,这里是关键的三次:

	@Test
	/*
		这里使用的是最基础的建立连接方式,直接使用java.sql库中的Driver接口接收驱动里的Driver对象,
		然后利用Driver接口里的connect方法,对驱动里的Driver对象进行操作,以获取连接。
	*/
	public void connection01() {
		Connection conn = null;
		try {
			//1、创建Driver驱动
		    //java.sql里面提供的Driver接口  =  new 驱动软件里提供的Driver类,实例出的对象
			Driver driver = new com.mysql.cj.jdbc.Driver();
			//2、设置而连接信息
			String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
			Properties info = new Properties();
			info.setProperty("user", "javaweb");
			info.setProperty("password", "123456");
			//3、建立连接
			conn = driver.connect(url, info);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		//4、输出连接信息
		System.out.println(conn);
		//com.mysql.cj.jdbc.ConnectionImpl@279ad2e3
	}

定义的url:

这里我把我的MySQL装到了虚拟机中,所以我的主机号是我的ip,如果将mysql装到本机的是localhost。
Properties类:它是读取配置文件的类。在Java语言中,配置文件的后缀为.properties. 在这里我用他设置了用户名和密码。切记:多查一些API文档。

@Test
	public void connection02() throws Exception {
		//加载驱动的类--利用反射机制,在com.mysql.cj.jdbc.Driver类中有一个静态方法。
		//当他被加载的时候,就会自动注册该类。
		Class.forName("com.mysql.cj.jdbc.Driver");
		//这是可以省略的代码:DriverManager.registerDriver(driver);
		String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
		Properties info = new Properties();
		info.setProperty("user", "javaweb");
		info.setProperty("password", "123456");
		
		Connection conn = DriverManager.getConnection(url, info);
		System.out.println(conn);
		//com.mysql.cj.jdbc.ConnectionImpl@279ad2e3
	}

我们通过观察Driver类中的静态代码可发现,当我们加载Driver类的时候,就会执行这一个静态代码块,他就会自动的注册Driver类,所以我们可以只用Class.forName(“com.mysql.cj.jdbc.Driver”)
附一下Driver类中的static:
在这里插入图片描述

最后一种方法就是对以上两种方法的优化迭代的,优化了上面的账号密码以及平台的可移植性的问题。
因为Java代码中出现了第三方库,和账号密码都写在java文件中,修改和可移植性差,就有了一下最后一种方法,就是利用Java反射机制,以及读取配置文件。

public void connection03() throws Exception {
		//1、加载配置文件,利用类加载器获取jdbc.properties文件,返回一个输入流
		InputStream in = Connetion_JDBC.class.getClassLoader()
							       	   		 .getResourceAsStream("jdbc.properties");
		//创建一个读取配置信息的Properties的类,将InputStream以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。 
		Properties pos = new Properties();
		//从输入字节流读取属性列表(键和元素对)。 
		pos.load(in);
		//获取对应的名称的值
		String user = pos.getProperty("user");
		String password = pos.getProperty("password");
		String url = pos.getProperty("url");
		String driverClass = pos.getProperty("classname");
		
		//2、加载驱动
		Class.forName(driverClass);

		//3、建立连接
		Connection conn = DriverManager.getConnection(url, user, password);
		System.out.println(conn);
		//com.mysql.cj.jdbc.ConnectionImpl@279ad2e3
	}
3、创建PreparedStatement对象,执行SQL语句,处理结果集

在SQL语句中我们可以刻意 的分为两类:一类是增删改,另一类是查询。

  • 增删改
    1、增删改的操作是不返回结果集的,只返回一个成功或不成功的状态值或者改动的行数,所以我们不用结果集进行接收数据库返回的结果,这样反而更加简单。

    2、当我们建立连接之后,会返回一个Connection 接口的对象(conn),我们利用Connection接口中的prepareStatement(String sql) 方法创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
    3、因为在Java中不能直接运行SQL语句,所以我们将SQL语句写入到String 类型的字符串中,让preparedStatement去预编译SQL语句。

    #另外preparedStatement 可以用占位符去替换要传入的变量,这样可以优化Statement的拼串问题,减少我们写SQL语句的难度,以及出错率(马虎是很可怕的)

	@Test
	public void update() {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1、加载驱动文件设置登录信息
			Driver driver = new Driver();
			Properties info = new Properties();
			info.setProperty("user", "javaweb");
			info.setProperty("password", "971005");
				//可用connection_jdbc方法代替
			String url = "jdbc:mysql://192.168.1.10:3306/javaweb";
			//2、建立连接
			conn = driver.connect(url, info);
			System.out.println(conn);//输出一下看是否连接成功
			//3、预编译SQL语句
			String sql = "INSERT INTO dept(id, job ) VALUES (?, ?)";// (?, ?)中的?均为占位符,在后面我们要为其填充数据。
			ps = conn.prepareStatement(sql);
			//4、填充上面得占位符
			ps.setInt(1, 3);
			ps.setString(2, "数据库工程师");
			//5、提交SQL语句
			ps.execute();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//6、释放资源
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (ps != null) {
				try {
					ps.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

我们上面讲到的插入数据的代码是可以通用的,更换一下sql语句,就可以变成更新和删除的语句,到最后我把他们封装成了一个工具类,这样就可以更加方便的调用了。

  • 查询
    查询操作比较特殊的地方是,我们要将查询到的结果返回给我们的客户端。

为了提高代码的简洁性和复用性,我将连接和关闭的操作都封装到了工具类中,这样就可以直接调用静态方法,建立连接。

@Test
	public void query() {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1、建立连接
			conn = JDBCUtils.connectionMysql();			
			//2、预编译SQL语句
			String sql = "select id, job from dept where id = ?";
			ps = conn.prepareStatement(sql);
			//3、填充站位符
			ps.setObject(1, 3);
			//4、提交并返回结果集
			rs = ps.executeQuery();
			//5、对结果集进行处理
				//获取结果集的元数据;
			ResultSetMetaData rsmd = (ResultSetMetaData) rs.getMetaData();
				//通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();
			if(rs.next()) {
				int i = rs.getInt(1);
				String job = rs.getString(2);
				//Dept类是我要查询的表而设置的类,这里是利用了ORM思想。
				Dept result = new Dept(i,job, null, 0);
				System.out.println(result);
			}
		} catch (Exception e) {
				e.printStackTrace();
		}
		JDBCUtils.closeRes(conn, ps, rs);
	}

以上是更新和查询数据的两个功能实现代码,但是这仅仅是只是实现了特定的更新和查询操作,因为这两个功能实现的只是针对于这一个表的操作,我们要实现的是针对每个表都能进行操纵,以增加代码的复用性,故我们转变设计的思路,将SQL语句以及必要的信息通过参数传递给方法。下面是实现的代码:

	/*
	这是通用的更新代码,只要将SQL语句和参数传入该方法中即可,这样就可以达到更新的功能。
	*/
	
	public static int update(String sql, Object ...args) {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1、建立连接
			conn = JDBCUtils.connectionMysql();
			//2、预编译SQL语句
			ps = conn.prepareStatement(sql);
			//3、填充占位符
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}
			//4、提交语句,获取返回值
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//5、释放资源
			JDBCUtils.closeRes(conn, ps, null);
		}
		return 0;
	}
	public static <T> T query(Class<T> clazz, String sql, Object ...args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		//Dept是我要查的表建的javabean类
		Dept dept = null;
		try {
			//1、建立连接
			conn = JDBCUtils.connectionMysql();
			//2、SQL语句的预编译
			ps = conn.prepareStatement(sql);
			//3、占位符填充
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}
			//4、提交操作,并获取结果集
			rs = ps.executeQuery();
			//5、操作结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();	
			if (rs.next()) {
				T t = clazz.newInstance();
				for (int j = 0; j < rsmd.getColumnCount(); j++) {
					//获取列名
					String name = rsmd.getColumnLabel(j + 1);
					//获取结果
					Object objectValue = rs.getObject(j + 1);
					//通过反射,将获取到的结果,存入对象中
					Field field = clazz.getDeclaredField(name);
					field.setAccessible(true);
					field.set(t, objectValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeRes(conn, ps, rs);
		}
		return null;
	}

以上就是更新和查询的两个静态方法,当我们需要查询和更新的时候就可以调用这两个方法了。现在考虑一个新问题,如果要查询的结果是多条记录怎么办,我上边的查询只能查找一条记录,不能查找多条记录,所以我们要将查找的多条记录放到容器里面:
以下是实现方法:

	public static <T> List<T> query(Class<T> clazz, String sql, Object...args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		ArrayList<T> list = new ArrayList<T>();
		try {
			//1、建立连接
			conn = JDBCUtils.connectionMysql();
			//2、预编译SQL语句
			ps = conn.prepareStatement(sql);
			//3、填充占位符
			for (int i = 0; i < args.length; i ++) {
				ps.setObject(i + 1, args[i]);
			}
			//4、执行SQL语句获取结果集
			rs = ps.executeQuery();
			//5、根据结果集获取结果集的元数据
			java.sql.ResultSetMetaData rsmd = rs.getMetaData();
			int columnCount = rsmd.getColumnCount();
			list = new ArrayList<T>();
			while ( rs.next()) {
				T t = clazz.newInstance();
				for (int i = 0; i < columnCount; i ++) {
					//6、通过反射技术,动态的将获取的数据存入类中
					String columnLabel = rsmd.getColumnLabel(i + 1);
					Object objectValues = rs.getObject(i + 1);
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, objectValues);
				}
				//7、将该对象添加到容器中
				list.add(t);
			}
			//8、返回容器
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {	
			//9、释放资源
			closeRes(conn, ps, rs);
		}
		return null;
	}

总结

经过学习,JDBC的第一部分已经学习完了,这只是Java在操作数据库的一些基本实现,在真实的项目中,JDBC已经被封装到框架里面去了,但是我们要了解这些底层的实现原理。
主要体现了两种思想和两种技术:
1、面向接口的编程思想,在我们的Connection 以及其他的接口中是利用的多态原理,使接口接收了一些实现这些接口的对象,接口成为这些对象的载体,我们对接口的操作,实际上都是在这些对象上进行操作。
2、ORM思想(对象关系映射),观察我们的查询操作,我们将查询到的结果存储起来,那我们选用的存储的载体十分重要,我们查询的每一个表返回的列数和返回值都是不一样的,这样我们统一处理就会很麻烦,所以采用ORM思想,将一个表映射为一个类,将一行数据映射为一个对象,将一个字段映射为类的一个属性,故当我们查询每一个表时,附带每个表的javabean类,这样接受数据以及存储和查看,都变得十分有条理。
3、JDBC的结果集的元数据,因为结果集并不提供查询列数的方法,所以我们只能使用ResultSetMetaData这个类来获得一些我们必要的信息,网上一些人说这个类的性能不是很好,而且我学习的比较差,所以日后学到在进行总结。
4(*)、反射技术,当我们无法确定属性的名称,以及其他需要操作属性的时候,这个时候我们就要考虑一下利用反射,我理解的反射是:在java中有一种数据类型Class是专门存储类和接口的数据类型,类中有属性、构造方法、方法等,既然我们类实例出来对象,那么我们用来存储类的对象,就可以实例化该类存储的类的对象,那么我们也可以用Class来访问和处理这个实例化的对象的功能。(反射学的不扎实,嘻嘻)
主要学习到的是思考问题的方法,就是我们从先去实现一个功能为出发点,然后将其缺点用更好的方法取代,去迭代的一种方式,直到将其实现为一个工具类的方法(没那么绝对)。
第一次写博客记录一下,加油,另附上一些java的源代码。

代码链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JDBC的学习(01) 的相关文章

随机推荐

  • ProXmox VE创建虚拟机

    Proxmox VE Proxmox VirtualEnvironment 是一个非常棒的集成OPENVZ支持KVM应用的环境 有方面易用的WEB界面 xff0c 基于JAVA的UI和内核接口 xff0c 可以登录到VM客户方便的操作 xf
  • 抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15%

    背景 启动是App给用户的第一印象 xff0c 对用户体验至关重要 抖音的业务迭代迅速 xff0c 如果放任不管 xff0c 启动速度会一点点劣化 为此抖音iOS客户端团队做了大量优化工作 xff0c 除了传统的修改业务代码方式 xff0c
  • supervisor安装使用

    supervisord服务进程管理器 一 安装 yum span class token function install span supervisor 二 目录及使用 安装后 1 生成 etc supervisord conf 配置文件
  • laravel引入第三方云平台及使用方法

    一 引入第三方类 1 阿里oss span class token function composer span require jacobcyl ali oss storage 2 1 2 腾讯cos span class token f
  • 在ubuntu上安装多个版本的CUDA,并且可以随时切换

    前言 实验室工作站被多人使用导致需求不同的cuda版本 xff0c 一直没找到一个完全完整靠谱的教程 xff0c 这是我参考几个博客完成测试的全过程记录 xff0c 方便以后操作 xff0c 无任何商业用途 xff0c 如有侵权 xff0c
  • 在CentOS运行APPIMAGE文件时提示AppImages require FUSE to run

    输入如下命令行 xff1a yum enablerepo 61 epel y install fuse sshfs install from EPEL user 61 w h o a m
  • C++中的虚函数详解

    虚函数在运行时绑定 xff0c 即运行时确定执行的函数 所谓的后期绑定就是一个基类中有一个虚函数 xff0c 而派生类中重写了这个函数 xff0c 那么调用的这个虚函数的时候根据类的实例的不同而调用不同的函数 实例化是指在面向对象的编程中
  • Android中的Log输出

    安卓系统的代码量十分庞大 xff0c 无法使用Jlink之类的工具进行单步调试 xff0c 因而日志系统十分有必要 为此 xff0c 安卓开发了Log日志类用于开发者的日常调试使用 Log中配置了五种Log类 xff0c 分别表示打印五类不
  • C++中的模板类

    什么是模板类 xff1a C 43 43 发展到一定层次后 xff0c 必然有部分代码存在仅传参的数据类型不同而内容一致的情况 因此 xff0c C 43 43 为解决此类场景 xff0c 特推出模板类的使用 模板类是一种泛型技术 xff0
  • 【C/C++】指针赋初值问题

    先说结论 xff1a 指针在使用时一定要进行初始化 一 问题来源 问题提示 xff1a null pointer dereference 此问题在编译时无误 xff0c 在运行 时出的问题 xff0c 排查起来非常头疼 一开始没头绪 xff
  • 【Linux应用编程基础】ioctl函数

    一 为什么需要ioctl函数 在裸机开发中 xff0c 应用程序直接作用于寄存器 而在Linux开发中Linux驱动与Linux应用在代码层级上是分开的 xff0c 在实际开发过程中也是分两拨人开发的 xff0c Linux驱动服务于应用
  • 【半音阶口琴】基础汇总

    一 简谱初学习 增时线 xff1a 2 后面的三个杠表示前面音符的续音 减时线 xff1a 121 表示三个音符的时值都减半 附点 xff1a 1 39 2 结合性为先结合减时线 xff0c 再结合附点 xff0c 因此1表示3 4拍 四分
  • 抖音包大小优化-资源优化

    1 概述 随着业务的快速迭代 xff0c 抖音 Android 端的包大小爆发式增长 包大小直接影响到下载转化率 推广成本 运行内存和安装时间等因素 xff0c 因此对 apk 进行瘦身是一件很有必要且收益很大的事情 apk 主要由 dex
  • 【正点原子Linux C应用编程指南】学习里程碑总结

    前言 说实话 xff0c 当阅读正点原子的这篇文档时 xff0c 带着些许失望 xff0c 并不是文档写的不好 xff0c 而是质量明显不如RTOS和裸机部分的编写 xff0c 可能是没有实践过的原因 记得朱有鹏老师之前说过 xff0c 正
  • 【上位机应用开发】Python篇 A部分

    虽是嵌入式方向 xff0c 刚毕业时一直想基于C 开发一款上位机 xff0c 没争取到 xff0c 近期偶然获取到一个用python开发的宝贵机会 xff0c 记录一下 1 用Shell命令行 VS code还是集成开发环境开发的问题 xf
  • 挂载时系统提示未找到fdisk指令

    64 通俗的讲解如何在Ubuntu系统上挂在 在学习的过程中 xff0c 总结了简介的挂在 xff0c 平时一直使用的记事本记载的 xff0c 以后会慢慢记录在上面 Ubuntu14 0 04版本虚拟机挂在U盘如何操作 先让虚拟机读取到物理
  • 微信聊天记录导出

    本文将介绍如何将微信聊天记录导出为结构化数据 所需材料 一部已经root的安卓手机带GPU的Linux电脑 整体流程如下 xff1a 数据库文件提取 使用RE管理器 xff0c 在已经root的安卓手机上 xff0c 找到如下文件 xff1
  • 详解vim插件安装及配置

    前言 vim插件依赖项安装 xff1a dnf install python future dnf install python devel dnf install ncurses dnf install cmake dnf install
  • SubliemText_3配置Java的编译环境

    Sublime Text 3 添加Java环境 最近在学习Java语言 xff0c 因为写一些基础的代码练习 xff0c 而使用sublime编辑器写代码 xff0c 因为每次运行都要在命令行执行 javac 和 java 代码 xff0c
  • JDBC的学习(01)

    关于JDBC的学习 1 JDBC的简介 JDBC Java DataBase Connectivity 是由Sun公司为简化java程序访问数据库而制定的一套面向对象的应用程序的接口 它规定了一套访问数据库的API xff0c 该API对应