栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

JDBC学习入门

Java 更新时间:发布时间: 百科书网 趣学号

目录

1.JDBC概述及配置

1.1 概述

1.2 配置

2.JDBC的开发

2.1 JDBC编程六步概述

2.2 具体代码实现

3.SQL注入

3.1 SQL注入现象

3.2 Statrment和PreparedStatement的对比

3.3 PreparedStatement实现增删改

4.JDBC的事务自动提交机制

4.1 JDBC的事务机制

5.JDBC的模糊查询

6.设置行级锁


1.JDBC概述及配置

1.1 概述

   JDBC:Java Database Connectivity。java语言连接数据库。

   JDBC的本质:是SUN公司编写的一套接口interface。在java.sql.*软件包下有很多接口。不同的数据库的底层实现原理都不一样,而不同的数据库如mysql、oracle都去实现这套接口,不同的数据库有不同的这些接口的实现类。

   接口都有调用者和实现者,面向接口调用、面向接口写是实现类都属于面向接口编程(为了解耦合,降低程序的耦合度,提高程序的扩展力)。

1.2 配置

   JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中,配置classpath时开头必须有".;"(.表示当前路径)。配置jar包就是为了获得数据库对于JDBC接口的实现类。当前配置的版本是5.1.23。

   以上配置针对于文本编辑器的开发方式,使用IDEA工具的时候,不需要配置以上的环境变量,IDEA有自己的配置方式。

   IDEA配置JDBC:

2.JDBC的开发

2.1 JDBC编程六步概述

   ①注册驱动:告诉JVM即将要连接的是哪个数据库

   ②获取连接:表示JVM的进程和数据库进程之间的通道打开了。属于进程间的通信,是重量级的,使用完之后一定要关闭通道。

   ③获取数据库操作对象(专门执行sql语句的对象):有个该对象,才可以执行sql语句。

   ④执行sql语句:DQL、DML语句。

   ⑤处理查询结果集:只有当第四步执行的是select语句的时候,才有这一步。

   ⑥释放资源:使用完资源之后一定要关闭资源。

2.2 具体代码实现
import java.sql.*;

public class JDBCTest01{
	public static void main(String[] args){
		 Connection conn = null;
         Statement stmt = null;
		 try{
		      //1.注册驱动
		      Driver driver = new com.mysql.jdbc.Driver();//注意:前面的Driver是一个接口,属于java.sql包下的,后面的Driver是接口Driver的一个Mysql实现类
		                                             //实现了多态,父类型引用指向子类型对象
		      DriverManager.registerDriver(driver);
			  //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
              //2.获取连接
			  String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";//当前电脑上的数据库bjpowernode
			  String user = "root";//用户名
			  String password = "Axxyneymar123";//密码
			  conn = DriverManager.getConnection(url,user,password);
			  //conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123"); 		  
			  System.out.println("数据库连接对象 = " + conn); 
              //3.获取数据库操作对象
              stmt = conn.createStatement();
              //4.执行sql语句
			  String sql1 = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";//插入
			  int count1 = stmt.executeUpdate(sql1);//专门执行DML语句的函数(insert,delete,update),返回值是“该DML语句影响数据库中的记录条数”
			  System.out.println(count1 == 1 ? "保存成功":"保存失败");
		 }catch(SQLException e){
		      e.printStackTrace();
		 }finally{
		      //6.释放资源
			  //为了保证资源一定释放,在finally语句块中关闭资源,并且要遵循从小到大依次关闭。
			  //分别对其try...catch
			  if(stmt != null){
				   try{
				        stmt.close();
				   }catch(SQLException e){
			            e.printStackTrace();
				   }
			  }
			  if(conn != null){
				   try{
				        conn.close();
				   }catch(SQLException e){
			            e.printStackTrace();
				   }
			  }
		 }
	}
}

   关于上述代码中的url:

        URL:统一资源定位符(网络中某个资源的绝对路径)。

        URL包括哪几部分:

                ①协议:通信协议是通信之前就提前定好的数据传送格式,数据包具体怎么传数据。

                ②IP

                ③端口PORT

                ④资源名

        比如:http://182.61.200.7:80/index.html   http://是通信协议,182.61.200.7是服务器IP地址,

      80是服务器上软件的端口,index.html是服务器上某个资源名。

        注意:localhost和127.0.0.1都是本机IP地址。

        Oracle的URL:jdbc:oracle:thin:@localhost:1521:orcl

   JDBC中的sql语句不需要分号;结尾,加上了分号会报错。

   利用反射使用类加载的方式注册驱动:

import java.sql.*;

public class JDBCTest03{
	public static void main(String[] args){
	     try{
			  //注册驱动的第二种方式,常用的。
			  //为什么常用?因为参数是一个字符串,字符串可以写到xxx.properties文件中。
			  //以下方法不需要返回值,因为我们只想用它的类加载动作
		      Class.forName("com.mysql.jdbc.Driver");//mysql的实现类的源代码内有一个静态代码块来实现驱动的注册。
			                                         //直接利用反射机制在类加载的时候就会执行静态代码块。
			  Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123"); 
			  System.out.println("数据库连接对象 = " + conn);
		 }catch(SQLException e){
		      e.printStackTrace();
		 }catch(ClassNotFoundException e){
		      e.printStackTrace();
		 }
	}
}

   com.mysql.jdbc.Driver的源码中有一个静态代码块,该代码块中调用了DriverManager.registerDriver()

   使用资源绑定器来绑定配置文件jdbc.properties,这样子不用把java代码写死,之后只要修改配置文件就行。

//将连接数据库的所有信息配置到配置文件中
import java.sql.*;
import java.util.*;

public class JDBCTest04{
	public static void main(String[] args){
		 //使用资源绑定器绑定配置文件
		 ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
		 String driver = bundle.getString("driver");
         String url = bundle.getString("url");
		 String user = bundle.getString("user");
		 String password = bundle.getString("password");
	     Connection conn = null;
         Statement stmt = null;
		 try{
			  Class.forName(driver);
              //2.获取连接
			  conn = DriverManager.getConnection(url,user,password); 		  
			  System.out.println("数据库连接对象 = " + conn); 
              //3.获取数据库操作对象
              stmt = conn.createStatement();
              //4.执行sql语句
			  String sql1 = "update dept set dname = '销售部',loc = '天津' where deptno = 20";
			  int count1 = stmt.executeUpdate(sql1);
			  System.out.println(count1 == 1 ? "修改成功":"修改失败");
		 }catch(SQLException e){
		      e.printStackTrace();
         }catch(ClassNotFoundException e){
			  e.printStackTrace();
		 }finally{
		      //6.释放资源
			  if(stmt != null){
				   try{
				        stmt.close();
				   }catch(SQLException e){
			            e.printStackTrace();
				   }
			  }
			  if(conn != null){
				   try{
				        conn.close();
				   }catch(SQLException e){
			            e.printStackTrace();
				   }
			  }
		 }
	}
}

   关于处理查询到的数据集:java.sql.ResultSet接口。

import java.sql.*;
import java.util.*;
public class JDBCTest05{
	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://127.0.0.1:3306/bjpowernode","root","Axxyneymar123");
			  //3.获取数据库操作对象
			  stmt = conn.createStatement();
			  //4.执行sql语句
			  String sql = "select empno as a,ename,sal from emp";
			  rs = stmt.executeQuery(sql);//专门执行DQL语句的方法
			  //5.处理查询结果集
			  while(rs.next()){//返回true代表这一行有数据
			       //String empno = rs.getString(1);//1,2,3代表第几列,但不建议使用这种方式
				   //String ename = rs.getString(2);
				   //String sal = rs.getString(3);
                   String empno = rs.getString("a");//以列的名字获取,若重命名,必须写重命名后的名字。
				                                    //重点注意:列名称不是表中的列名称,是查询结果集的列名称。
				   String ename = rs.getString("ename");
				   String sal = rs.getString("sal");
				   System.out.println(empno + "," + ename + "," + sal);
                   
				   //除了可以以String类型取出之外,还可以以特定的类型取出
                   int empno = rs.getInt("a");
				   String ename = rs.getString("ename");
				   double sal = rs.getDouble("sal");
				   System.out.println(empno + "," + ename + "," + (sal + 100));//若使用double,可以直接加法运算
			  }
		 }catch(Exception e){
		      e.printStackTrace();
		 }finally{
		      //释放资源
              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();
				   }
			  }
		 }
	}
}

3.SQL注入

3.1 SQL注入现象

   比如下面这个sql语句:

//模拟用户登录
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
//只有在数据库中能找到一条记录,才说明登录成功

   此时如果输入:

        数据库中没有该用户,但是仍显示登录成功。

        此时sql语句相当于:'1'='1'恒成立,且是or表示或者,因此where后面的条件一直成立,所以

      就能查出数据记录了,而判断逻辑就是有结果数据就说明登录成功,因此显示登录成功。本质

      上就是正好完成了sql语句的拼接,使其可以组成一个正确的sql语句。

   这种现象称为SQL注入。

   导致SQL注入的原因:用户输入的信息中含有sql语句的关键字,并且这些关键字会参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。

   解决办法:

        只要用户提供的信息不参与sql语句的编译过程,问题就解决了。即使用户提供的信息中含有

      sql语句的关键字,但是没有参与编译,不起作用。要想用户信息不参与sql语句的编译,那么

      必须使用java.sql.PreparedStatement接口,该接口继承了java.sql.Statement。

      PreparedStatement是属于预编译的数据库操作对象,其原理是预先对sql语句的框架进行编

      译,然后再给sql语句传"值"。?作为值的占位符。

   代码改为:

//JDBC代码
        Connection conn = null;
        PreparedStatement ps = null;//这里使用PreparedStatement(预编译的数据库操作对象)
        ResultSet rs = null;
        try{
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123");
            //3.获取预编译的数据库操作对象
            //SQL语句的框子,其中一个?表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来。
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";//SQL语句的框架
            //程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译
            ps = conn.prepareStatement(sql);
            //给占位符?传值(第1个问号小标是1,第2个问号下标是2,JDBC中所有下标从1开始)
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            //4.执行sql
            rs = ps.executeQuery();//此处参数不能再加sql了,加上了会再进行一次编译。
            //5.处理结果集
            if(rs.next()){
                //如果rs.next()返回true说明查找到了记录,说明登录成功,标记置为true
                loginSuccess = true;
            }

3.2 Statrment和PreparedStatement的对比

   对比:

        ①Statement存在sql注入问题;PreparedStatement解决了sql注入问题。

        ②Statement是编译一次执行一次,PreparedStatement是编译一次可执行N次,只要传值就

          行,因此PreparedStatement效率较高一些。

        ③PreparedStatement会在编译阶段做类型的安全检查,传入的值的类型是否与要求一致。

   综上所述:PreparedStatement使用较多,只有极少数的情况下会使用Statement。

   什么情况下必须使用Statement?

        业务方面要求必须支持SQL注入的时候。比如需要升序降序时,如果使用setString进行传值

      给占位符,会给升序降序关键字加上单引号'',导致sql语句不正确了,此时需要使用到拼接。

 //用户在控制台输入desc就是降序,输入asc就是升序
        Scanner s = new Scanner(System.in);
        System.out.println("输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入:");
        String keyWords = s.nextLine();
        //执行sql语句
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123");
            //获取数据库操作对象
            stmt = conn.createStatement();
            String sql = "select ename from emp order by ename "+keyWords+"";
            //或者String sql = "select ename from emp order by ename" + keyWords;
            rs = stmt.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("ename"));
            }

3.3 PreparedStatement实现增删改
Connection conn = null;
        PreparedStatement ps = null;
        try{
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123");
            //3.获取预编译的数据库操作对象
            
            
            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,60);
            //4.执行sql语句
            int count = ps.executeUpdate();
            System.out.println(count);

4.JDBC的事务自动提交机制

4.1 JDBC的事务机制

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

   用一个账户转账的例子来实现事务:

public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","Axxyneymar123");
            //将自动提交机制修改为手动提交
            conn.setAutoCommit(false);//开启事务
            //3.获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1,10000);
            ps.setInt(2,111);
            int count = ps.executeUpdate();

            String s = null;
            s.toString();

            ps.setDouble(1,10000);
            ps.setInt(2,222);
            count += ps.executeUpdate();
            //程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
            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();
                }
            }
        }
    }
}

   重点代码:

        Connection对象conn。

        conn.setAutoCommit(false);

        conn.commit();

        conn.rollback();

5.JDBC的模糊查询

   JDBC模糊查询的格式:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs= null;
            
//错误写法,问号旁边不应该有符号


String sql = "select ename from emp where ename like ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"_A%");
rs = ps.executeQuery();
while (rs.next()){
    System.out.println(rs.getString("ename"));
}

6.设置行级锁

   在select语句中末尾加上for update就会把这几条记录锁住,即加上了行级锁(悲观锁)。下面用两个事务来举个例子,debug后发现两个事务必须排队进行。

public class JDBCTest13 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DBUtil.getConnertion();
            //开启事务
            conn.setAutoCommit(false);
            //在sql语句后面加上for update就会加上行级锁
            String sql = "select ename,job,sal from emp where job = ? for update";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");

            rs = ps.executeQuery();
            while (rs.next()){
                System.out.println(rs.getString("ename") + " " + rs.getString("job") + " " + rs.getDouble("sal"));
            }
            //提交事务(事务结束)
            conn.commit();
        } catch (Exception e) {
            if(conn != null){
                try {
                    //回滚事务(事务结束)
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally{
            DBUtil.close(conn,ps,rs);
        }
    }
}
public class JDBCTest14 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps =null;
        try {
            conn = DBUtil.getConnertion();
            conn.setAutoCommit(false);
            String sql = "update emp set sal = sal*1.1 where job = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");
            int count = ps.executeUpdate();
            System.out.println(count);
            conn.commit();
        } catch (Exception e) {
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);//没有这个参数就写null
        }
    }
}

PS:根据动力节点课程整理,如有侵权,联系删除。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/281953.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号