文章目录
JDBC
- java DataBase Connectivty(java语言连接数据库)
- JDBC是sun制定的接口(interface):java.sql.*
- 面向接口:解耦合:降低程序的耦合度,提高程序的扩展力
- 每一个数据库底层实现原理都不一样
idea导入JDBCjar包
- JDBC开发前,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中
- classpath = .;E:\JDBC\mysql-connector-java-8.0.27
- 使用IDEA的时候,不需要配置环境变量
- 新建空项目,在项目里面新建模块
- 在对应的模块上,右击,选择open module setting
JDBC流程—六步
注册驱动
- 告诉java程序,即将连接哪个数据库
- 使用java.sql.Driver接口的DriverManager.registerDriver()方法,传入一个数据库驱动,该接口是sun公司提供,具体的实现类“驱动”是数据库公司提供,即创建对象是,需要找到数据库对应Driver接口的实现类——也是Driver。
- 区分清楚这两个Driver
获取连接
- 表示JVM和数据库的进程通道打开,使用完之后,关闭
- DriverManager的getConnection()方法可以获取连接
- 方法需要的参数:协议;IP;数据库端口号;数据库实例名
获取数据库操作对象
- 专门执行sql语句的对象
- Connection的createStatement()可以获取对象
执行SQL语句
- DQL、DML…
处理查询结果集
- 只有当第四步执行的select语句时,才有第五步处理查询结果集
释放资源
- 使用完资源,就需要关闭资源。
注册驱动——使用类加载
- 该方法常用,参数是字符串,字符串可以写进配置文件
- Class.forName(“E:\JDBC\mysql-connector-java-8.0.27\src\main\user-impl\java\com\mysql\cj\jdbc”);
数据存放配置文件
- 在jdbc.properties文件配置如下内容
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/yi
user=root
password=1230258
使用查询语句,处理结果集
- 查询语句:String sql = “select empno,ename,sal from emp”;
- stmt.executeQuery(sql); 执行语句,返回结果集ResultSet rs
- ResultSet 有一个next() 方法,当该行有数据,rs.next()返回值为true
- 当返回值为true时,可以遍历该行的数据
用户登录功能
- 数据库内数据
- 获取用户登录时输入信息
package jdbcfrist;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
/**
* 实现功能:
* 需求:模拟用户登录功能
* 程序运行时,提供一个输入入口。让用户输入用户名、密码
* 之后,提交信息java程序收集用户信息
* java程序连接数据库,验证用户和密码是否合法
* 合法:登录成功
* 不合法:登录失败
* */
public class loadFuction {
public static void main(String[] args) {
//初始化界面
Map<String,String> unserinfo =initUI();
//验证用户名和密码
boolean result = login(unserinfo);
//输出结果
System.out.println(result?"登录成功":"登录失败");
}
/**
* 初始化用户界面
* @return 用户名和密码
*/
private static Map<String,String> initUI() {
Scanner s =new Scanner(System.in);
System.out.println("用户名 :");
String loginName =s.nextLine();
System.out.println("密码 :");
String loginPassword =s.nextLine();
//获取完信息,加载到Map集合
Map<String,String> user = new HashMap<>();
user.put("loginName",loginName);
user.put("loginPassword",loginPassword);
return user;
}
/**
* @param unserinfo
* @return
*/
private static boolean login(Map<String, String> unserinfo) {
//jdbc代码
ResourceBundle bundle =ResourceBundle.getBundle("jdbc");
Connection conn=null;
Statement stmt =null;
ResultSet rs =null;
//取出数据
String username =unserinfo.get("loginName");
String password =unserinfo.get("loginPassword");
//标志
boolean flag =false;
try {
Class.forName(bundle.getString("driver"));
conn= DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),
bundle.getString("password"));
stmt = conn.createStatement();
String sql = "select * from login where user ='"+username+"' and password='"+password+"'";
rs=stmt.executeQuery(sql);
if(rs.next()){
flag=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return flag;
}
}
sql注入
- 用户登录出现的问题:
- 用户名:aaa
- 密码:aaa’or’1’='1
- 这样的现象被称为sql注入(安全隐患)
- 导致sql注入的根本原因
- 用户输入的信息含有sql语句的关键字,并且这些关键字参与sql语句的编译过程
- 导致sql原意被扭曲,达到sql注入
sql注入解决—perparementStatement
- 目标:用户提供的信息不参与sql语句的编译过程,问题就可以解决
- 要想用户提供的信息不参与sql语句的编译,那么就需要使用java.sql.PreparedStatement
- preparedStatement接口继承了java.sql.Statement,属于预编译的数据库操作对象
- 执行效果
package jdbcfrist;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
/**
* 实现功能:
* 需求:模拟用户登录功能
* 程序运行时,提供一个输入入口。让用户输入用户名、密码
* 之后,提交信息java程序收集用户信息
* java程序连接数据库,验证用户和密码是否合法
* 合法:登录成功
* 不合法:登录失败
* */
public class loadFuction {
public static void main(String[] args) {
//初始化界面
Map<String,String> unserinfo =initUI();
//验证用户名和密码
boolean result = login(unserinfo);
//输出结果
System.out.println(result?"登录成功":"登录失败");
}
/**
* 初始化用户界面
* @return 用户名和密码
*/
private static Map<String,String> initUI() {
Scanner s =new Scanner(System.in);
System.out.println("用户名 :");
String loginName =s.nextLine();
System.out.println("密码 :");
String loginPassword =s.nextLine();
//获取完信息,加载到Map集合
Map<String,String> user = new HashMap<>();
user.put("loginName",loginName);
user.put("loginPassword",loginPassword);
return user;
}
/**
* @param unserinfo
* @return
*/
private static boolean login(Map<String, String> unserinfo) {
//jdbc代码
ResourceBundle bundle =ResourceBundle.getBundle("jdbc");
Connection conn=null;
Statement stmt =null;
ResultSet rs =null;
//取出数据
String username =unserinfo.get("loginName");
String password =unserinfo.get("loginPassword");
//标志
boolean flag =false;
try {
Class.forName(bundle.getString("driver"));
conn= DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),
bundle.getString("password"));
stmt = conn.createStatement();
String sql = "select * from login where user ='"+username+"' and password='"+password+"'";
rs=stmt.executeQuery(sql);
if(rs.next()){
flag=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return flag;
}
}
PerparementStatement ; Statement
- Statement存在sql注入问题;PerparementStatement解决sql注入问题
- Statement编译一次执行一次;PerparementStatement编译一次,可执行N次。PerparementStatement效率高一些
- PerparementStatement会在编译阶段做类型的安全检查
- 即:PerparementStatement使用较多,但极少数情况下仍需要使用Statement
Statement
- 使用场景
- 业务方面要求sql注入的情况
- 凡是业务要求是sql语句拼接的,必须使用Statement。单纯的传“值”,使用PerparementStatement
package jdbcfrist;
import java.sql.*;
import java.util.ResourceBundle;
import java.util.Scanner;
/**
* 用户在控制台输入desc:降序;asc升序
*/
public class Orderby {
public static void main(String[] args) {
Scanner s =new Scanner(System.in);
System.out.println("请输入desc或者asc,desc为降序;asc为升序");
String m = s.nextLine();
//jdbc
ResourceBundle bundle =ResourceBundle.getBundle("jdbc");
Connection conn =null;
Statement stmt =null;
ResultSet rs =null;
try {
Class.forName(bundle.getString("driver"));
conn = DriverManager.getConnection(bundle.getString("url"),
bundle.getString("user"),bundle.getString("password"));
stmt =conn.createStatement();
String sql = "select sal ,ename from emp order by sal "+m;
rs= stmt.executeQuery(sql);
while(rs.next()){
int sal = rs.getInt("sal");
String name =rs.getString("ename");
System.out.println(sal+" "+name);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
PerparementStatement
- 使用PerparementStatement 完成增删改:出现的问题
package jdbcfrist;
import java.sql.*;
import java.util.ResourceBundle;
/**
* 使用PerparementStatement 完成增删改
*/
public class jdbcTest04 {
public static void main(String[] args) {
//注册驱动
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
Connection conn = null;
PreparedStatement ps =null;
try {
//建立连接
Class.forName(bundle.getString("driver"));
conn= DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),
bundle.getString("password"));
//获取预编译数据库对象
String sql = "update dept set dname =? where deptno =?";
ps =conn.prepareStatement(sql);
//传值
ps.setString(1,"x部门");
ps.setInt(2,30);
int count = ps.executeUpdate();
/**
* 在 int count = ps.executeUpdate();处加断点可以看出
* jdbc只要执行任意一条DML语句,就提交一次。即jdbc自动提交
* 这样的处理方式是操作者不想要的,因为在业务中可能需要多个sql语句
* 当前面的sql语句执行完,如果有一个sql语句发生异常,不再执行
* 但是之前的sql的修改已经写进数据库
*
*/
ps.setString(1,"y部门");
ps.setInt(2,20);
count = ps.executeUpdate();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
银行转账
- 由于jdbc存在自动提交sql语句,如果一个事务未执行完,就出现异常。那么就会造成数据的错误。如图所示
setAutoCommit(false)
- 解决方法
package jdbcfrist;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ResourceBundle;
/**
* 数据库中有两个银行账户
* 111 存款20000
* 222 存款0
* 现在111账户给222账户转账10000
* 使得两个账户都有10000存款
*/
public class bank {
public static void main(String[] args) {
//注册驱动
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
Connection conn = null;
PreparedStatement ps =null;
try {
//建立连接
Class.forName(bundle.getString("driver"));
conn= DriverManager.getConnection(bundle.getString("url"),bundle.getString("user"),
bundle.getString("password"));
//将自动提交修改为手动提交
//开启事务
conn.setAutoCommit(false);
//获取预编译数据库对象
String sql = "update t_act set banlance=? where actno=?";
ps =conn.prepareStatement(sql);
//传值
ps.setInt(1,10000);
ps.setInt(2,111);
int count = ps.executeUpdate();
//空指针异常
String s= null;
s.toString();
ps.setInt(1,10000);
ps.setInt(2,222);
count += ps.executeUpdate();
System.out.println(count==2?"转账成功":"转账失败");
//程序执行到此,说明事务没有异常
//提交事务
conn.commit();
} catch (ClassNotFoundException e) {
//回滚事务
if(conn!=null){
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBC封装工具类
package jdbcfrist;
import java.sql.*;
/**
* JDBC工具类,简化JDBC编程
*/
public class DBUtil {
/**
* 工具类的构造方法是私有的
* 因为工具类的方法都是静态的,不需要new对象,直接采用类名调用
*/
private DBUtil(){
}
/**
* 静态代码块在类加载时执行,只执行一次
*/
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws Exception{
return DriverManager.getConnection("jdbc:mysql://localhost:3306/yiyu", "root", "970619");
}
/**
* 关闭
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集
*/
public static void close(Connection conn, Statement ps, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
模糊查询
package jdbcfrist;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 1 测试DBUtil
* 2 模糊查询
*/
public class DBUtilTest {
public static void main(String[] args) {
Connection conn = null;
ResultSet rs=null;
PreparedStatement ps = null;
try {
//获取连接
conn = DBUtil.getConnection();
//获取预编译的数据库操作对象
String sql="select ename from emp where ename like ?";
ps=conn.prepareStatement(sql);
ps.setString(1,"%A%");
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
DBUtil.close(conn,ps,rs);
}
}
}