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

JDBC学习入门

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDBC学习入门

目录

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.mshxw.com
本文地址:https://www.mshxw.com/it/281953.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号