码迷,mamicode.com
首页 > 数据库 > 详细

JDBC的使用

时间:2020-09-04 17:24:26      阅读:52      评论:0      收藏:0      [点我收藏+]

标签:基础上   hashmap   exe   sel   完成   下标   div   ref   查看   

JDBC

JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

JDBC的本质是什么?

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的本质是什么?
	一套接口。

JDBC开发前的准备工作

先从数据库官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。
classpath= .;D:\mysql-connector-java-8.0.21\mysql-connector-java-8.0.21.jar
	以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。IDEA有自己的配置方式。

JDBC编程六步

第一步:注册驱动(作用:告诉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();
            }

        }
    }
}

SQL注入问题

案例:用户登录

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;
    }
}

发现SQL注入问题

以上java程序会出现SQL注入问题
例如:如果在密码输入中注入sql语句则会登录成功
用户名:xiao
密码:xiao‘ or ‘1‘=‘1
登录成功

解决SQL注入问题

使用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。

PreparedStatement完成增删改

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");

JDBC事务

验证JDBC事务机制

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();
                }
            }
        }
    }
}

JDBC工具类的封装

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);
        }
    }
}

JDBC的使用

标签:基础上   hashmap   exe   sel   完成   下标   div   ref   查看   

原文地址:https://www.cnblogs.com/xiaokw/p/13569464.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!