JDBC简介
- JDBC 全称为Java DataBase Connectivity(Java语言连接数据库)
- JDBC是SUN公司制定的一套接口(interface)。
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程
- JDBC之所以要面向接口编程,是因为解耦合:降低程序的耦合度,提高程序的扩展力。多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
- 因为每一个数据库产品都有自己独特的实现原理,所以不同数据库产品之间需要一个类似桥梁的JDBC接口用来实现操作数据库
- JDBC编程六步
- 注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
- 获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)
- 获取数据库操作对象(专门执行sql语句的对象)
- 执行SQL语句(DQL,DML…)
- 处理查询结果集 (只有当第四步执行的是select语句的时候,才有本步)
- 释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
IDEA编写JDBC连接MySQL
mysql-connector-java的不同版本对比
MySQL Connector / J 5.1 5.1版本支持java5及其以上的版本,支持5.6、5.7、8.0版本的mysql数据库,支持3.0、4.0、4.1、4.2版本的jdbc。在5.1中,Driver的实现类的全路径名是com.mysql.jdbc.Driver。
MySQL Connector / J 8.0 8.0版本支持java8及其以上的版本,支持5.6、5.7、8.0版本的mysql数据库,支持4.2版本的jdbc。在8.0中,Driver的实现类的全路径名是com.mysql.cj.jdbc.Driver。
下图是官网上mysql-connector-java的版本对应的mysql版本和jdk的版本。
下载驱动jar包 mysql-connector-java
要使用mysql连接器,就要先下载它。如果是一般的项目,那我们需要下载jar包,然后放到项目的lib目录下。如果使用maven构建的项目,我们可以通过maven直接安装。不同的下载方式有不同的操作,常见的有直接官网下载和maven下载(下周讲解maven)。 下载jar包最直接的方式是从官网下载,官网地址是:https://dev.mysql.com/downloads/connector/j/。直接点链接进入mysql官网,选择所需的版本和操作系统(要下载jar包就要选:Platform Independent),然后点击download按钮就可以下载了。为java提供的连接器是Connector / J,也就是mysql-connector-java,它分为5.1版本和8.0版本。Connector / J实现了JDBC,为使用java开发的程序提供连接,方便java程序操作数据库。
2、从maven安装 使用maven安装mysql-connector-java就简单很多,直接打开maven的中央仓库地址,输入mysql-connector-java就可以找到不同版本的依赖。地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java
6.3 IDEA导入jar包
先检查jar包位置:在目录里新建一个文件夹 libs,把jar包复制进去
然后右键选择新建文件夹libs转成library
然后就可以写代码啦!
根据六步编写代码:先不写处理代码
6.4 JDBC连接mysql 程序编写
6.5.1 第一种注册驱动方式
package com.shujia.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import com.mysql.jdbc.Driver;
/*
1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
2.获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)。
3.获取数据库操作对象(专门执行sql语句的对象)
4.执行SQL语句(DQL,DML…)
5.处理查询结果集 (只有当第四步执行的是select语句的时候,才有本步)
6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
工具类:DriverManager
没有构造方法(构造方法私有化),方法是静态的。
*/
public class JDBCTest1 {
private static Statement stat;
private static Connection conn;
public static void main(String[] args) {
//1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
//第一种注册驱动的方式
//jdk自己提供了一个工具类DriverManager给我们使用,其中有一个静态的方法registerDriver()可以实现注册驱动功能
//static void registerDriver(Driver driver)
//注册与给定的驱动程序 DriverManager 。
//而jdkapi文档中发现,Driver是一个接口,而接口是不能直接实例化的
//这里要想传入一个Driver的对象,就必须找到一个对应的实现类
//我们现在是要使用mysql的驱动,所以就猜测mysql的驱动包中应该有一个类实现了jdk提供的Driver接口
try {
DriverManager.registerDriver(new Driver());
//2.获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)。
//jdk自身提供的工具类DriverManager中有一个静态方法,可以让我们调用并且获取与数据库的连接
//static Connection getConnection(String url, String user, String password)
//尝试建立与给定数据库URL的连接。
/**
* url: 统一资源定位系统
* http/https: 通过网络请求去访问网络上的资源
* jdbc:mysql: 是驱动提供的请求头
* 请求的地址: 指定mysql的服务器地址:192.168.254.150
* 端口号: 3306
* useUnicode=true&characterCharset=utf8
*
* jdbc:mysql://192.168.254.150:3306?useUnicode=true&characterCharset=utf8
*
* user: 用户名 root
* password: 密码 123456
*/
//在mysql5.7+版本不建议使用连接直接访问服务器
String url = "jdbc:mysql://192.168.254.150:3306/bigdata19?useUnicode=true&characterEncoding=utf8&useSSL=false";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
System.out.println("与mysql建立连接成功:"+conn); //[email protected]
//3.获取数据库操作对象(专门执行sql语句的对象)
stat = conn.createStatement();
//4.执行SQL语句(DQL,DML…)
//DQL
//ResultSet executeQuery(String sql)
//执行给定的SQL语句,该语句返回单个 ResultSet对象。
//DML
//int executeUpdate(String sql)
//执行给定的SQL语句,这可能是 INSERT , UPDATE ,或 DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。
// 方法的返回值指的是受影响的行数: Affected rows: 1
int count = stat.executeUpdate("insert into dept(deptno,dname,loc) values(70,'教学部','安徽合肥')");
if(count==1){
System.out.println("数据保存成功!!");
}else {
System.out.println("数据保存失败!!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源的代码
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
6.5.2 第二种注册驱动方式
package com.shujia.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/*
注册驱动的第二种方式:
正常情况下,我们今后遇到的数据库肯定不止mysql一种,但是呢,使用java连接数据库的方式却大同小异,无非是内部的实现不一样
如果按照上午所说的那样注册驱动的话,上午的程序案例只能连接mysql的驱动,将来如果想换成sqlserver的话还不太好换
想一想,我们之前在安装jdk的过程在配置环境变量的时候,有一步我们可以借鉴它的思想
所以,我们在这里注册驱动的时候,能不能传入字符串呢?
注册驱动底层实际上就是用驱动包中对应的类,而驱动包中的类都是class后缀的编译好的文件
要想直接使用编译好的类,使用反射来实现。
想一想,反射是如何获取一个类的class文件对象呢?3种方式
1、getClass
2、.class
3、Class.forName()
*/
public class JDBCTest2 {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
//1、加载驱动
//第二种方式来实现
Class.forName("com.mysql.jdbc.Driver");
//2、创建与数据库的连接对象
String url = "jdbc:mysql://192.168.254.150:3306/bigdata19?useUnicode=true&characterEncoding=utf8&useSSL=False";
String username = "root";
String password = "123456";
conn = DriverManager.getConnection(url, username, password);
//3、获取与操作数据库的对象
stat = conn.createStatement();
//4、编写sql语句并执行
//增
// String sql = "insert into students(sno,sname,sgender,sage,sclass) values('110','小虎','男',18,'19独立营')";
// int count = stat.executeUpdate(sql);
// System.out.println(count==1?"数据保存成功!!":"数据保存失败!!");
//改
// String sql3 = "update students set sname='李凯迪' where sno='110'";
// int count3 = stat.executeUpdate(sql3);
// System.out.println(count3==1?"数据更新成功!!":"数据更新失败!!");
//
//删
String sql2 = "delete from students where sno='110'";
int count2 = stat.executeUpdate(sql2);
System.out.println(count2==1?"数据删除成功!!":"数据删除失败!!");
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放资源
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
6.5.3 将连接数据库的所有信息配置到配置文件中
package com.shujia.jdbc;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
/*
使用配置文件将连接数据库的相关参数进行保存,然后在代码中使用
使用jdk自身提供的Properties类来加载配置文件
IO流:
字节流
字节输入流
字节输出流
字符流 = 字符转换流 = 字节流+编码表
字符输入流
字符输出流
相对路径:将项目作为根目录
绝对路径/完整路径:带盘符的路径
*/
public class JDBCTest3 {
public static void main(String[] args) throws Exception {
//使用无参构造方法创建Properties类的对象
Properties prop = new Properties();
//使用字符流读取配置文件
// InputStreamReader , FileReader, BufferedReader
BufferedReader br = new BufferedReader(new FileReader("E://projects//IdeaProjects//bigdata19-mysql//resource//jdbc.properties"));
prop.load(br);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//加载驱动
Class.forName(driver);
//获取连接对象
Connection conn = DriverManager.getConnection(url, username, password);
//获取数据库操作对象
Statement stat = conn.createStatement();
//执行sql语句
int count = stat.executeUpdate("insert into dept(deptno,dname,loc) values(80,'管理层','安徽合肥')");
System.out.println(count==1?"数据插入成功!!":"数据插入失败!!");
//释放资源
stat.close();
conn.close();
}
}
6.5.4 处理查询结果集
package com.shujia.jdbc;
import com.shujia.utils.MySqlTool;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
/*
使用jdbc查询数据库中的数据
*/
public class JDBCTest5 {
public static void main(String[] args) throws Exception {
//获取与数据库的连接对象
//使用工具类的方式进行获取
Connection conn = MySqlTool.init();
//获取数据库操作对象
Statement stat = conn.createStatement();
//需求:查询dept表中的所有数据
//查询sql语句中,把要查询的列进行罗列出来
ResultSet resultSet = stat.executeQuery("select deptno as a1,dname,loc from dept");
//boolean next()
//将光标从当前位置向前移动一行。
//这里的光标可以理解为集合中的光标
// boolean b = resultSet.next();
// //如果光标移动的位置有值,这里的返回值是true
// if(b){
// //获取一行中每一列的数值,第一种方式
// //String getString(int columnIndex)
// //这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。
// //columnIndex从1开始,1表示一行中的第一列,以此类推
// String deptno = resultSet.getString(1);
// String dname = resultSet.getString(2);
// String loc = resultSet.getString(3);
// System.out.println("部门编号:"+deptno+",部门名称:"+dname+",地址:"+loc);
// }
//正常情况下,一张结果表的行数我们不确定,但是我们知道,当next的结果是false的时候,表示读取到末尾
//使用while循环
while (resultSet.next()) {
// //获取一行中每一列的数值,第一种方式
// //String getString(int columnIndex)
// //这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。
// //columnIndex从1开始,1表示一行中的第一列,以此类推
// String deptno = resultSet.getString(1);
// String dname = resultSet.getString(2);
// String loc = resultSet.getString(3);
// System.out.println("部门编号:" + deptno + ",部门名称:" + dname + ",地址:" + loc);
//第二种方式:根据列名获取对应的列值
//String getString(String columnLabel)
//这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。
//注意事项:当查询sql语句中存在起别名的情况时,通过列名获取列值的时候,使用名字是别名
String deptno = resultSet.getString("a1");
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc");
System.out.println("部门编号:" + deptno + ",部门名称:" + dname + ",地址:" + loc);
}
System.out.println("==================================================================");
//注意事项2:同一个结果resultSet对象,只能遍历一次
// while (resultSet.next()) {
//// //获取一行中每一列的数值,第一种方式
//// //String getString(int columnIndex)
//// //这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。
//// //columnIndex从1开始,1表示一行中的第一列,以此类推
//// String deptno = resultSet.getString(1);
//// String dname = resultSet.getString(2);
//// String loc = resultSet.getString(3);
//// System.out.println("部门编号:" + deptno + ",部门名称:" + dname + ",地址:" + loc);
//
// //第二种方式:根据列名获取对应的列值
// //String getString(String columnLabel)
// //这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。
// //注意事项:当查询sql语句中存在起别名的情况时,通过列名获取列值的时候,使用名字是别名
// String deptno = resultSet.getString("a1");
// String dname = resultSet.getString("dname");
// String loc = resultSet.getString("loc");
// System.out.println("部门编号:" + deptno + ",部门名称:" + dname + ",地址:" + loc);
// }
//释放资源
stat.close();
conn.close();
}
}
登录注册案例
package com.shujia.jdbc;
/*
使用JDBC模拟登录注册案例
1、注册账号:往数据库插入一条数据
2、登录账号:查询数据库
实现步骤:
1、建表
*/
import com.shujia.utils.MySqlTool;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
import java.util.UUID;
public class JDBCTest6 {
public static void main(String[] args) throws Exception {
System.out.println("欢迎进入小虎婚介所");
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
//使用工具类获取与数据库的连接对象
Connection conn = MySqlTool.init();
//获取数据库操作对象
Statement stat = conn.createStatement();
//编写sql语句
String sql = "select username,password from user where username='"+username+"' and password='"+password+"'";
// select username,password from user where username='root' and password='123456'
//执行sql语句
ResultSet rs = stat.executeQuery(sql);
boolean b = rs.next();
if(b){
//如果b为true表示,数据库中存在该用户,登录成功
System.out.println("登录成功!!欢迎进入小虎婚介所!!");
}else {
//如果b为false,表示该用户还没有注册,需要先进行注册
System.out.println("您还未注册,请先注册(Y/N)");
String choice = sc.next();
if("Y".equals(choice)){
while (true){
System.out.println("请输入您的用户名:");
String new_username = sc.next();
System.out.println("请输入您设置的密码:");
String new_password = sc.next();
System.out.println("请确认密码:");
String password_again = sc.next();
if(password_again.equals(new_password)){
UUID uuid = UUID.randomUUID();
String new_uuid = uuid+"-"+System.currentTimeMillis();
String sql2 = "insert into user(uid,username,password) values('"+new_uuid+"','"+new_username+"','"+new_password+"')";
int count = stat.executeUpdate(sql2);
if(count==1){
System.out.println("用户注册成功!!!");
}else {
System.out.println("用户注册失败!!!");
}
break;
}else {
System.out.println("两次密码不一致!!请重新输入");
}
}
}
}
//释放资源
stat.close();
conn.close();
}
}
6.5.6 解决sql注入问题
package com.shujia.jdbc;
/*
使用JDBC模拟登录注册案例
1、注册账号:往数据库插入一条数据
2、登录账号:查询数据库
实现步骤:
1、建表
1234' or '1'='1
我们写完程序后发现,当我们将密码输入成1234' or '1'='1这个样子的时候,发现登录成功
原因:sql在编译的时候,会将我们输入的内容存在关键字or,会把or当作sql语法关键字进行执行
如何解决呢?
这样的问题,称之为sql注入的问题
解决的思路就是在sql编译完后再传入数据
获取数据库操作对象另外一种方式,预编译的方式
*/
import com.shujia.utils.MySqlTool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
import java.util.UUID;
public class JDBCTest6 {
public static void main(String[] args) throws Exception {
System.out.println("欢迎进入小虎婚介所");
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//使用工具类获取与数据库的连接对象
Connection conn = MySqlTool.init();
//获取数据库操作对象
// Statement stat = conn.createStatement();
//编写sql语句
// String sql = "select username,password from user where username='"+username+"' and password='"+password+"'";
// select username,password from user where username='root' and password='123456'
// select username,password from user where username='xiaohu' and password='1234' or '1'='1'
// System.out.println(sql);
//这里的问号相当于一个占位符,为了后面传值
String sql = "select username,password from user where username=? and password=?";
//创建预编译数据库操作对象
PreparedStatement pps = conn.prepareStatement(sql);
//将sql中的占位符附上值
//第一个参数表示给第几个问号传值,第二个参数表示具体的值,序号从1开始
pps.setString(1,username);
pps.setString(2,password);
//执行sql语句
// ResultSet rs = stat.executeQuery(sql);
ResultSet rs = pps.executeQuery();
boolean b = rs.next();
if(b){
//如果b为true表示,数据库中存在该用户,登录成功
System.out.println("登录成功!!欢迎进入小虎婚介所!!");
}else {
//如果b为false,表示该用户还没有注册,需要先进行注册
System.out.println("您还未注册,请先注册(Y/N)");
String choice = sc.next();
if("Y".equals(choice)){
while (true){
System.out.println("请输入您的用户名:");
String new_username = sc.next();
System.out.println("请输入您设置的密码:");
String new_password = sc.next();
System.out.println("请确认密码:");
String password_again = sc.next();
if(password_again.equals(new_password)){
UUID uuid = UUID.randomUUID();
String new_uuid = uuid+"-"+System.currentTimeMillis();
String sql2 = "insert into user(uid,username,password) values('"+new_uuid+"','"+new_username+"','"+new_password+"')";
// int count = stat.executeUpdate(sql2);
int count = pps.executeUpdate(sql2);
if(count==1){
System.out.println("用户注册成功!!!");
}else {
System.out.println("用户注册失败!!!");
}
break;
}else {
System.out.println("两次密码不一致!!请重新输入");
}
}
}
}
//释放资源
// stat.close();
pps.close();
conn.close();
}
}
JDBC事物机制: 1.JDBC中的事务自动提交的,什么是自动提交? 只要执行任意一条 DML语句,则自动提交一次。这是JDBC默认的事务行为。 但是在实际的业务中,通常都是N条DML语句共同联合才能完成,必须 保证这些DML语句在同一个事务中同时成功或者同时失败 解决方案:三行重要的代码 conn.setAutoCommit(false);//手动提交事务 conn.commit();//提交事务 conn.rooback;当发生异常时或者程序错误时,进行回滚。
6.5.7 工具类封装
package com.shujia.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/*
工具类:
1、不能让外界实例化(构造方法私有化)
2、方法必须是静态
*/
public class MySqlTool {
private static Connection conn;
private MySqlTool(){}
public static Connection init(){
try {
//使用无参构造方法创建Properties类的对象
Properties prop = new Properties();
//使用字符流读取配置文件
// InputStreamReader , FileReader, BufferedReader
BufferedReader br = new BufferedReader(new FileReader("E://projects//IdeaProjects//bigdata19-mysql//resource//jdbc.properties"));
prop.load(br);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//加载驱动
Class.forName(driver);
//获取连接对象
conn = DriverManager.getConnection(url, username, password);
}catch (Exception e){
e.printStackTrace();
}
return conn;
}
}
工具类使用
package com.shujia.jdbc;
import com.shujia.utils.MySqlTool;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest4 {
public static void main(String[] args) throws Exception {
//使用工具类获取连接对象
Connection conn = MySqlTool.init();
//获取数据库操作对象
Statement stat = conn.createStatement();
//执行sql语句
int count = stat.executeUpdate("insert into dept(deptno,dname,loc) values(90,'行政部','数加科技')");
System.out.println(count==1?"数据插入成功!!":"数据插入失败!!");
//释放资源
stat.close();
conn.close();
}
}
6.5.8 JDBC实现模糊查询
package com.shujia.jdbc; import com.shujia.utils.MySqlTool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /* 进行模糊查询 */ public class JDBCTest { public static void main(String[] args) throws Exception { Connection conn = MySqlTool.init(); PreparedStatement prep = conn.prepareStatement("select uid,username,password from user where username like ?"); prep.setString(1, "%xiaohu%"); ResultSet rs = prep.executeQuery(); while (rs.next()) { String uid = rs.getString("uid"); String username = rs.getString("username"); String password = rs.getString("password"); System.out.println("姓名:" + username + ",密码:" + password); } prep.close(); conn.close(); } }
6.5 悲观锁和乐观锁的概念
事务1–>读取到版本号1.1 事务2—>读取到版本号1.1
其中事务1先修改了,修改之后看了版本号是1.1 ,于是提交修改的数据,将版本号修改为1.2 其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2 ,和它最初读的版本号不一致。回滚。
悲观锁:事务必须排队执行。数据锁住了,不允许并发。 (行级锁: select后面添加for update ) 乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。
6.6 演示索引的效率(springboot,redis,mysql)
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/java/279737.html