发布于2021-05-29 21:34 阅读(1291) 评论(0) 点赞(13) 收藏(2)
1、Java DataBase Connectivity (Java语言连接数据库)
2、JDBC本质上是SUN公司制定的一套接口。(java.sql.*;里面有很多接口)
3、为什么要制定一套JDBC接口?
因为每一个数据库都有自己的底层原理,实现的原理也不一样。Java程序员在使用数据库时,只要实现这个的接口就可以,接口的具体实现由各大数据库厂家来实现,我们只需要去官网下载即可。
3、
为什么要面向接口编程? 解耦合:降低程序的耦合度,提高程序的扩展力
多态机制就是非常典型的面向抽象编程。
import java.sql.*;
public class Test01 {
public static void main(String args[]) {
//1、注册驱动
Connection conn = null;
Statement stmt = null;
try {
java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();//父类引用指向子类对象
DriverManager.registerDriver(driver);
//2、获取连接
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "root";
//com.mysql.cj.jdbc.ConnectionImpl@52af6cff
conn = DriverManager.getConnection(url,user,password);
System.out.println("数据库对象 = " + conn);
//3、创建数据库操作对象
stmt = conn.createStatement();
//4、执行SQL语句
String sql = "insert into dept(deptno,dname,loc) value(50,'人事部','北京')";
//executeUpdate方法专门执行DML语句(desert,delete,update)
//返回值是“影响数据库中的记录条数”
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "插入成功" : "插入失败");
//5、处理查询结果集
} catch ( SQLException e) {
e.printStackTrace();
} finally {
//6、释放资源
// 一定要在finally里面关闭
// 一定要分别try catch
// 从小到大关闭
try {
if(stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
url:网络中某个资源的绝对路径。它由协议、IP、PORT、资源名构成
例如:http://182.61.200.7:80/index.html (百度的首页)
http:// 通信协议(通信协议就是提前定好的数据传送格式)
182.61.200.7 服务器IP地址
80 服务器上软件的端口
index.html 服务器上的某个资源名
使用java反射实现注册驱动(常用)
Class.forName()方法会导致括号里面的类加载。而com.mysql.cj.jdbc.Driver类里面的静态代码块中实现了注册驱动,而类加载会导致静态代码块执行。
import java.sql.*;
public class Test01 {
public static void main(String args[]) {
Connection conn = null;
Statement stmt = null;
try {
//java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();
Class.forName("com.mysql.cj.jdbc.Driver");
//代替上面这个注释掉地代码。这样做的好处参数是字符串,而字符串可以写在配置文件中,便于修改
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
注:com.mysql.cj.jdbc.Driver实现了java.sq.Driver接口
用ResultSet类的对象来保存查询结果集。然后循环遍历,判断条件使用ResultSet类的对象的next()方法。
import java.sql.*;
public class Test01 {
public static void main(String args[]) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root");
stmt = conn.createStatement();
String sql = "select empno,ename,sal from emp";
rs = stmt.executeQuery(sql);//执行DQL语句的方法
/*
while (rs.next()) {
可以用下标来表示第几列(JDBC中的下标都从 1 开始)
int empno = rs.getInt(1);
String ename = rs.getString(2);
Double sal = rs.getDouble(3);
System.out.println(empno + "," + ename + "," + sal);
}
*/
while (rs.next()) {
//让程序更健壮的方式是用字段名
int empno = rs.getInt("empno");
String ename = rs.getString("ename");
Double sal = rs.getDouble("sal");
System.out.println(empno + "," + ename + "," + sal);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
如果select 语句中有起别名,则遍历时必须用别名查询
import java.sql.*;
public class Test01 {
public static void main(String args[]) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root");
stmt = conn.createStatement();
String sql = "select empno as a,ename,sal from emp";//注意这里给 empno 起了别名 a
rs = stmt.executeQuery(sql);
while (rs.next()) {
int empno = rs.getInt("a");//因为是对结果集的遍历,所以如果字段有别名,遍历时必须用别名查询
String ename = rs.getString("ename");
Double sal = rs.getDouble("sal");
System.out.println(empno + "," + ename + "," + sal);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
模拟用户登录功能实现。
程序运行时,提供一个输入的入口,可以让用户输入用户名和密码
用户输入用户名和密码后,提交信息,java程序收集到用户信息
Java程序连接数据库验证用户名和密码是否合法
这里使用Navicat创建表
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JDBCTest02 {
public static void main(String args[]) {
//初始化页面
Map<String,String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
//最后输出结果
System.out.println(loginSuccess ? "登陆成功" : "登陆失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false表示失败 true表示成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
boolean loginSuccess = false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");
//创建操作数据库对象
stmt = conn.createStatement();
String sql = "select * from Login_tb where loginName = '"+loginName+"'and loginPwd = '"+loginPwd+"'";
//执行SQL语句
rs = stmt.executeQuery(sql);
//处理查询结果集
if(rs.next()) {
loginSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
//释放资源
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return loginSuccess;
}
/**
*初始化用户界面
*用户输入的用户名的密码等登录信息
*/
private static Map<String, String> initUI() {
Scanner s = new Scanner(System.in);
System.out.println("用户名:");
String loginName = s.nextLine();
System.out.println("密码:");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
注意:上述代码中会有SQL注入问题。
SQL注入:
SQL 注入攻击是通过将恶意的SQL 查询或添加语句插入到应用的输入参数中,再在后台 SQL服务器上解析执行进行的攻击,它目前是黑客对数据库进行攻击的最常用手段之一。
例如,上述程序中,如果是如下输入:
用户名:
aaa
密码:
aaa' or '1' = '1
那么会显示登陆成功。
因为输入语句中含有SQL语句关键字,因为SQL语句在进行编译时把用户输入信息拼接到了SQL语句中,
使得:
select * from Login_tb where loginName = '"+loginName+"'and loginPwd = '"+loginPwd+"
这个SQL语句变成了:
select * from Login_tb where loginName = 'aaa'and loginPwd = 'aaa' or '1' = '1';
这样的话,where语句的判断条件就变成了
(loginName = 'aaa'and loginPwd = 'aaa')
or
('1' = '1')
而'1' = '1'
这个条件是一定成立的。
解决方法:
只要让输入的参数不参与SQL语句的编译即可。
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JDBCTest02 {
public static void main(String args[]) {
//初始化页面
Map<String,String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
//最后输出结果
System.out.println(loginSuccess ? "登陆成功" : "登陆失败");
}
private static boolean login(Map<String, String> userLoginInfo) {
Connection conn = null;
PreparedStatement ps = null;//修改 Statement 为 PreparedStatement(预编译数据库操作对象)
ResultSet rs = null;
boolean loginSuccess = false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");
//这里是写了一个SQL语句的框子 (SQL语句里面的 ? 是占位符,不可以用''括起来)
String sql = "select * from Login_tb where loginName = ? and loginPwd = ? ";
//程序执行到这里,会发送SQL语句框子给DBMS,然后DBMS进行预先编译
//获取预编译的数据库操作对象
ps = conn.prepareStatement(sql);//修改 createStatement() 为 prepareStatement
//给 ? 传值。问号的下标从1开始
ps.setString(1,loginName);
ps.setString(2,loginPwd);
rs = ps.executeQuery(); //这里不传SQL语句
if(rs.next()) {
loginSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return loginSuccess;
}
private static Map<String, String> initUI() {
Scanner s = new Scanner(System.in);
System.out.println("用户名:");
String loginName = s.nextLine();
System.out.println("密码:");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
prepareStatement与Statement的区别:
综上所述:prepareStatement使用较多,Statement只在极少数情况下使用,比如业务特别要求可以支持SQL注入
使用prepareStatement实现
例如:
import java.sql.*;
public class JDBCTest03 {
public static void main(String args[]) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
//写DML语句时,所插入的值统统先用 ?占位
String sql = "insert into dept(deptno,dname,loc) value(?,?,?)";
ps = conn.prepareStatement(sql);
//而后再给 ? 赋值
ps.setInt(1,60);
ps.setString(2,"Tom");
ps.setString(3,"Beijing");
int count = ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
以后再补…
import java.sql.*;
/**
* JDBC工具类:简化JDBC编程
*/
public class DBUtil {
/**
* 工具类中的类都是私有的
* 因为工具类中的方法都是静态的,不需要new对象,直接类名调用
*/
private DBUtil() { }
//静态代码块在类加载时执行,并且只执行一次
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
}
/**
* 关闭资源
* @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();
}
}
}
}
模糊查询的实现(使用PreparedStatement):
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCTest04 {
public static void main(String args[]) {
Connection conn = null;
PreparedStatement ps= null;
ResultSet rs = 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 (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn,ps,rs);调用工具类的方法
}
}
}
悲观锁:
乐观锁:
dept:
emp:
login_tb:
作者:程序员之神
链接:http://www.javaheidong.com/blog/article/207379/b4c29a75f5defbc44385/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!