JDBC知识详解(二)

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: JDBC(java database connectivity)驱动程序是对JDBC规范完整的实现,它的存在在JAVA程序与数据库系统之间建立了一条通信的渠道。

JDBC知识详解(一)


JDBC核心API

Statement

三个方法,分别执行三种语句,DQL,DML,DDL语句。


Statement st = conn.createStatement();
String ddl="create table a "+"(ids varchar(6))";
boolean b=st.execute(ddl);
System.out.println(b);

/*这里的返回结果:

                 TRUE:表示有结果集,

       *         False:表示没有结果集

       *         创建失败抛异常

       * 在ddl语句执行如何判断结果,可以根据是否有没有异常,没有则创建成功。

       * 在ddl语句中,创建表,实际上没有结果集返回,因为没有二维表返回,在这里并不是创建成功就是TRUE。但是如果执行select查询语句时,就有结果集返回,即为TRUE。*/


这里显示为FALSE,但是实际上是创建成功了。

不仅如此,进行表的删除等操作都是一样的。

注意点:

public class test {
    public static void main(String[] args) {
        try{
            Class.forName("com.mysql.jdbc.Driver");
            String url="jdbc:mysql://127.0.0.1:3306/test";
            String username="root";
            String password="123456";
            Connection conn= DriverManager.getConnection(url,username,password);
            //创建 Statement
            Statement st= conn.createStatement();
            //执行DML语句
            String dml="insert into k (name,id) values('cc',24) ";
            int b=st.executeUpdate(dml);
            System.out.println(b);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在idea返回结果

在数据库中查询的结果。

在拼接SQL语句时,一定要注意之间的空格,不然容易报错。

ResultSet

      ResultSet代表dql查询结果,是2维结果,其内部维护了一个读取数据的游标,默认情况下,游标在第一行数据之前,当调用next()方法时候,游标会向下移动,并将返回结果集中是否包含数据,如果包含数据就返回TRUE,结果集还提供了很好getXXX方法用于获取结果集游标指向当前行数据。

String sql="select * from k";
ResultSet rs=st.executeQurry(sql);
while(rs.next()){
String str=rs.getString("age"),
System.out.println(str);
}

ResultSetMetaData

ResultSetMetaData:数据结果集的元数据(结果集的相关数据,不是结果集本身)


Connection conn=null;
try{
conn=DBUtils.getConnection();
String sql="select * from k";
Statement st=conn.createStatement();
ResultSet rs=conn.executeQuery(sql);
//结果集元数据
ResultSetMetaData rsm =rs.getMetaData();
int n=rsm.getColumnCount();
for (int i=1;i<=n;i++){
    String name=metaData.getColumnName(i);
    System.out.println(name);
   }
}catch(Exception e){
   e.printStackTrace();
}finally{
 DUBtils.close(conn);
}


Properties

Properties 读取配置文件

properties 是Java中专门用于读取配置文件的API。

  • 其底层就是文本的API
  • Propertics 本身实现MAP接口,内部是散列表
  • Properties限定key和value都是String类型。

Properties常用的API方法

  • load(流) 读取一个配置文件
  • String getProperty(key)读取一个属性值

使用步骤:

  1. 创建properties对象
  2. 利用load方法读取配置文件
  3. 利用getproperty查询属性文件内容。

利用配置文件可以将程序中的参数保存到配置文件中,修改程序参数只需要修改配置文件即可。

  //1.创建properties 对象
        Properties pr=new Properties();
        System.out.println(pr);
        System.out.println(pr.size());
        //2.利用load方法读取文件
        InputStream in = day02.class.getClassLoader().getResourceAsStream("db.properties");
        //执行后,将文件内容读取到了散列表中
        pr.load(in);
        System.out.println(pr);
        System.out.println(pr.size());
        //查找 文件内容,就是读取文件内容
        System.out.println(pr.getProperty("jdbc.password"));
# db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.user=root
jdbc.password=123456


PreparedStatement

preparedStatement 对象用于执行带参数的预编译执行计划。


  //带参数的SQL语句
 String sql="insert into k (name,age) values (?,?)";
// 将SQL发送到数据库,创建执行计划,返回值就是代表SQL语句在服务器的执行计划。
PreparedStatement ps=conn.prepareStatement(sql);
//替换执行计划中的参数,2个参数,顺序不可错,按照序号发送参数。
ps.setString(1,"bb");
 ps.setInt(2,21);
//执行执行计划
int n=ps.executeUpdate();
System.out.println(n);
预防SQL

SQL注入的产生

public class login {
    public static void main(String[] args){
        //获取用户输入
        Scanner in=new Scanner(System.in);
        System.out.println("用户名:" );
        String name=in.nextLine();
        System.out.println("密码:");
        String pwd=in.nextLine();
        //检查登录情况
        boolean pass=login(name,pwd);
        if (pass){
            System.out.println("欢迎你!"+name);
        }else{
            System.out.println("用户名或者密码错误!");
        }
    }
    //检查用户是否能够登录
public static boolean login(String name,String pwd){
 String sql="select * from h where name=\'"+name+"\'"+"and pwd=\'" +pwd+"\' ";
        System.out.println(sql);
        Connection conn=null;
        try {
            conn=DBUtils.getConnection();
            Statement st= conn.createStatement();
            ResultSet rs=st.executeQuery(sql);
            while (rs.next()){
                int  a=rs.getInt("id");
                return a>=1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(conn);
        }
        return false;
    }
}


SQL注入的原因

and 的优先级高于or ,所以当前面的FALSE and FALSE,即为 FALSE,但是 or 后面为TRUE,所以FALSE or TRUE,最终结果为TRUE。

用户输入了含有SQL成分的语句的参数,参数在拼接SQL时候造成了SQL语句的语义改变,改变了SQL语句的执行计划!

防守手段:

拦截用户输入的SQL成分。

select * from h where name=? and pwd =?

这种情况,or 只是一个普通的字符串,不当成SQL成分

这种就可以利用preparedStatement。


public class right {
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        System.out.println("用户名:");
        String name=in.nextLine();
        System.out.println("密码:");
        String pwd=in.nextLine();
        boolean pass=right(name,pwd);
        if (pass){
            System.out.println("登录成功");
        }else {
            System.out.println("登陆失败");
        }
    }
    public static boolean right(String name,String pwd){
        Connection conn=null;
        String sql="select * from h "+" where name=? and pwd=? ";
        System.out.println(sql);
        try {
            conn=DBUtils.getConnection();
            PreparedStatement pr= conn.prepareStatement(sql);
            pr.setString(1,name);
            pr.setString(2,pwd);
            ResultSet rs=pr.executeQuery();
            while (rs.next()){
                int n=rs.getInt("id");
                return n>=1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(conn);
        }
        return false;
    }
}

连接管理

在软件中数据库连接使用非常频繁,如果每次都创建连接,就会造成代码的大量冗余,常规做法是建立数据库连接工具类,封装数据库连接流程,统一数据库连接过程,使用时候可以简化代码。

实现步骤:

  1. 创建数据库连接参数文件,db.properties
  2. 创建DBUtils.java 封装数据库连接方法
  • 利用properties读取配置文件夹中的数据库连接参数
  • 创建方法 getConnection方法封装数据库连接过程。

 3.使用getConnection()连接数据库

public class DBUtils {
        static String driver;
        static String url;
        static String user;
        static String password;
       //静态代码块的目的是从
       static {
        //初始化静态属性
        /*1.利用properties读取配置文件
        * 2.从配置文件中查找相应参数值
        * */
       try{
      Properties pr=new Properties();
      InputSteam in=DBUtils.class.getClassLoader().getResourceAsStream("db.properties");
      pr.load(in);
      System.out.println(pr);
//初始化数据
      driver=pr.getProperty("jdbc.driver");
      url=pr.getProperty("jdbc.url");
      password=pr.getProperty("jdbc.password");
      user=pr.getProperty("jdbc.user");
      in.close();
       }catch(IOException e){
      e.printStackTrace();
       }
       }
    /*封装创建数据库连接的过程,简化数据库的连接*/
    public static Connection getConnection(){
        try {
        Class.forName(driver);
        Connection conn=DriverManager.getConnection(url,user,password);
            return conn;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /*
     * dbutils.java
     * 关闭数据库连接方法,封装复杂的关闭过程
     * */
public static void close(Connection conn){
        if (conn!=null){
            try {
                conn.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

这样可以简化重复操作。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.user=root
jdbc.password=123456
//创建连接
Connection conn=DBUtils.getConnection();
//创建Statement()对象
Statement st=conn.creataStatement();
String st="select *from k";
//查询结果
ResultSet rs=st.executeQuery(sql);
while(rs.next()){
String name=rs.getString("name");
System.out.println("name: "+name);
}
conn.close();

DriverManager 管理数据库连接适合单线程情况,而在多线程并发情况下,为了能够重用数据库连接,同时控制并发连接总数,保护数据库避免连接重载,一定要使用数据库连接池。

连接池技术

数据库连接池的开源实现非常多,dbcp是常用的连接池之一。

导入dbcp。

导包

从模块中搜索对应的包,然后导入时,点击应用后,在确定。


import org.apache.commons.dbcp2.BasicDataSource;
public class day03 {
    public static void main(String[] args)
    throws Exception{
        String driver="com.mysql.jdbc.Driver";
        String url="jdbc:mysql://127.0.0.1:3506/test";
        String password="123456";
        String user="root";
       BasicDataSource ds=new BasicDataSource();
       ds.setDriverClassName(driver);
       ds.setUrl(url);
       ds.setUsername(user);
       ds.setPassword(password);
       //设置连接池的管理参数
        ds.setInitialSize(2);
        ds.setMaxTotal(100);
        //使用连接池中的数据库连接
        Connection conn=ds.getConnection();
        //执行SQL
        String sql="select *from k";
        Statement st=conn.createStatement();
        ResultSet rs=st.executeQuery(sql);
        while(rs.next()){
            String str=rs.getString("age");
            System.out.println(str);
        }
        //归还连接到数据库连接池
        conn.close();     
    }
}
try{
conn=DBUtils.getConnection();
String sql="select * from k";
Statement st=conn.createStatement();
ResultSet rs=st.executeQuery(sql);
while(rs.next()){
String str=rs.getString("name");
System.out.println(str);
}
rs.close();
st.close();
}


连接并发测试

public class day05 {
    public static void main(String[] args){
        Thread t1=new TestTherad(4000);
        Thread t2=new TestTherad(5000);
        Thread t3=new TestTherad(1000);
        t1.start();
        t2.start();
        t3.start();
    }
}
class TestTherad extends Thread{
    int wait;
    public TestTherad(int wait){
        this.wait=wait;
    }
    public void run(){
        Connection conn=null;
        try {
            conn=DBUtils.getConnection();
            System.out.println("获取连接:"+conn);
            Thread.sleep(wait);
            String sql="select * from k";
            Statement st=conn.createStatement();
            ResultSet rs=st.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("name"));
            }
            System.out.println(wait+"结束");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(conn);
        }
    }
}

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
10月前
|
Java 关系型数据库 MySQL
JDBC知识【JDBC概述】第一章
JDBC知识【JDBC概述】第一章
|
10月前
|
SQL Java 关系型数据库
JDBC知识【JDBC API详解】第三章下篇
JDBC知识【JDBC API详解】第三章下篇
|
10月前
|
druid Java 数据库连接
JDBC知识【数据库连接池】第四章
JDBC知识【数据库连接池】第四章
|
10月前
|
Java 数据库连接 数据库
JDBC知识【JDBC练习】第五章
JDBC知识【JDBC练习】第五章
|
SQL Oracle Java
JDBC 的原理 | 学习笔记
快速学习 JDBC 的原理
142 0
JDBC 的原理 | 学习笔记
|
10月前
|
SQL Java 关系型数据库
JDBC知识【JDBC快速入门】第二章
JDBC知识【JDBC快速入门】第二章
|
SQL Java 关系型数据库
JDBC快速入门(二) JDBC各个类详解以及代码规范
JDBC快速入门(二) JDBC各个类详解以及代码规范
101 0
|
存储 SQL Java
JDBC知识详解(一)
JDBC(java database connectivity)驱动程序是对JDBC规范完整的实现,它的存在在JAVA程序与数据库系统之间建立了一条通信的渠道。
111 1
|
SQL Oracle Java
JDBC知识详解(三)
JDBC(java database connectivity)驱动程序是对JDBC规范完整的实现,它的存在在JAVA程序与数据库系统之间建立了一条通信的渠道。
77 1
|
SQL Java 关系型数据库
JDBC 编程
JDBC 编程
93 0
JDBC 编程