标签:基础上 hashmap exe sel 完成 下标 div ref 查看
Java DataBase Connectivity(Java语言连接数据库)
JDBC是SUN公司制定的一套接口(interface)
接口都是有调用者和实践者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。
为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
建议:
Animal a = new Cat();
Animal a = new Dog();
//喂养的方法
public void feed(Animal a){ //面向父类编程
}
不建议:
Dog d = new Dog();
Cat c = new Cat();
思考:为什么SUN公司制定一套JDBC接口呢?
因为每一个数据库的底层实现原理都不一样。
Oracle数据有自己的原理。
MySQL数据库也有自己的原理。
MS SqlServer数据库也有自己的原理。
...
每一个数据库产品都有自己独特的实现原理。
JDBC的本质是什么?
一套接口。
先从数据库官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。
classpath= .;D:\mysql-connector-java-8.0.21\mysql-connector-java-8.0.21.jar
以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。IDEA有自己的配置方式。
第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭。
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行SQL语句(DQL DML...)
第五步:执行查询结果集(只有当第四步执行的select语句的时候,才有这第五步处理查询结果集。)
第六步:释放资源
jdbc.properties
-------------------
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC&useSSL=false
user=root
password=xiaokw
sql=select * from emp
package cn.xiaokw.java.jdbc;
import java.sql.*;
import java.util.ResourceBundle;
public class JDBCTest {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
ResourceBundle bundle = ResourceBundle.getBundle("cn/xiaokw/java/jdbc/jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
try{
//1、注册驱动
Class.forName(driver);
//2、获取连接
conn = DriverManager.getConnection(url,user,password);
//3、获取数据库操作对象
stmt = conn.createStatement();
//4、执行SQL语句
String sql = "select empno,ename,sal from emp";
rs = stmt.executeQuery(sql);
//5、处理查询结果集
System.out.println("EMPNO\tENAME\tSAL");
for (int i = 0; i < 22; i++) {
System.out.printf("-");
}
System.out.println();
while(rs.next()){
int empno = rs.getInt("empno");
String ename = rs.getString("ename");
double sal = rs.getDouble("sal");
System.out.println(empno + "\t" + ename + "\t" + sal);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally{
//6、释放资源
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();
}
}
}
}
案例:用户登录
package cn.xiaokw.java.jdbc;
import javax.xml.transform.Result;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JDBCTest06 {
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 flag = false;
try{
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
//3、获取数据库对象
stmt = conn.createStatement();
//4、执行sql语句
String sql = "select * from t_user where loginName = ‘"+userLoginInfo.get("loginName") + "‘ and loginPwd = ‘" +userLoginInfo.get("loginPwd") + "‘";
//如果这里的sql语句这样写的话会出现bug
/*用户名:xiao
密码:xiao‘ or ‘1‘=‘1
登录成功*/
//这种现象被称为SQL注入(安全隐患)
/*
以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译。
正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
*/
/*
导致SQL注入的根本原因是什么?
用户输入的信息中含有sql语句的关键字,并且这些关键字参与了sql语句的编译过程,
导致sql语句的原意被扭曲,进而达到sql注入。
*/
rs = stmt.executeQuery(sql);
//5、处理结果集
if(rs.next()){
//登录成功
flag = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally{
//释放资源
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(stmt != null)
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return flag;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登陆信息
*/
private static Map<String, String> initUI() {
Scanner sc = new Scanner(System.in);
System.out.printf("用户名:");
String loginName = sc.nextLine();
System.out.printf("密码:");
String loginPwd = sc.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName" , loginName);
userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo;
}
}
以上java程序会出现SQL注入问题
例如:如果在密码输入中注入sql语句则会登录成功
用户名:xiao
密码:xiao‘ or ‘1‘=‘1
登录成功
使用PreparedStatement
package cn.xiaokw.java.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
解决SQL注入问题?
只要用户提供的信息不参与sql语句的编译过程,问题就解决了
即使用户提供的信息中含有sql语句的关键字,但是没有参与编译,不起作用。
要想用户信息不参与sql语句的编译,那么使用java.sql.PreparedStatement
PreparedStatement接口继承了java.sql.Statement
PreparedStatement是属于预编译的数据库操作对象
PreparedStatement的原理是:预先对sql语句的框架进行编译,然后再给sql语句传“值”。
*/
public class JDBCTest07 {
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;
PreparedStatement ps = null; //这里使用了PreparedStatement(预编译的数据库操作对象)
ResultSet rs = null;
boolean flag = false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try{
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
//3、获取数据库对象
//sql语句的框子。其中一个?,表示一个占位符,一个?将来接受一个“值”,注意:占位符不能使用单引号括起来。
String sql = "select * from t_user where loginName = ? and loginPwd= ? ";
ps = conn.prepareStatement(sql);
//给占位符?传值(第一个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始)
//这里调用的是setString 所有传入sql语句中会自动加入单引号
ps.setString(1,loginName);
ps.setString(2,loginPwd);
//4、执行sql语句
rs = ps.executeQuery(); //这里就不需要再传入sql了
//5、处理结果集
if(rs.next()){
//登录成功
flag = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally{
//释放资源
try{
if(rs != null)
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(ps != null)
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try{
if(conn != null)
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return flag;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登陆信息
*/
private static Map<String, String> initUI() {
Scanner sc = new Scanner(System.in);
System.out.printf("用户名:");
String loginName = sc.nextLine();
System.out.printf("密码:");
String loginPwd = sc.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName" , loginName);
userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo;
}
}
对比一下Statement和PreparedStatement?
Statement存在sql注入问题,PreparedStatement解决了sql注入问题。
Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些。
PreparedStatement会在编译阶段做类型的安全检查。
综上所述:PreparedStatement使用较多。只有极少数的情况下需要使用Statement
什么情况下必须使用Statement呢?
业务方面要求必须支持SQl注入的时候。
Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
package cn.xiaokw.java.jdbc;
import java.sql.*;
import java.util.ResourceBundle;
public class JDBCTest08 {
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
//3、获取预编译的数据库操作对象
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1,"50");
ps.setString(2,"销售部");
ps.setString(3,"北京");
//4、执行sql
int count = ps.executeUpdate();
System.out.println(count==1 ? "成功" : "失败");
} catch (Exception e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
在原有的基础上修改第三步即可
//3、获取预编译的数据库操作对象
String sql = "delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"50");
//3、获取预编译的数据库操作对象
String sql = "update dept set dname = ? , loc = ? where deptno = ? ";
ps = conn.prepareStatement(sql);
ps.setString(1,"技术部");
ps.setString(2,"上海");
ps.setString(3,"50");
package cn.xiaokw.java.jdbc;
import java.sql.*;
/**
* JDBC事务机制
* JDBC中的事务是自动提交的,什么是自动提交?
* 只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
* 但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时完成或者同时失败。
*/
public class JDBCTest09 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
//3、获取预编译的数据库操作对象
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
ps = conn.prepareStatement(sql);
//第一次给占位符传值
ps.setString(1,"50");
ps.setString(2,"研发部");
ps.setString(3,"上海");
int count = ps.executeUpdate();
System.out.println(count);
//使用断点调试,当执行到这一步时,可以去数据库里查看数据是否发生改变,如果已经成功添加了这条数据,则证明JDBC中的事务是执行一条DML则自动提交一次的。
//重新给占位符传值
ps.setString(1,"60");
ps.setString(2,"技术部");
ps.setString(3,"北京");
count = ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
package cn.xiaokw.java.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 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(10001,2000);
* insert into t_act(actno,balance) values(10002,0);
* commit;
* select * from t_act;
*
* 演示:10001账户向10002账户转账1000元?
* 重点的三行代码?
* conn.setAutoCommit(false);
* conn.commit();
* conn.rollback();
*/
public class JDBCTest10 {
public static void main(String[] args){
Connection conn = null;
PreparedStatement ps = null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
conn.setAutoCommit(false); //将自动提交机制修改为手动
//3、获取预编译的数据库对象
String sql = "update t_act set balance = ? where actno = ?";
ps = conn.prepareStatement(sql);
ps.setDouble(1,1000);
ps.setInt(2,10001);
int count = ps.executeUpdate();
//当转账中途中出现异常
// String str = null;
// str.toString();
ps.setDouble(1,1000);
ps.setInt(2,10002);
count += ps.executeUpdate();
System.out.println(count == 2 ? "转账成功" : "转账失败");
conn.commit(); //当执行完毕后,则提交
} catch (Exception e) {
if(conn != null){
try {
conn.rollback();
System.out.println("出现异常,已为您回滚操作数据!");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
e.printStackTrace();
}finally{
if(ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
package cn.xiaokw.java.jdbc;
import java.sql.*;
public class DBUtil {
private DBUtil(){}
//静态代码块在类加载时执行,并且只执行一次
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return 连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/xiaokw?serverTimezone=UTC","root","xiaokw");
}
/**
* 关闭资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集
*/
public static void close(Connection conn , Statement ps, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
package cn.xiaokw.java.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 模糊查询:使用封装好的DBUtil工具
* 查询名字中含有A的人名?
*/
public class JDBCTest11 {
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%");
//执行sql
rs = ps.executeQuery();
//处理结果集
while(rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
//释放资源
DBUtil.close(conn,ps,rs);
}
}
}
标签:基础上 hashmap exe sel 完成 下标 div ref 查看
原文地址:https://www.cnblogs.com/xiaokw/p/13569464.html