JDBC简介_jdbc的介绍-程序员宅基地

技术标签: 编辑器  mysql  数据库  

JDBC简介
一、JDBC简介
JDBC(Java DataBase Connectivity, java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 java 语言编写的类和接口组成。JDBC 提供了一个种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC 也是个商标名。

jdbc的本质:
jdbc是sun公司制定的一套接口(interface)
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程呢?
解耦合:降低程序的耦合度,提高程序的扩展力。

思考:为什么SUN制定一套JDBc接口呢?
因为每一个数据库的底层实现原理都不一样。oracle数据库有自己的原理。
MysQL数据库也有自己的原理。
Ms sqlserver数据库也有自己的原理。
每一个数据库产品都有自己独特的实现原理。

二、JDBC 的开发步骤
1)注册驱动:主要告诉 JVM 我们的程序将要使用哪一种数据库
注册驱动的方法:1.DriverManager.registerDriver(new com.mysql.jdbc.Driver());
2.利用反射机制(常用):Class.forName(“com.mysql.jdbc.Driver”);
常用原因:参数是一个字符串,字符串可以写到xxxx.properties文件中
这个方法不需要返回值,因为我们只想用他的类加载动作
2)获取连接:使用 JDBC 中的类,获得数据库的连接对象 Connection
要知道数据库URL,用户名,密码
URL:包括协议,IP地址,端口,资源名 http://39.156.66.14/index.html
就像mysql的URL一样 url=jdbc:mysql://127.0.0.1:3306/database
3)获取数据库操作对象:通过 Connection 可以获取执行者对象,Statement、PreparedStatement.
4)执行 SQL 语句:使用执行者对象,向数据库中执行 SQL 语句,然后可以得到对应的接口,有单个结果,也可能有结果集 ResultSet。
5)处理查询结果集(只有当第四步执行的是select语句的时候,才有这一步)
6)释放对象:关闭顺序:rs -> stmt 、ptmt -> conn
关闭顺序要遵循从小到大依次释放,分别对其try…catch

package com.jdbc;
import java.sql.*;
public class JDBCTest03 {
    public static void main(String[] args)  {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
       try { //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.连接数据库
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
        //3.获取数据库操作对象
        stmt = conn.createStatement();
        //4.执行SQL语句
        String sql = "select username,age,password from user.user";
        rs = stmt.executeQuery(sql);
        //5.处理查询结果集
        //Boolean flag = rs.next();
         /*  if (flag ){
               //光标指向的行有数据
               //取数据
               //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
               String username1 =rs.getNString("1");//JDBC中下标从1开始,以下代码中的123指的是第几列,用SQL查询中的字段更健壮
               String age1 = rs.getString("2");
               String pwd = rs.getString("3");
               System.out.println(username1+","+age1+","+pwd);
           }*/
         /*  while (rs.next() ){
               //光标指向的行有数据
               //取数据
               //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
               String username1 =rs.getNString("username");//以下代码中的123指的是第几列,用SQL查询中的字段更健壮
               String age1 = rs.getString("age");
               String pwd = rs.getString("password");
               System.out.println(username1+","+age1+","+pwd);
           }*/

      /**  while (rs.next() ){
            //光标指向的行有数据
            //取数据
            //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
            String username1 =rs.getNString("username");//JDBC中下标从1开始,以下代码中的123指的是第几列,用SQL查询中的字段更健壮
            String age1 = rs.getString("age");
            String pwd = rs.getString("password");
            System.out.println(username1+","+age1+","+pwd);
        }*/
            while (rs.next() ){ //next()方法的特点:如果有数据则返回true 没数据就返回false
            //光标指向的行有数据
            //取数据
            //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
            String username1 =rs.getNString("username");//得到的类型也可以是多样的,如getInt,getDouble等
            Int age1 = rs.getInt("age");
            Double pwd = rs.getDouble("password");
            System.out.println(username1+","+age1+","+pwd);
        }
        }catch(Exception e){
           e.printStackTrace();
       }finally {
           //6.释放资源 从小到大释放 在final语句中写 确保能执行
           //1.先释放查询结果集 2. 释放数据库操作对象 3. 释放连接 
           
           if (rs !=null){
               try{
                   rs.close();
               }catch (Exception e){
                   e.printStackTrace();
               }
               if (stmt  != null){
                   try{
                       stmt.close();
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               }
               if (conn  != null){
                   try{
                      conn.close();
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               }
           }
       }
    }
}

解决SQL注入问题:
只要用户提供的信息部参与SQL语句的编译过程,问题就得到解决了。
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
想要用户信息部参与SQL语句的编译,那么就必须使用java.sql.PreparedStatement
PreparedStatement接口继承了java.sql.statement
PreparedStatement是属于预编译的数据库操作对象。
PreparedStatement 的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值。
解决SQL注入的关键是:即使输入的信息中含有SQL语句中的关键字,但是这些关键字并没有参与编译,不起作用。
使用PreparedStatement防注入

package com.jdbc;

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

public class JDBCTest05 {
    public static void main(String[] args) {
        //1.初始化一个界面
         Map<String,String> userInfo = initUI();
        //2.验证用户名和密码
        boolean loginSuccess = login(userInfo);
        System.out.println(loginSuccess ? "登录成功":"登录失败");
    }
    private static boolean login(Map<String, String> userInfo) {
        //设置一个标记值
        Boolean loginSuccess = false;
        //String name = userInfo.get("name");
        //String psw = userInfo.get("psw");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.注册数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取数据库连接            conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
            // SQL语句的框子,其中一个?代表一个占位符,一个?将来会接收一个‘值’。注意占位符不能用单引号括起来,别人会被认为是一个字符串
            //3.编写SQL语句
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            //4.创建数据库操作对象
            ps = conn.prepareStatement(sql);
            ps.setString(1,userInfo.get("name"));//因为设置的值是String类型的,所以在什么SQL语句中会自动添加单引号
            ps.setString(2,userInfo.get("psw"));
            //5.执行SQL语句 注意要传完值之后再执行SQL语句
            rs = ps.executeQuery();
            //6.释放资源
            if (rs.next()){
               loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        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;
    }
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String  name = s.nextLine();
        System.out.println("请输入密码:");
        String psw = s.nextLine();
        Map<String,String> userInfo = new HashMap<>();
        userInfo.put("name",name);
        userInfo.put("psw",psw);
        return userInfo;
    }
}

对比Statement和PreparedStatement
1.Statement存在SQL注入问题。PreparedStatement解决了SQL注入问题
2.Statement执行效率低,编译一次执行一次 。PreparedStatement是编译一次课执行n次。因为执行SQL语句的特点,如果第二次执行相同的SQL语句,会直接执行,而Statement的语句每次都不一样,PreparedStatement的SQL语句是用占位符的,每次执行都一样。
3.PreparedStatement会在编译阶段进行安全检查。
综上所述:大多的情况下都使用PreparedStatement,只有极少数情况下用statement

5.使用statement的情况:业务要求要进行SQL注入的情况下才用。例如商品的下拉框的选项,升序或降序的情况下用Statement进行SQL注入
例如

package com.jdbc;

import java.sql.*;
import java.util.Scanner;
public class JDBCTest06desc {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入升序desc或降序asc:");
        String kw = s.nextLine();
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1.注册连接驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
            //3.获取数据库操作对象
            stmt = conn.createStatement();
            //4.执行SQL 这样写SQL会有SQL注入问题 如在输入用户名时输入abc' or '1' = 1'
            String sql = "select loginName from t_user order by loginName " + kw;
            //5.处理结果集
            rs = stmt.executeQuery(sql);
            while (rs.next())  {
                System.out.println(rs.getString("loginName"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.关闭资源
            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();
                }
            }
        }
    }
}

三、JDBC事务
1.JDBC中的事务是自动提交的
自动提交:只要执行任意一条DML语句。则自动提交一次。这是JDBC默认的事务行为。但在实际的业务中,通常都是n条DML语句共同联合才能完成的,必须保证他们这些DML语句在通过一个事务中同时成功或同时失败。

  conn.setAutoCommit(false);//开启事务 关闭 JDBC的自动提交事务 在获取数据库连接之后开启
//在事务结束时 提交事务
  conn.commit();//提交事务
   if(conn !=null)
            try {
                conn.rollback();//事务回滚 遇到异常就回滚
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }

ACID
原子性:要么同时成功,要么同时失败

一致性:结果总数不变。就像转账,转账之前和转账之后的总数不变

持久性:一旦sql执行就不可更改

隔离性:事务间互不影响

会出现三种问题

  脏读:一个事务读到了另一个没有提交的事务

  不可重复读:在同一个事务中多次读取同一条记录,读出的结果不同

  虚读( 幻读):在一个事务中读到了别的事务中插入的数据,导致前后读出的结果不一致

四、悲观锁和乐观锁

行级锁: 在SQL语句后面加 for update 查出来的数据就锁住了,不能修改 又称为悲观锁
事务1—>读取到版本号1.1
事务2—>读取到事务1.1
其中事务1先修改了,修改之后看了版本号是1.1,于是提交修改的数据,并将版本号改成1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2,和它最初读的版本号不一致,于是就回滚。
悲观锁特点:事务必须排队执行。数据锁住了,不允许并发。
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/O_Y_Y_K/article/details/121022784

智能推荐

ASP.NET 打印、导出 【转载】-程序员宅基地

文章浏览阅读61次。我是菜鸟,我是新手。前几天写程序时,应客户要求加上打印和数据导出功能,在过去从未做过类似的,就在网上搜了一搜,结果云云,方法云云。试过好多都要出错。于是乎,拿过来好几个,相比对照之下,终于弄出来了,记录下来,以后会用得着,也给予同我一样的新手借鉴。导出功能,还是并非我最想要的(怎么能把浏览器下载那个提示框取消了,就如同保存图片一样),有高手路过,请指点一二......*****..._asp.net中的导出(打印)

《uboot与linux内核间的参数传递过程分析》-程序员宅基地

文章浏览阅读527次。1.内核中对boot loader描述(源码位于kernel中的Documentation/arm/booting)4. Setup boot data------------------Existing boot loaders: OPTIONAL, HIGHLY RECOMMENDEDNew boot loaders: MA..._uboot通过向设备树增加节点方式向linux内核传递启动参数

CSS科技感四角边框_有科技感的css边框-程序员宅基地

文章浏览阅读1.4k次。CSS科技感四角边框_有科技感的css边框

QT5 学习之路21---文件对话框_qt5 不显示对话框直接打开存在的文件-程序员宅基地

文章浏览阅读1.9k次。在前面的章节中,我们讨论了 Qt 标准对话框QMessageBox的使用。所谓标准对话框,其实也就是一个普通的对话框。因此,我们同样可以将QDialog所提供的其它特性应用到这种标准对话框上面。今天,我们继续讨论另外一个标准对话框:QFileDialog,也就是文件对话框。在本节中,我们将尝试编写一个简单的文本文件编辑器,我们将使用QFileDialog来打开一个文本文件,并将修改过的文件保存到硬盘_qt5 不显示对话框直接打开存在的文件

归一化 均值归一化_归一化折现累积收益-程序员宅基地

文章浏览阅读1.1k次。归一化 均值归一化Do you remember the awkward moment when someone you had a good conversation with forgets your name? In this day and age we have a new standard, an expectation. And when the expectation is not..._折扣累积增益

基于C#的AE二次开发之测量点坐标txt数据转Shapefile点数据_c# ae txt经纬度坐标点数据转shp矢量点-程序员宅基地

文章浏览阅读1.5k次,点赞3次,收藏24次。基于C#的AE二次开发之测量点坐标txt数据转Shapefile点数据1、数据准备2、搭建平台框架3、代码块string str_docx = this.textBox1.Textstring[] files = Directory.GetFiles(str_docx, "*.docx").Union(Directory.GetFiles(str_docx, "*.doc")).ToArr..._c# ae txt经纬度坐标点数据转shp矢量点

随便推点

解决mysql在Windows终端显示中文乱码问题_mysql utf8mb4 字符windows 显示汉字乱码-程序员宅基地

文章浏览阅读798次。原因:mysql一般默认是utf8字符,而在Windows上都是标准的GBK格式,所以会出现中文乱码:解决:#include "windows.h" //一定要添加这个头文件std::string UtfToGbk(const char* utf8){ int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); wchar_t* w..._mysql utf8mb4 字符windows 显示汉字乱码

vue 调色板_使用vue.js从图像中获取主要调色板-程序员宅基地

文章浏览阅读1.8k次。vue 调色板 节点振动的 (node-vibrant)Extract prominent colors from an image (node-vibrant). 从图像中提取突出的颜色(节点充满活力)。 View Demo查看演示 View Github 查看Github v3.0中新的WebWorker支持 (New WebWorker support in v3.0)Quan..._vibrant js 用法

spring+jpa_spring-statemachine+jpa-程序员宅基地

文章浏览阅读1.4k次。一、在applicationContent配置: --> --> 二、jpa配置文件(jpaPersistence.xml) xmlns=_spring-statemachine+jpa

欧姆龙rxd指令讲解_欧姆龙cp1h常用指令学习(四)串口无协议读写指令TXD,RXD...-程序员宅基地

文章浏览阅读4.8k次。(六)串口无协议读写指令RXD、TXD有了串品读写,PLC就可以发信息给上位机,这样可以做许多有意思的事情,比如写个小游戏,可以让PLC做逻辑,上位机程序负责更新界面。RXD为读串口,TXD为写串口。下面的程序中,A526.01的作用如下:当通讯突然中断时,缓冲区内的数据一直保存,事实上,RXD指令并不负责清空缓冲区,换句话说,缓冲区内始终为接收的数据。所以,如果遇到这种问题,你可以执行A526...._欧姆龙串口指令

利用Tess4J进行验证码识别_图形验证码增加斜线java-程序员宅基地

文章浏览阅读2.2k次,点赞4次,收藏5次。前言最近爬虫兴起,为了避免被爬,很多网站会加入随机图像验证码来设下防线,这时候就需要我们进行图像识别再进行登录操作了。另外,项目中也可能多多少少的用到图像识别功能。所以,今天我们来讲解一下Java的图像识别。正文我在网上搜了一下,Java的图像识别开源免费的就Tess4J做得还不错。① 首先,我们直接引入maven依赖: <!--图像识别tess4j--> <dependency> <groupId>net._图形验证码增加斜线java

No module ‘xformers‘. Proceeding without it._no module 'xformers'. proceeding without it.-程序员宅基地

文章浏览阅读3.5w次,点赞11次,收藏23次。通过python命令行安装确认运行时python版本,不确定的话进创建的虚拟环境中找到python文件运行下述命令 stable-diffusion-webui-master\虚拟环境名称\Scripts\python.exe)下载xformers-0.0.14.dev0-cp310-cp310-win_amd64.whl文件。_no module 'xformers'. proceeding without it.

推荐文章

热门文章

相关标签