JDBC

JDBC

jdbc本质就是一套接口,程序员不需要关心数据库的具体品牌,只需要面向jdbc接口写代码。

驱动

所有的数据库驱动都是以jar包的形式存在,jar包中有许多.class文件(驱动就是实现jdbc接口的实现类)。

JDBC编程

  • 注册驱动(告诉java程序,即将连接的是那个品牌的数据库)
  • 获取连接(表示jvm的进程和数据库进程之间的通道打开了。使用完要关闭)
  • 获取数据库操作对象(创建执行数据库的对象)
  • 执行sql语句(DQL,DML。。。)
  • 处理查询结果集
  • 释放资源(使用完资源之后一定要关闭,java和数据库属于进程间的通信,开启之后一定要关闭)
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;

public class JDBCTest{
    
    public static void main(String [] args){
        Connection conn=null;
        Statement stmt=null;
        try{
            //注册驱动
            Driver driver=new com.mysql.jdbc.Driver();//父类型引用指向子类对象
            DriverManager.registerDriver(driver);
            //注册驱动的第二中方法;因为参数是一个字符串,字符串可以写到.properties文件中。不需要接受返回值是因为我们只想用它的类加载动作。
            //Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            String url="jdbc:mysql://192.168.151.80:3306/userDb";
            String user="root";
            String password="root";
            conn=DriverManager.getConnection(url,user,password);
            System.out.println("数据连接对象"+conn);
            //获取数据库操作对象(Statement专门执行SQl语句的)
            stmt=conn.createStatement();
            //执行sql
            String sql="";
            
            
        }
        catch(SQLException e){
            e.printStackTrace();
        }finally{
            //释放资源
            try{
                if(stmt!=null){
                    stmt.close();
                }
            }catch(SQLException e){
                    e.printStackTrace();
                }
             try{
                if(conn!=null){
                    conn.close();
                }
            }catch(SQLException e){
                    e.printStackTrace();
                }
        }
    }
}


//实际开发中会将连接数据库的信息写在配置文件中,不会直接写在java程序代码中
//使用资源绑定器绑定属性配置文件
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");
//配置文件jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.151.80:3306/userDb
user=root
password=root

SQL注入

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

解决SQL注入:只要用户提供的信息不参与sql语句的执行就可以了,即使用户提供的信息包括sql关键词但没有参与到编译就不起作用了。

要想用户提供的信息不参与编译就要使用到java.sql.PreparedStatement;

PreparedStatement接口继承了Statement;PreparedStatement属于预编译数据库操作对象,在用户提供信息之前将sql语句进行了预编译。即使用户提供的信息含有sql关键字但不参与sql编译所以不起作用。

PreparedStatement ps=null;
//在获取数据库操作对象是使用PreparedStantement
//sql语句,问号代表一个占位符用于接收用户输入的值
String sql="select * from 表名 where loginName=? and loginPwd=?";
//程序执行到此处会将sql语句进行预编译,使用的是PrepareStatement方法
ps.conn.PrepareStatement(sql);
//给占位符赋值,数字1代表第一个问号;因为jdbc下标是从1开始的。
ps.setString(1,loginName);
ps.setString(2,loginPwd);

Statement和PreparStatement对比

  • Statement存在sql注入问题,PreparStatement不存在sql注入问题;
  • Statement是编译一次执行一次,PreparStatement是编译一次可以执行N次,效率较高;
  • PreparStatement在编译阶段会进行类型的安全检查;

并不是在以后不使用Statement而是在需要进行sql注入时使用Statement

jdbc事务

jdbc的事务是自动提交的,只要执行一条sql语句就会自动提交一次;

解决jdbc事务自动提交:

//在获取连接后关闭事务自动提交,开启事务
conn.setAutoCommit(false);
//执行完sql语句后进行提交,事务的提交
conn.commit;
//如果有错进行事务回滚
conn.rollback();

封装jdbc工具类

public class DButil{
    
    private DButil(){}
    //静态代码块在类加载时执行,并且只执行一次
    static{
        try{
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    //获取数据库连接
    public static Connection getConnection() throws SQLexception{
        return DriverManager.getConnection("jdbc:mysqk://localhost:3306/userDb","root","root");
        
    }
    //释放资源
    public static void close(Connection conn,Statement ps,ResultSet rs){
        //结果集
        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();
            }  
        }
        
    }
}

悲观锁/行级锁:在sql语句后加上for update;将查询的语句进行加锁其它无法进行更改

乐观锁:指线程1访问后会在数据一个版本号,线程2访问后并进行更改会将版本号更改,当线程1再次访问时数据以被更改太它会回滚到以前的版本;

posted @ 2021-01-04 01:18  L#  阅读(42)  评论(0编辑  收藏  举报