1、JDBC 是什么?
Java Database connectivity(java语言连接数据库)
2、JDBC本质是什么?
JDBC是sun公司制定的一套接口(interface)
接口都有调用者和实现者
面向接口调用、面向接口写实现类、这都属于面向接口编程
为什么要有面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力
多态机制就是非常典型的:面向抽象编程(不需要面向具体编程)
JDBC编程6步
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
//1:注册驱动 2:获取链接 3:获取数据库操作对象 4:执行sql 5:处理查询结果集 6:释放资源
public class JDBCTest01{
public static void main(String[] args){
try{
//1、注册驱动
//由于Driver是一个接口无法创建对象,所以需要new实现它的类
java.sql.Driver driver = new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);
//获取连接
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root";
String password = "333"
Connection conn = DriverMabager.getConnection(url,user,password);
system.out.println("数据库连接对象 = " + conn)
}catch(SQLException e){
e.printStackTrace();
}
}
}
删除操作
import java.sql.*;
public class Test02{
public static void main(String[] args){
Connection conn = null;
Statement stmt = null;
try{
//注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//获取链接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpiwernode","root","333");
//获取数据库操作对象
stmt = conn.createStatement();
//执行sql JDCB sql语句不需要写分号。
String sql = "delete from dept where deptno = 40";
int count = stmt.excuteUpdate(sql);
System.out.print(connt ==1 ? "删除成功":"删除失败")
}catch(SQLException e){
e.printStackTrace();
}finally{
//关闭资源
if(stmt !=null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
if(conn !=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
P14(笔记)
* 实现功能
* 1:需求:模拟用户登录功能
* 2:描述:程序运行的时候,提供一个输入的入口,能让用户输入账户和密码
* 用户输入用户名和密码后,提交信息,Java程序收集到信息。
* java程序连接数据库,验证用户名和密码是否正确
* 合法显示成功,不合法显示不成功。
* 3:数据的准备
* 在实际开发中,表的设计会使用专业的建模工具,安装powerde'signer
* 使用pd工具来进行数据库的表设计。(参考user-login.sql)
PowerDesigner (用于建模型)
键表的步骤在视频观看。
主要碰到的问题是 修改记事本的编码,在cmd’->mysql没有生效。
修改步骤: 首先查看表的编码格式 show create table t_user;
修改的命令:alter table t_user default character set utf8;
但是发现还是报错。
还要修改每一条语句:alter table t_user change loginPwd loginPwd varchar(255) character set utf8; 其他一样
修改后再插入数据
P17(笔记)
今天一整天在处理报错问题。切记mysql6一下的数据库版本不需要在url地址加上时区。
程序代码
package com.bjpowernode.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
* 实现功能
* 1:需求:模拟用户登录功能
* 2:描述:程序运行的时候,提供一个输入的入口,能让用户输入账户和密码
* 用户输入用户名和密码后,提交信息,Java程序收集到信息。
* java程序连接数据库,验证用户名和密码是否正确
* 合法显示成功,不合法显示不成功。
* 3:数据的准备
* 在实际开发中,表的设计会使用专业的建模工具,安装powerdesigner
* 使用pd工具来进行数据库的表设计。(参考user-login.sql)
*
*
* */
public class JDBCTest06 {
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) {
//打标记意识
boolean loginSuccess = false;
//单独定义变量
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
//JDBC代码1
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//获取数据库操作对象
stmt = conn.createStatement();
//执行sql
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
rs = stmt.executeQuery(sql);
//处理数据结果集
if(rs.next()){
loginSuccess = true;
}
}catch (Exception e){
e.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 loginSuccess;
}
/*
* 初始化用户界面
* @return 用户输入的用户名和 密码等信息
*
* */
private static Map<String, String> initUI() {
Scanner s = new Scanner(System.in);
System.out.print("用户名:");
String loginName = s.nextLine();
System.out.print("密码:");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
主要问题。程序启动无法正常执行;
原因:开启了两个数据库 一个是5.5版本 一个是5.7版本。 同时占用这3306端口
而且5.5版本的数据库在最开始的时候也做了jdbc练习,所以里面有同名同姓的数据库和表。系统默认是走的5.5版本的,实际上idea的程序是要走5.7,数据无法匹配所以无法成功。 关闭了5.5重新导入数据。测试成功。
SQL注入(黑客经常使用)
导致sql注入的原因是什么?
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程导致sql语句的原本的意思被扭曲,从而达到sql注入。
代码
package com.bjpowernode.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JDBCTest07 {
//解决sql注入的问题
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) {
//打标记意识
boolean loginSuccess = false;
//单独定义变量
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
//JDBC代码1
Connection conn = null;
// Statement stmt = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//获取预编译的数据库操作对象
//sql语句中。一个?代表一个占位符,一个?将来接收一个”值“,注意。占位符不能用单引号括起来。
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
//程序执行到这里。会发送sql语句给DBMS,然后DBMS在进行sql预先编译。
ps = conn.prepareStatement(sql);
//给占位符传值,JDBC下标从1开始。
ps.setString(1,loginName);
ps.setString(2,loginPwd);
//执行sql
rs = ps.executeQuery();
//处理数据结果集
if(rs.next()){
loginSuccess = true;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放资源
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();
}
}
}
return loginSuccess;
}
/*
* 初始化用户界面
* @return 用户输入的用户名和 密码等信息
*
* */
private static Map<String, String> initUI() {
Scanner s = new Scanner(System.in);
System.out.print("用户名:");
String loginName = s.nextLine();
System.out.print("密码:");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
解决sql注入:
只要用户提供信息不参与sql语句编译就能解决问题。
即便是有sql语句关键字。但是没用参与,不起作用。
要想用户信息不参与sgz语句的编译,那么必须使用java.sql.PreparedStatementPreparedstatement接口继承了java.sql.statement
Preparedstatement是属于预编译的数据库操作对象。
reparedstatement的原理是:预先对sql语句的框架进行编译,然后再给sql语句传值。
对比点:statement和preparedstatement?
statement 有sql注入问题。preparedstatement解决了sql注入问题/
执行效率方面:
statement是编译一次执行一次,preparedstatement是编译一次,执行N次,且会在编译阶段做类型安全检查
preparedstatement 执行效率高
注意:当业务需求SQL注入的话 必须使用statement 用来拼接。
PreparedStatement完成增删改
package com.bjpowernode.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCTest09 {
public static void main(String[] args) {
//第一步 搭架子
Connection conn = null;
PreparedStatement ps = null;
try {
//第二步 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//第三步 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowerbode","root","333");
//第四步 获取预编译的数据库操作对象
// String sql = "insert into dept(delete,dname,loc) value(?,?,?)";
// //编译sql语句
// ps = conn.prepareStatement(sql);
// //给编译语句传值
// ps.setInt(1,60);
// ps.setString(2,"销售部");
// ps.setString(3,"上海");
// String sql = "update dapt set dname =?, loc = ? where deptno = ?";
// //编译sql语句
// ps = conn.prepareStatement(sql);
// //给编译语句传值
// ps.setInt(1,60);
// ps.setString(2,"销售部");
// ps.setString(3,"上海");
String sql = "delete from dept where = deptno = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,60);
//第五步 执行sql
int count = ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.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事务机制
什么是自动提交?
只要任意一条DML语句(数据操纵语句: 插入:INSERT、更新:UPDATE、删除:DELETE),则自动提交一次。这是JDBC的默认行为,但是在实际业务中,通常都是N条DML语句共同联合来完成的,必须保证他们这些DML语句在同一个事务中同步成功或者失败。
以下程序来验证JDBC的事务是否自动提交机制(结果:JDBC只要执行任意一条DML语句就提交一次)
Class public JDBCTest10{
public static void main(String[] args){
//搭架子
Connection conn = null;
PreparedStatement ps = null;
try{
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn.DriverManager.getConnection("jdbc:mysql://localhost:3306:/bjpowernode","root","333");
//获取预编译数据库操作对象(1:写入sql语句 2:编译sql 3:传值)
String sql = "update dept set dname = ? where deptno = ?";
//第一次给占位符传值
ps.conn.preparedStatement(sql);
ps.setString(1,"xx部门");
ps.setInt(2,30);
//执行sql语句
int count = ps.executeUpdate();
// 重新给占位符传值(第二次执行)
ps.setString(1,"yy部门");
ps.setInt(2,20);
int count = ps.executeUpdate();
}catch(Exception e){
e.printStackTrece();
}finally{
//释放资源
if(ps != null){
ps.close();
}catch(SQLException e){
e.printStackTrece();
}
if(conn != null){
conn.close();
}catch(SQLException e){
e.printStackTrece();
}
}
}
}
转账演示事务
package com.bjpowernode.jdbc;
import java.sql.*;
/*
* sql 脚本
* drop table if exists t_act;
* create table t_act(
* actno int,
* balance double(7,2) //7表示有效数字,2表示小数位个数
* );
* insert into t_act(actno,balance) values(111,20000);
* insert into t_act(actno,balance) values(222,0);
* commit;
* select * from t_act;
*
*
*
*
* */
public class JDBCTest11 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//开启事务
conn.setAutoCommit(false);
String sql = "update t_act set balance = ? where actno = ?";
ps = conn.prepareStatement(sql);
//故意制造空指针异常。
String s = null;
s.toString();
//给第一个?传值
ps.setDouble(1,10000);
ps.setInt(2,111);
int count = ps.executeUpdate();
//给第二个?传值
ps.setDouble(1,10000);
ps.setInt(2,222);
count += ps.executeUpdate();
System.out.println(count ==2 ? "转账成功" : "转账失败");
conn.commit();
} catch (Exception e) {
//回滚事务
if (conn != null){
try{
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
}finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
通过空指针异常,如果没加事务 commit rollback 会发现数据丢失
添加事务操作,要么一起完成,要么一起失败。保证数据的完整性
for update;(行级锁又叫悲观锁) 保证数据的准确性
select ename,job,sal from emp where job=‘MANAGER’ for update;(行级锁)