文章目录
Ⅰ、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)仅学习记录,如果有错误,还望指正