Java——JDBC(1)——JDBC介绍、JDBC涉及的类对象、JDBC事务管理

目录

1.JDBC的介绍

(1)是什么?

(2)怎么用

2.各个类对象的详解

(1)DriverManager(驱动管理对象)

(2)Connection(数据库连接对象)

(3) Statement(执行sql的对象)

(4)ResultSet(结果集对象)

(5)PreparedStatement(执行sql的对象)

3.抽取JDBC工具类 ——JDBCUtils

4.JDBC事务管理


1.JDBC的介绍

(1)是什么?

概念:Java DataBase Connectivity  Java 数据库连接, Java语言操作数据库

JDBC本质:其实是官方定义的一套操作所有关系型数据库的规则,即接口。每个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

(2)怎么用

步骤:

  • 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
  •         1)复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
  •         2)右键-->Add As Library
  • 2. 注册驱动
  • 3. 获取数据库连接对象 Connection
  • 4. 定义sql
  • 5. 获取执行sql语句的对象 Statement
  • 6. 执行sql,接受返回结果
  • 7. 处理结果
  • 8. 释放资源

使用示例:

准备好驱动jar包

  • 新建一个项目,在项目下新建一个libs文件夹,专门用于保存要用的库的包
  • 直接将要用mysql的jar包复制到libs目录下,然后右键单击该文件夹libs------>Add As Library

   

示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class Main {

    public static void main(String[] args) throws Exception {

        //1. 导入驱动jar包
        //2.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //3.获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb","root","");
        //4.定义sql语句
        String sql = "update sc set grade = 90 where sid = 'S1'";
        //5.获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();
        //6.执行sql
        int count = stmt.executeUpdate(sql);
        //7.处理结果
        System.out.println("处理结果:"+count);
        //8.释放资源
        stmt.close();
        conn.close();
    }
}

代码执行前,mydb数据库中的sc表的数据如下:

代码执行后的结果:

        

2.各个类对象的详解

(1)DriverManager(驱动管理对象)

功能:

  • 1.注册驱动:告诉程序该使用哪一个数据库驱动jar

              查看JDK的API文档,可以发现它有如下方法

而写代码时使用下述代码?

 Class.forName("com.mysql.jdbc.Driver");   //把com.mysql.jdbc.Driver这个类加载进内存

通过查看源码发现:com.mysql.jdbc.Driver类中存在静态代码块(随着类的加载会被执行),其中调用了registerDriver方法

注意:mysql5之后的驱动jar包可以省略注册驱动的步骤,因为在mysql5之后的jar包中的java.sql.Driver文件中已经帮我们把com.mysql.jdbc.Driver类写到该文件中,写到该文件它就可以帮我们自动地注册驱动,不过建议写上此段代码

  • 2. 获取数据库连接:

   方法:

 参数说明:

  • url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称

例子:jdbc:mysql://localhost:3306/mydb

细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
  • user:用户名
  • password:密码 

(2)Connection(数据库连接对象)

功能:

  • 1. 获取执行sql 的对象


  • 2. 管理事务:见下文JDBC事务管理

(3) Statement(执行sql的对象)

功能:

执行sql

  • 1)执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
  • 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功,如果返回值>0的则执行成功,反之,则失败。
  • 2)执行DQL(select语句)

练习:为sc表 添加一条记录

添加前:

代码:

import java.sql.*;

public class Main {

    public static void main(String[] args){

        Connection conn=null;
        Statement state=null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.定义sql
            String sql="insert into sc values('S6','C2',93)";
            //3.获取connection对象
            conn= DriverManager.getConnection("jdbc:mysql:///mydb","root","");
            //4.获取执行sql的对象
            state=conn.createStatement();
            //5.执行sql
            int count=state.executeUpdate(sql);  //影响的行数
            //6.处理结果
            if(count>0)
            {
                System.out.println("添加成功");
            }
            else{
                System.out.println("添加失败");
            }
            

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
        
        //7.释放资源
            //如果conn或state在没被初始化时,就出现异常,则此处如果直接释放资源,会报空指针异常
            //conn.close();
            //state.close();

            //为了防止空指针异常需要判断是否为空
            if(conn!=null)
            {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(state!=null)
            {
                try {
                    state.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

添加后:

(4)ResultSet(结果集对象)

功能:封装查询结果

  • 1)光标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
  • 2)getXxx(参数):获取数据    Xxx:代表数据类型   如: int getInt() ,    String getString()
  •  参数:
  •     1. int:代表列的编号,从1开始   如: getString(1)代表获取结果集的第一列
  •     2. String:代表列名称。 如: getDouble("balance")代表获取结果集中的balance列

注意:
使用步骤:

  • next函数完成:
  •     1. 游标向下移动一行
  •     2. 判断是否有数据
  • getXxx函数完成:
  •     3. 获取数据

使用演示:

import java.sql.*;

public class Main {

    public static void main(String[] args){

        Connection conn=null;
        Statement state=null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.定义sql
            String sql="select * from sc";
            //3.获取connection对象
            conn= DriverManager.getConnection("jdbc:mysql:///mydb","root","");
            //4.获取执行sql的对象
            state=conn.createStatement();
            //5.执行sql
            ResultSet result=state.executeQuery(sql);  //影响的行数
            //6.处理结果

            //6.1 循环判断游标是否是最后一行末尾。
            while(result.next()){

                //6.2 获取数据
                String sid = result.getString(1);
                String cid = result.getString("cid");
                int grade = result.getInt(3);

                System.out.println(sid + "---" + cid + "---" + grade);
            }


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.释放资源
            //如果conn或state在没被初始化时,就出现异常,则此处如果直接释放资源,会报空指针异常
            //conn.close();
            //state.close();

            //为了防止空指针异常需要判断是否为空
            //释放conn对象
            if(conn!=null)
            {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            //释放state对象
            if(state!=null)
            {
                try {
                    state.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

运行结果:

select查询练习:定义一个方法,查询sc表的数据将其封装为对象,然后装载集合,返回。

  • 1. 定义Sc类
package com.test.jdbc;

public class Sc {
    private String sid;
    private String cid;
    private int grade;

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Sc{" +
                "sid='" + sid + '\'' +
                ", cid='" + cid + '\'' +
                ", grade=" + grade +
                '}'+"\n";
    }
}
  • 2. 定义方法 public List<Sc> findAll(){}
  • 3. 实现方法 select * from sc;
package com.test.jdbc;

import com.test.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class JDBC_test8 {

    public static void main(String[] args) {
        List<Sc> list=new JDBC_test8().findAll();
        //打印list,此处打印是按照Sc的toString方法打印
        System.out.println(list);
        System.out.println();
        System.out.println(list.size());
    }
    /**
     * 查询所有Sc对象
     */
    public List<Sc> findAll()
    {
        Connection conn=null;
        Statement state=null;
        ResultSet result=null;
        List<Sc> list=null;
        try {
            //1.注册驱动
            //2.获取connection对象
            conn= JDBCUtils.getConnection();  //此处使用了博客下文中JDBCUtils类
            //3.定义sql
            String sql="select * from sc";
            //4.获取执行sql的对象
            state=conn.createStatement();
            //5.执行sql
            result=state.executeQuery(sql);  //影响的行数
            //6.处理结果

            //6.1 循环判断游标是否是最后一行末尾。
            Sc sc=null;
            list=new ArrayList<Sc>();
            while(result.next()){

                //6.2 获取数据
                String sid = result.getString(1);
                String cid = result.getString("cid");
                int grade = result.getInt(3);

                sc=new Sc();
                sc.setCid(cid);
                sc.setSid(sid);
                sc.setGrade(grade);

                //装载集合
                list.add(sc);
            }


        }catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.释放资源
            JDBCUtils.close(result,state,conn);   //此处使用了博客下文中JDBCUtils类
        }

        return list;
    }
}

注意:此处的代码使用了后面封装的JDBCUtils类

运行结果:

          

 

(5)PreparedStatement(执行sql的对象)

1)SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

演示:

在SQLyog中使用如下语句建表:

      

需求:

  • 1.通过键盘录入用户名和密码
  • 2.判断用户是否登录成功

代码:

package com.test.jdbc;

import com.test.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class JDBC_test9 {

    public static void main(String[] args) {
        //1.键盘登录,接受用户名和密码
        Scanner scan=new Scanner(System.in);
        System.out.print("请输入用户名:");
        String user=scan.nextLine();
        System.out.print("\n请输入密码:");
        String password=scan.nextLine();

        //2.调用登录方法
        boolean flag=new JDBC_test9().login(user,password);
        if(flag)
            System.out.println("登录成功");
        else
            System.out.println("登录失败");

    }

    /**
     * 登录方法
     */
    public boolean login(String username,String password)
    {
        if(username==null || password==null)
            return false;

        Connection conn=null;
        Statement state=null;
        ResultSet rs=null;
        try {
            //1.注册驱动
            //2.获取connection对象
            conn= JDBCUtils.getConnection();
            //3.定义sql
            String sql="select * from user where username='"+username+"' and password='"+password+"'";
            System.out.println(sql);
            //4.获取执行sql的对象
            state=conn.createStatement();
            //5.执行查询
            rs=state.executeQuery(sql);
            //6.处理结果
            return rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //8.释放资源
            JDBCUtils.close(rs,state,conn);
        }

        return false;
    }
}

正常执行结果:

1

2.

3.

但是当随便输入一个表中不存在的用户,输入密码:a' or 'a ' = 'a时:

这样的密码输入使得sql为:select * from user where username = 'zhang' and password = 'a' or 'a' = 'a' ,

where后的结果恒为true,该sql等价于:select * from user,只要表中有数据,就会返回true

2)解决sql注入问题:使用PreparedStatement对象来解决

PrepareStatement是Statement的子接口,也用来执行sql对象

3)静态SQL和预编译SQL

静态SQL:所有的sql语句在执行前都是拼接好的

预编译的SQL:参数使用?作为占位符,在执行sql的时候,为它赋值就可以了,示例如下:

4)使用步骤:

  • 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
  • 2. 注册驱动
  • 3. 获取数据库连接对象 Connection
  • 4. 定义sql
  •         注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
  • 5. 获取执行sql语句的对象 PreparedStatement  Connection.prepareStatement(String sql) 
  • 6. 给?赋值:
  •        方法: setXxx(参数1,参数2)
  •             参数1:?的位置编号 从1 开始
  •             参数2:?的值
  • 7. 执行sql,接受返回结果,不需要传递sql语句
  • 8. 处理结果
  • 9. 释放资源

上述登录代码改造:

package com.test.jdbc;

import com.test.utils.JDBCUtils;

import java.sql.*;
import java.util.Scanner;

public class JDBC_test9 {

    public static void main(String[] args) {
        //1.键盘登录,接受用户名和密码
        Scanner scan=new Scanner(System.in);
        System.out.print("请输入用户名:");
        String user=scan.nextLine();
        System.out.print("\n请输入密码:");
        String password=scan.nextLine();

        //2.调用登录方法
        boolean flag=new JDBC_test9().login(user,password);
        if(flag)
            System.out.println("登录成功");
        else
            System.out.println("登录失败");

    }

    /**
     * 登录方法
     */
    public boolean login(String username,String password)
    {
        if(username==null || password==null)
            return false;

        Connection conn=null;
        PreparedStatement pstate=null;
        ResultSet rs=null;
        try {
            //1.注册驱动
            //2.获取connection对象
            conn= JDBCUtils.getConnection();
            //3.定义sql
            String sql="select * from user where username = ? and password = ?";

            //4.获取执行sql的对象
            pstate=conn.prepareStatement(sql);
            //5.给?赋值
            pstate.setString(1,username);
            pstate.setString(2,password);

            //6.执行查询
            rs=pstate.executeQuery();
            //7.处理结果
            return rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //8.释放资源
            JDBCUtils.close(rs,pstate,conn);
        }

        return false;
    }
}

注意:后期都会使用PreparedStatement来完成增删改查的所有操作

  • 1. 可以防止SQL注入
  • 2. 效率更高

3.抽取JDBC工具类 ——JDBCUtils

上述的代码存在一个问题,就是每次要连接数据库,我们都会写挺过重复的数据库连接和资源释放的重复代码,我们可以将这些代码抽取出来,进行复用

目的:简化书写,代码复用

分析:

  • 1. 注册驱动也抽取(不会每次都来注册,写在静态代码块中随着类的加载进行)
  • 2. 抽取一个方法获取连接对象
  •              需求:不想传递参数(麻烦),还得保证工具类的通用性。
  •              解决:配置文件:jdbc.properties
  •                        url=
  •                        user=
  •                        password=
  • 3. 抽取一个方法释放资源

(1)新建一个utils包来存放工具类,在utils中新建JDBCUtil类:

(2)JDBCUtil类的代码:

package com.test.utils;

import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

/**
 *JDBC的工具类
 * */
public class JDBCUtils {
    private  static String url;
    private  static String user;
    private  static String password;
    private  static String driver;


    /**
     * 配置文件的读取,只需要读取一次即可拿到这些值,使用静态代码块
     */
    static{
        //读取资源文件,获取值

        try {
            //通过Properties读取
            //1.创建Properties集合类
            Properties pro = new Properties();
            //2.加载文件

            //获取src路径下的文件
            ClassLoader classLoader=JDBCUtils.class.getClassLoader();
            URL res=classLoader.getResource("jdbc.properties");
            String path=res.getPath();
            System.out.println(path);


            pro.load(new FileReader(path));
            //3.获取属性,赋值
            url=pro.getProperty("url");
            user=pro.getProperty("user");
            password=pro.getProperty("password");
            driver=pro.getProperty("driver");
            //4.注册驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     *获取连接
     * @return 连接对象
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

    /**
     * 释放资源
     * @param state 要释放的执行sql的对象
     * @param connection 要释放的连接对象
     */
    public static void close(Statement state,Connection connection)
    {
        //为了防止空指针异常需要判断是否为空
        if(connection!=null)
        {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(state!=null)
        {
            try {
                state.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放资源
     * @param result 要释放的结果集合
     * @param state 要释放的执行sql的对象
     * @param connection 要释放的连接对象
     */
    public static void close(ResultSet result,Statement state, Connection connection)
    {
        //为了防止空指针异常需要判断是否为空
        if(result!=null)
        {
            try {
                result.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        close(state,connection);
    }

}

(3)在src目录下定义配置文件jdbc.properties:

                    

(4)连接数据库执行sql的Main类中代码:

package com.test.jdbc;

import com.test.utils.JDBCUtils;
import java.sql.*;

public class Main {

    public static void main(String[] args){

        Connection conn=null;
        Statement state=null;
        ResultSet result=null;
        try {
            //1.注册驱动
            //2.获取connection对象
            conn=JDBCUtils.getConnection();
            //3.定义sql
            String sql="select * from sc";
            //4.获取执行sql的对象
            state=conn.createStatement();
            //5.执行sql
            result=state.executeQuery(sql);  //影响的行数
            //6.处理结果

            //6.1 循环判断游标是否是最后一行末尾。
            while(result.next()){

                //6.2 获取数据
                String sid = result.getString(1);
                String cid = result.getString("cid");
                int grade = result.getInt(3);

                System.out.println(sid + "---" + cid + "---" + grade);
            }


        }catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.释放资源
            JDBCUtils.close(result,state,conn);
        }

    }
}

(5)运行结果:

     

4.JDBC事务管理

  • 1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
  • 2.事务的三个核心操作:
  •        1) 开启事务
  •        2)提交事务
  •        3)回滚事务
  • 3.使用Connection对象来管理事务
  •       1)开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
  •         
  •         2)提交事务:commit() 
  •         
  •         3)回滚事务:rollback() 
  •         

代码演示:

package com.test.jdbc;

import com.test.utils.JDBCUtils;

import java.sql.*;

public class JDBC_test9 {

    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement pstate1=null;
        PreparedStatement pstate2=null;

        try {
            //1.注册驱动
            //2.获取connection对象
            conn= JDBCUtils.getConnection();
            //开启事务
            conn.setAutoCommit(false);
            //3.定义sql
            String sql1="update account set balance =balance + ? where id = ?";
            String sql2="update account set balance =balance - ? where id = ?";
            //4.获取执行sql的对象
            pstate1=conn.prepareStatement(sql1);
            pstate2=conn.prepareStatement(sql2);
            //5.给?赋值
            pstate1.setInt(1,500);
            pstate1.setInt(2,1);

            pstate2.setInt(1,500);
            pstate2.setInt(2,2);
            //6.执行查询
            int count1=pstate1.executeUpdate();
            //制造一个异常,如果不使用事务,这里就会造成数据不一致
            int error=3/0;

            int count2=pstate2.executeUpdate();
            //7.处理结果
            System.out.println(count1);
            System.out.println(count2);

            //处理完提交事务
            //提交事务
            conn.commit();

        } catch (Exception e) { //不管出现什么异常,都应该回滚,所以这里应该抓取Exception
            //事务回滚
            try {
                if(conn!=null)
                    conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            //8.释放资源
            JDBCUtils.close(pstate1,conn);
            JDBCUtils.close(pstate2,null);
        }

    }

}

代码执行前的表:

如果将代码中使用事务的代码注释:在执行一条语句后,发生异常后出现数据不一致

使用了事务:在执行一条语句后,发生异常后,会回滚到事务执行前的状态

事务使用步骤总结:

  • 1.在连接建立执行之后执行sql之前开启事务
  • 2.在执行完sql之后关闭事务
  • 3.一旦出现不管任何异常(Exception),即在catch中回滚事务
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值