JDBC

目录

​JDBC编程六步

JDBC事务机制

批处理


1、JDBC:Java DataBase Connectivity(Java语言连接数据库)

2、JDBC的本质是什么?JDBC是SUN公司制定的一套接口(interface)
        java.sql.*; (这个软件包下有很多接口。)

    为什么要面向接口编程?
        解耦合:降低程序的耦合度,提高程序的扩展力。
        多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)

3、为什么SUN制定一套JDBC接口呢?
        因为每一个数据库的底层实现原理都不一样。
        Oracle数据库有自己的原理。
        MySQL数据库也有自己的原理。
        MS SqlServer数据库也有自己的原理。
        ....
        每一个数据库产品都有自己独特的实现原理。


JDBC编程六步

下面使用的是mysql8.0数据库,还需要mysql连接器的jar包,打开这个网址mysql连接jar根据本机上数据库版本下载对应连接器版本。

有一个名为ms的数据库,其中有user表


    第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库) 

方式一:

Driver driver = new com.mysql.cj.jdbc.Driver();  // 多态,父类型引用指向子类型对象。
DriverManager.registerDriver(driver);

方式二:(常用)

因为参数是一个字符串,字符串可以写到xxx.properties文件中。以下方法不需要接收返回值,因为我们只想用它的类加载动作。

Class.forName("com.mysql.cj.jdbc.Driver");

可以写一个工具类,写在静态代码块中,在类加载的时候执行,且只执行一次。下面步骤中重复的代码都可以写在工具类。方便编程。

static {
    try {
        Class.forName("com.mysql.cj.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

创建jdbc.properties文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/easyshop?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
user=root
password=root

使用资源绑定器绑定属性配置文件,获取数据库的连接信息也可以写进配置文件中。

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");
Class.forName(driver);

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

        url:统一资源定位符(网络中某个资源的绝对路径),如:http://www.baidu.com/ 这就是url。
           URL包括:协议,IP,PORT,资源名
           http://182.61.200.7:80/index.html
                    http://    通信协议
                    182.61.200.7    服务器IP地址
                    80    服务器上软件的端口
      什么是通信协议,有什么用?
             通信协议是通信之前就提前设定好的数据传送格式。数据包具体是怎么传数据,格式提前定好的。

String url = "jdbc:mysql://localhost:3306/ms?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
String user = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);

其中,url,user是数据库账号,password是数据库密码。

    第三步:获取数据库操作对象(专门执行sql语句的对象)

Statement方式

Statement stmt = conn.createStatement();

【注意】:java.sql.Statement可能会出现SQL注入问题

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

2、解决SQL注入问题:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement。

        PreparedStatement接口继承了java.sql.Statement,PreparedStatement是属于预编译的数据库操作对象。PreparedStatement的原理是:预先对sql语句的框架进行编译,然后再给SQL语句传“值”。

3、解决SQL注入的关键是什么?
        用户提供的信息中即使含有SQL语句的关键字,但是这些关键字并没有参与编译,不起作用。

4、对比以下Statement和PreparedStatement?
        - Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
        - Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高一些。
        - PreparedStatement会在编译阶段做类似的安全检查。
综上所述,PreparedStatement使用较多,只有极少数的情况下需要使用Statement

5、什么情况下必须使用Statement呢?
        业务方面要求必须支持sql注入的时候。Statement支持sql注入,凡是业务方面要求是需要sql语句拼接的,必须使用Statement。

PreparedStatement方式

String sql = "select * from user where username = ? and password = ? "; 
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "asd");
ps.setString(2, "147");

解析:sql字符串是SQL语句的框子。其中一个?,表示一个占位符,一个?来接收一个“值”,【注意】:占位符不能使用单引号括起来。

conn.prepareStatement(sql);程序执行到这里,会发送sql语句给DBMS进行sql语句的预先编译。

setString(1,"asd"); 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有的下标从1开始。)

实现模糊查询

String sql = "select * from user where username = ?"; 
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "%s%");

    第四步:执行SQL语句(DQL DML....)

executeUpdate();专门执行的DML语句的(insert delete update),返回值是“影响数据库中的记录条数”。

Statement方式

String sql = "insert into user(username, password) values('asd', '147')";
int count = stmt.executeUpdate(sql);

PreparedStatement方式

rs = ps.executeUpdate();

executeQuery(); 专门执行select的,返回值是ResultSet结果集合

Statement方式

String sql = "select id, username, password from user";
ResultSet rs = stmt.executeQuery(sql); 

PreparedStatement方式

rs = ps.executeQuery();

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

方式一:(常用)

while(rs.next()){				
    int id = rs.getInt("id");
    String username = rs.getString("username");
    String password = rs.getString("password");
    System.out.println(id + "," + username + "," + password);
}

getString(),getInt()....其中参数是不是表中的列名称,是查询结果集的名称。(使用别名,就是别名)。

方式二:可以以特定的类型取出

while(rs.next()){				
    int id = rs.getInt(1);
    String username = rs.getString(2);
    String password = rs.getString(3);
    System.out.println(id + "," + username + "," + password);
}

    第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)

在finally语句块中关闭资源,并且要遵循从小到大依次关闭,分别对其try..catch

Statement方式

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();
    }
}

PreparedStatement方式

if(rs != null){
    try{
        rs.close();	
    }catch(Exception e){
        e.printStackTrace();
    }
}		
if(ps != null){
    try{
        ps.close();	
    }catch(Exception e){
        e.printStackTrace();
    }
}			
if(conn != null){
    try{
        conn.close();	
    }catch(Exception e){
        e.printStackTrace();
    }
}

JDBC事务机制

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

在获取数据连接对象时调用函数,开始事务

conn.setAutoCommit(false);

在执行SQL语句之后,提交事务

conn.commit();

程序能够走到上面代码说明程序没有异常,事务结束,手动提交数据

如果程序中途发生错误在catch语句中,会执行回滚事务

if (conn != null) {
    try {
        conn.rollback();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}

批处理

当插入数据的数据量比较多的时候,可以使用批处理,增加效率。

String  sql = "insert into user(id, username) value(?,?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 1000; i++){
    ps.setInt(1, i);
    ps.setString(2, "username"+i);
    ps.addBatch(); // 打包
    if (i % 300 == 0){                      
        ps.executeBatch(); // 每300个发送一次给数据库
        ps.clearBatch(); // 清空包
    }
}
ps.executeBatch();// 为了保证所有数据插入,所以这里再执行一次

悲观锁(行级锁)

语法:select后面添加for update

开启一个事务1进行查询,并且使用悲观锁,锁住相关记录。

String sql = "select id,name,age from user where id=? for update";
ps = conn.prepareStatement(sql);
ps.setString(1, "1001");
rs = ps.executeQuery();

再开启另一个事务2修改这条数据,无法进行,因为其中事务1先执行,执行之后看到版本号是1.1,于是提交事务,将版本号修改成1.2,事务2后执行,执行之后准备提交事务的时候,发现版本号是1.2,和它最初读的版本号不一致。回滚数据。

悲观锁:事务必须排队执行,数据锁住了,不允许并发。

乐观锁:支持并发,事务不需要排队,只不过需要一个版本号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值