Java代码操作数据库(上)——JDBC(JDBC、JUnit、PreparedStatement) && 练习

Ⅰ、JDBC简介

一、定义
Java DataBase Connectivity :Java数据库连接技术,它是一个规范,不是一个实现。即,通过java代码对数据库进行操作管理
sun公司提供的一套连接数据库的规范(接口JDBC API),各个数据库厂商就会遵守这套规范,提供一套访问自己公司数据库产品的程序,这套程序称为数据库驱动(JDBC)。即,提供一套规则(JDBC),每个数据库公司都得遵守这套规则

CRUD:增删改查

二、JDBC常用的接口和类

DriverManager类:驱动管理类,用于:注册JDBC驱动
Connection接口:连接对象,用于:与数据库建立连接
Satement接口:SQL编译器,用于:将SQL语句发送到数据库,返回执行结果
	PreparedStatement子接口
ResultSet接口:结果集对象,用于:执行查询操作时,接收结果(将查询结果,封装起来)

Ⅱ、JDBC入门

1、下载驱动
什么数据库就去什么官网找驱动.比如这里使用MySQL
https://dev.mysql.com/downloads/connector/j/
在这里插入图片描述
2、导入到编辑器(eclipse),即一般新建一个lib文件夹,把.jar文件放入其中
在这里插入图片描述
3、添加生成路径
在这里插入图片描述
在这里插入图片描述
4、初·代码

步骤:
1、注册驱动。8.0之后自动注册,可以不写
2、创建连接对象
3、创建SQL编译器
4、执行sql语句,并返回结果集
5、释放资源
package com.dbc.hxh;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Demo_jdbc_first {

	public static void main(String[] args) {
		//定义在外面,是为了,释放资源时也能访问(变量的访问范围问题)
		Connection conn =  null;
		Statement stm = null;
		ResultSet res = null;
		try {
			//1、注册驱动(全类名)
			Class.forName("com.mysql.cj.jdbc.Driver");
			//2、创建连接对象
			String url = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useUnicode=true";
			String user = "root";
			String pwd = "123456";
			conn = DriverManager.getConnection(url,user,pwd);
			//3、创建SQL编译器
			stm = conn.createStatement();
			//4、执行sql语句,并返回结果集
			String sql = "select * from emp";
			res = stm.executeQuery(sql);
			while(res.next()) {
				System.out.println(res.getString("ename"));
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {//不管前面是否出现异常,始终需要释放资源,不然浪费内存资源
			try {
				if(res !=null)//避免空指针异常
					res.close();
				if(stm !=null)
					stm.close();
				if(conn !=null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		
		}
	}

}

Ⅲ、JDBC详解0

1、注册驱动
Class.forName(“com.mysql.cj.jdbc.Driver”);使用全类名将Driver类加载到JVM,完成该类的初始化工作
在这里插入图片描述
2、创建连接对象

String url = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useUnicode=true";
String user = "root";
String pwd = "123456";
conn = DriverManager.getConnection(url,user,pwd);

1)【url】

jdbc:当前连接数据库使用的是jdbc连接环境

mysql:使用jdbc连接的是MySQL数据库

localhost:3306:MySQL服务器地址和端口号(使用默认地址和端口号时,可以省略)

test:MySQL中的数据库名

?:分隔连接字符串和其他参数

&:多个参数之间使用&连接

serverTimezone——时区问题
	serverTimezone=GMT%2B8北京时间(东八区)
	serverTimezone=Asia/Shanghai上海时间
	serverTimezone=UTC世界标准时间(UTC时区),比北京晚8个小时
	
	
characterEncoding=UTF-8&useUnicode=true":解决乱码问题

2)user:数据库登录名
3)pwd:数据库登录密码

3、创建SQL编译器
Statement stm = conn.createStatement();

4、执行SQL语句并返回结果
常用两个方法:
1)增删改——int executeUpdate(String sql):sql是指sql语句,方法返回值为sql执行后影响的行数
2)查询——ResultSet executeQuery(String sql):返回结果集

对结果集处理常用的方法
1)boolean next():将光标从当前位置向下移动一行,如果有可读数据返回true,否则返回false

2)××× get×××(int columnindex/String cloumnLabel):获取对应的值
	×××:获取的数据是什么类型的,就写什么类型。比如,获取整型,int getInt(…)
	int columnindex:列的顺序号。比如,name在第二列,就写2
	String cloumnLabel:列的列名,推荐

5、释放资源
从后往前关(先申请的后关)

Ⅳ、单元测试(Junit)

1、简述及导包

JUnit(eclipse自带)单元测试是为了能够在一个类创建多个可执行的方法,每个方法都在独立执行,可以将每个方法都当作mian方法看待。使用注解@Test,一类中可以有多个@Test,但是不要将类名写成Test,不然会冲突。
在这里插入图片描述
2、代码

package com.dbc.hxh;

import org.junit.Test;

public class DemoJUnit {
	@Test
	public void print() {
		System.out.println("测试JUnit");
	}

}

3、执行
在这里插入图片描述
4、规范0
1)单元测试的修饰符必须是public
2)方法不能有返回值
3)方法参数必须为空
4)不要忘记添加@Test注解(注意每个方法都需加@Test)

Ⅵ、JDBC实现CRUD

使用单元测试(JUnit)实现增删改查(CRUD)

下方代码为增加insert

package com.dbc.hxh;

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

import org.junit.Test;

public class DemoJUnit {
	//增加一条数据
	@Test
	public void insert() {
		Connection conn = null;
		Statement stm = null;
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/offcnoa?serverTimezone=GMT%2B8&characterEncoding=utf-8&useUnicode=true","root","123456");
			stm = conn.createStatement();
			int a = stm.executeUpdate("insert into emp(empno,ename)values('8080','KINGH')");
			System.out.println("受影响行数" + a);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(stm !=null)
					stm.close();
				if(conn !=null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

Ⅶ、SQL注入

上方的JDBC实现CRUD时,涉及字符串的拼接。比如:String sql = “select * from emp where ename=” + name(键盘输入的值,用name变量存储)。那么如果此时用户输入 ‘1’='1’这类一个恒等的值,查询的就是全表,比较危险。这类情况称为SQL注入。

解决方法:
1)不直接使用字符拼接
2)不使用Statement
3)使用PreparedStatement

Ⅷ、PreparedStatement0

一、解释:
PreparedStatement位于java.sql包中的接口,是Statement的子接口,为了解决上述情况发生。PreparedStatement对象会将SQL语句进行预编译,通过?占位进行SQL语句的拼接,避免将变量中的内容当成SQL关键字参与编译,从而解决SQL注入问题。

预编译如何解决SQL注入的?
举例说明:将传来的值 Tom or 1=1,使用单引号包起来’Tom or 1=1’,从而解决or关键字带来的影响。另外,单引号,使用的是转义之后的单引号\'

二、说明、方法0

1、将原本的Statement接口,换成子接口PreparedStatement
2、创建PreparedStatement对象,使用prepareStatement()传递sql语句,进行预编译
3、sql语句中,需要将用户输入的数据添加到sql中的,用英文状态下的?占位(不要给?打引号,不然当作字符了)

4、使用set×××(parameterIndex,×××)方法完成?参数的赋值操作

set×××:参数类型(比如,setString)
parameterIndex:第几个问号,从1开始
×××:给?赋值的参数值

5、因为已经赋值并且预编译了,所以使用PreparedStatement调用executeQuery方法执行sql语句时,不要再传入参数

三、好处:

可读性、维护性更高,虽然语句多了几句
效率高(预编译)
避免注入问题,提高安全性

四、代码,👇👇

Ⅸ、PreparedStatement的CRUD

查询例举

package com.dbc.hxh;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

import org.junit.Test;

public class DemoPrepared {

	@Test
	public void select() {
		System.out.println("请输入带查询的人的姓名:");
		String name = new Scanner(System.in).nextLine();
		Connection conn = null;
		PreparedStatement  prst = null;
		ResultSet rs = null;
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql:///offcnoa?characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8", "root", "123456");
			prst = conn.prepareStatement("select * from emp where ename = ?");
			//第一个问号,参数为name变量中的值
			prst.setString(1, name);
			rs = prst.executeQuery();
			while(rs.next()) {
				//查询符合条件的数据,只取姓名,职位
				System.out.println(rs.getString("ename") + "," + rs.getString("job"));
			}	
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			try {
				if(rs !=null)
					rs.close();
				if(prst !=null)
					prst.close();
				if(conn !=null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	
		
	}

}

Ⅹ、JDBC的工具类的封装

一、第0版

将一些常用的、共有的内容,封装到一个类中

1、封装到类中

package com.dbc.hxh;

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

public class DemoSeal{
	//连接
	public static Connection connect() {
		Connection conn = null;
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			String url = "jdbc:mysql:///offcnoa?characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8";
			String username = "root";
			String password = "123456";
			conn = DriverManager.getConnection(url, username,password);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	//关闭
	public static void closeAll(ResultSet rs,PreparedStatement prst,Connection conn) {
		try {
			if(rs !=null)
				rs.close();
			if(prst !=null)
				prst.close();
			if(conn !=null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
}

2、使用

@Test
	public void select() {
		System.out.println("请输入带查询的人的姓名:");
		String name = new Scanner(System.in).nextLine();
		Connection conn = DemoSeal.connect();
		PreparedStatement  prst = null;
		ResultSet rs = null;
		try {
			prst = conn.prepareStatement("select * from emp where ename = ?");
			prst.setString(1, name);
			rs = prst.executeQuery();
			while(rs.next()) {
				//查询复合条件的数据,只取姓名,职位
				System.out.println(rs.getString("ename") + "," + rs.getString("job"));
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DemoSeal.closeAll(rs, prst, conn);
		}
		
		
		
	}

二、【第1版】

使用配置文件properties

1、创建一个jdbc.properties配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///offcnoa?characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8
username=root
password=123456

2、封装(读取配置文件,连接数据库以及释放资源)

package com.dbc.hxh;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class UseProperties{
	private static final String DRIVER;
	private static final String URL;
	private static final String USERNAME;
	private static final String PASSWORD;
	
	//读取配置文件(只需要加载一次即可)
	static{
		Properties pro = new Properties();
		try {
			//相对路径,读取配置文件——此处放在src下面的
			pro.load(new FileInputStream("src/jdbc.properties"));
		} catch (Exception e) {
			// TODO: handle exception
		}
		DRIVER = pro.getProperty("driver");
		URL = pro.getProperty("url");
		USERNAME = pro.getProperty("username");
		PASSWORD = pro.getProperty("password");
	}
	//注册JDBC驱动
	static {
		try {
			Class.forName(DRIVER);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//创建连接对象connection
	public static Connection connect0() {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(URL,USERNAME,PASSWORD);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	//释放资源(没有结果集ResultSet 时,传个null值过来即可。重载closeAll也可)
	public static void colseAll0(ResultSet rs,PreparedStatement prst,Connection conn) {
		try {
			if(rs !=null)
				rs.close();
			if(prst !=null)
				prst.close();
			if(conn !=null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	
}

3、使用

package com.dbc.hxh;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

import org.junit.Test;

public class Demo_Test_Properties {

	@Test
	public void delete() {
		System.out.println("请输入待删除的人的名字");
		String name = new Scanner(System.in).nextLine();
		Connection conn = UseProperties.connect0();
		PreparedStatement prst = null;
		try {
			prst = conn.prepareStatement("delete from emp where ename=?");
			prst.setString(1, name);
			int i = prst.executeUpdate();
			System.out.println(i + "行受影响");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			UseProperties.colseAll0(null, prst, conn);
		}
		
		
	}

}

Ⅺ练习

题目:

1)插入至少10条不同的学生信息到表中
2)查询出班上所有的女生的信息,并打印到控制台
3)查询出班上年龄大于21岁的男同学的姓名,并打印到控制台
4)根据学号删除某同学
5)对某同学的信息进行修改

在这里插入图片描述

步骤一:

创建配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///school?characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8
user=root
pwd=123456

步骤二:

将一些经常使用,但是重复的代码封装,形成工具类

package com.day08.homework;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class Sael {

	private static final String DRIVER;
	private static final String URL;
	private static final String USER;
	private static final String PWD;
	//读取配置文件,只执行一次,用静态代码块
	static {
		Properties pro = new Properties();
		try {
			pro.load(new FileInputStream("src/jdbc.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		//注意加引号
		DRIVER = pro.getProperty("driver");
		URL = pro.getProperty("url");
		USER = pro.getProperty("user");
		PWD = pro.getProperty("pwd");
	}
	//注册JDBC,只执行一次,用静态代码块
	static {
		try {
			Class.forName(DRIVER);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//创建连接对象
	public static Connection connect() {
		Connection conn = null;
		try {
			conn = DriverManager.getConnection(URL,USER,PWD);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	//关闭
	public static void closeAll(ResultSet rs,PreparedStatement prst,Connection conn) {
		try {
			if(rs !=null)
				rs.close();
			if(prst !=null)
				prst.close();
			if(conn !=null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	

}

步骤三:

使用PreparedStatement进行CRUD

package com.day08.homework;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

import org.junit.Test;

public class TestProperties {
	//创建连接连接对象
	Connection conn = Sael.connect();
	
	//1)插入至少10条不同的学生信息到表中
	@Test
	public void insert() {
		PreparedStatement prst = null;
		String insertSql = "INSERT INTO student(sname,sage,ssex,semail)\r\n" + 
				"	VALUES('张三','17','男','110@qq.com'),\r\n" + 
				"	('张三','17','男','110@qq.com'),\r\n" + 
				"	('李四','17','女','110@qq.com'),\r\n" + 
				"	('王五','17','男','110@qq.com'),\r\n" + 
				"	('赵六','17','男','110@qq.com'),\r\n" + 
				"	('田七','17','男','110@qq.com'),\r\n" + 
				"	('徐八','17','女','120@qq.com'),\r\n" + 
				"	('韩九','17','女','130@qq.com'),\r\n" + 
				"	('潘十','17','女','140@qq.com'),\r\n" + 
				"	('蒋十一','17','女','160@qq.com'),\r\n" + 
				"	('唐十二','17','男','190@qq.com'),\r\n" + 
				"	('萧十三','17','男','100@qq.com')";
		try {
			prst = conn.prepareStatement(insertSql);
			int inserCount = prst.executeUpdate();
			System.out.println(inserCount + "行受影响");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			Sael.closeAll(null, prst, conn);
		}
		
	}
	
	//2)查询出班上所有的女生的信息,并打印到控制台
	@Test
	public void selectGril() {
		PreparedStatement prst = null;
		ResultSet rs = null;
		String selectGrilSql = "SELECT * FROM student WHERE ssex = '女'";
		try {
			prst = conn.prepareStatement(selectGrilSql);
			rs = prst.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getString("sname") + "\t" + rs.getInt("sage") + "\t" + rs.getString("ssex") + "\t" + rs.getString("semail"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			Sael.closeAll(rs, prst, conn);
		}
		
	}
	
	//3)查询出班上年龄大于21岁的男同学的姓名,并打印到控制台
	@Test
	public void selectAgeGt21() {
		PreparedStatement prst = null;
		ResultSet rs = null;
		String selectAgeGt21Sql = "SELECT * FROM student WHERE sage >21 AND ssex = '男' ";
		try {
			prst = conn.prepareStatement(selectAgeGt21Sql);
			rs = prst.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getInt(1) + "\t" + rs.getString("sname") + "\t" + rs.getInt("sage") + "\t" + rs.getString("ssex") + "\t" + rs.getString("semail"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			Sael.closeAll(rs, prst, conn);
		}
		
	}
	
	//4)根据学号删除某同学
	@SuppressWarnings("resource")
	@Test
	public void deleteSid() {
		System.out.println("请输入待删除同学的学号");
		int sid = new Scanner(System.in).nextInt();
		PreparedStatement prst = null;
		String deleteSidSql = "DELETE FROM student WHERE sid = ?";
		try {
			prst = conn.prepareStatement(deleteSidSql);
			prst.setInt(1, sid);
			int count = prst.executeUpdate();
			System.out.println(count + "行受影响,删除成功");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			Sael.closeAll(null, prst, conn);
		}
		
	}
	
	//5)对某同学的信息进行修改(为了测试避免SQL注入的功能,画蛇添足)
	@SuppressWarnings("resource")
	@Test
	public void updateName() {
		PreparedStatement prst = null;
		System.out.println("请输入待修改信息的同学的名字");
		String name = new Scanner(System.in).nextLine();
		System.out.println("请输入他(她)的年龄");
		int age = new Scanner(System.in).nextInt();
		System.out.println("请输入他(她)的性别");
		String sex = new Scanner(System.in).nextLine();
		System.out.println("请输入他(她)的邮箱");
		String email = new Scanner(System.in).nextLine();
		String updateNameSql = "UPDATE student SET sage = ?,ssex = ?,semail = ? WHERE sname = ? ";
		try {
			prst = conn.prepareStatement(updateNameSql);
			prst.setInt(1, age);
			prst.setString(2, sex);
			prst.setString(3, email);
			prst.setString(4, name);
			int count = prst.executeUpdate();
			System.out.println(count + "行受影响,修改成功");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			Sael.closeAll(null, prst, conn);
		}
		
	}

}

练习发现的注意

1、配置文件中不要写任何多余的字符(空格、分号、引号之类)
2、进行查询操作时,才会返回结果集(释放资源时判断了是否为空,如果没有使用结果集,参数给个null即可)
3、预执行增删改的sql语句的方法一样,都是executeUpdate;查询是executeQuery
4、释放资源时,记得倒叙(先创建的对象,后关闭)
5、使用JUnit时,每个方法都需要加@Test,及其注意事项



PS:
1)目录加0表示有需要注意的点
2)【】表示重点
3)删除线表示已解决
4)仅学习记录,如果有错误,还望指正

Java代码操作数据库(下)

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:黑客帝国 设计师:我叫白小胖 返回首页

打赏作者

Today_He

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值