Java JDBC实践

任何技术都是为了解决实际问题而存在的

目标

  1. 数据库连接,简单查询
  2. 批量插入问题
  3. 事务问题,开始事务,提交事务,回滚,事务隔离级别
  4. 高级查询,子查询

技术栈

  • maven 包管理打包工具
  • java. jdbc

1. 数据库连接,简单查询

  • 加载驱动, 不同的数据库都有不同的驱动,但都是事先的统一的接口,java.sql的接口

MySQL : com.mysql.jdbc.Driver // 需要依赖 mysql: mysql-connector-java,本篇样例使用的是MySQL
Oracle: com.oracle.ojdbc14 // 这个需要你去oracle官网下载,根据你的数据库版本,选择相应的驱动,然后集成到项目中,关于依赖本地文件,可以上网查一下

  • 获取连接,输入相应的URL, USERNAME,PASSWORD,创建数据库连接
  • 获取操作Statement,要想执行SQL,必须先获取Statement
  • 执行语句
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcConnect {
    private static final String URL = "jdbc:mysql://localhost:3306/task_enjoy";
    private static final String NAME = "root";
    private static final String PASSWORD = "xxxxxx"; // TODO 自己改密码

    public static void main(String[] args) throws Exception {

        //1.加载驱动程序
        Class.forName("com.mysql.jdbc.Driver");
        //2.获得数据库的连接
        Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD);
        //3. 通过连接,获取数据库操作Statement
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("select name,suffix from app_file");//选择import java.sql.ResultSet;
        while (rs.next()) {//如果对象中有数据,就会循环打印出来
            System.out.println(rs.getString("name") + "," + rs.getString("suffix"));
        }
    }
}

1.1 查询详解

  • 创建数据库操作Statement时,有三种Statement,分别对应下面三种情况
    1. 执行静态语句,可使用Statement实例实现。
    2. 执行动态SQL语句,可使用PreparedStatement实例实现
    3. 执行数据库存储过程,可使用CallableStatement实例实现。

    CallableStatement 继承 PreparedStatement, PreparedStatement 继承 Statement

下面举例说明一下,分为测试类和业务类,
业务类

package com.fjp.base.jdbc;

import java.sql.*;

public class JdbcConnect {
    private static final String URL = "jdbc:mysql://localhost:3306/task_enjoy";
    private static final String NAME = "root";
    private static final String PASSWORD = "xxxx"; // TODO 自己改密码
    private static Connection conn = null;

    static {
        try {
            //1.加载驱动程序
            Class.forName("com.mysql.jdbc.Driver");
            //2.获得数据库的连接
            conn = DriverManager.getConnection(URL, NAME, PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void excuteCommonSql() {
        try {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("select name,suffix from app_file");
            while (rs.next()) {
                System.out.println(rs.getString("name") + "," + rs.getString("suffix"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void excutePreparedCommonSql() {
        try {
            PreparedStatement preparedStatement = conn.prepareStatement("select name,suffix from app_file where `name` = ?");
            preparedStatement.setString(1, "aassasd");
            ResultSet rs = preparedStatement.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("name") + "," + rs.getString("suffix"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void excuteCallbleSql() {
        try {
            CallableStatement preparedStatement = conn.prepareCall("{CALL demoSp(? , ?)}");
            preparedStatement.setString(1, "aassasd");
            preparedStatement.setString(2, "33");
            ResultSet rs = preparedStatement.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("name") + "," + rs.getString("suffix"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

测试类

public class JdbcTest {
    private JdbcConnect jdbcConnect = new JdbcConnect();

    @Test
    public void test() {
        jdbcConnect.excuteCommonSql();
    }

    @Test
    public void test2() {
        jdbcConnect.excutePreparedCommonSql();
    }
    @Test
    public void test3() {
        jdbcConnect.excuteCallbleSql();
    }

}

注:接下来都写伪代码了

  • 执行SQL语句
    Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate 和execute

    1. ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句,返回一个结果集(ResultSet)对象。
    2. int executeUpdate(String sqlString):用于执行INSERT、UPDATE或 DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等
    3. execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的语句。

    具体实现的代码:

ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ;
Integer rows = stmt.executeUpdate("INSERT INTO ...") ;
Boolean flag = stmt.execute(String sql) ;

  • 处理结果集
    两种情况:
    1、执行更新返回的是本次操作影响到的记录数。
    2、执行查询返回的结果是一个ResultSet对象。
    • ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些行中数据的访问。
    • 使用结果集(ResultSet)对象的访问方法获取数据:
while(rs.next()){   
         String name = rs.getString("name") ;   
         String pass = rs.getString(1) ; // 此方法比较高效   (列是从左到右编号的,并且从列1开始)   
}
  • 关闭JDBC对象
    操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声
    明顺序相反:
    1、关闭记录集
    2、关闭声明
    3、关闭连接对象
if(rs != null){   // 关闭记录集   
        try{   
            rs.close() ;   
        }catch(SQLException e){   
            e.printStackTrace() ;   
        }   
}
 if(stmt != null){   // 关闭声明   
        try{   
            stmt.close() ;   
        }catch(SQLException e){   
            e.printStackTrace() ;   
        }   
}   
 if(conn != null){  // 关闭连接对象   
         try{   
            conn.close() ;   
         }catch(SQLException e){   
            e.printStackTrace() ;   
         }   
 }  

2. 批量插入问题

public void excuteBatch() {
        try {
            Statement statement = conn.createStatement();
            for (int i = 0; i < 500; i++) {
                statement.addBatch("insert into app_file(id, `name`, suffix) values ('" + "id02" + i + "', '111', '222')");
            }
            int[] re = statement.executeBatch();
            statement.clearBatch();
            System.out.println(re);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

批量插入,需要考虑事务的问题,和每次提交的数量问题,下面会有说明

3. 事务问题

事务涉及到的知识较多,需要考虑事务隔离级别,和事务的传播问题

3.1 事务的使用

public void transactionMananger() {
        try {
            Statement statement = conn.createStatement();
            conn.setAutoCommit(false);
            String sql = "insert into app_file(id, `name`, suffix) values ('id022222', '111', '222')";
            statement.execute(sql);
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }

3.2 数据库事务正确执行的四个基本要素 ACID

原子性(A, Atomicity):指整个数据库事务是不可分割的工作单位。只有使据库中所有的操作执行成功,才算整个事务成功;事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。

一致性(C, Consistency):指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款总额为2000元。

隔离性(I, Isolation):指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

持久性(D, Durability):指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

事务的(ACID)特性是由关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。

3.3 事务并发处理可能的问题

1、脏读(dirty read):一个事务读取了另一个事务尚未提交的数据
2、不可重复读(non-repeatable read):一个事务的操作导致另一个事务前后两次读到不同的数据
3、幻读(phantom read):一个事务的操作导致另一个事务前后两次查询的结果数据量不同

3.4 事务隔离级别

JDBC定义了五种事务隔离级别:
TRANSACTION_NONE JDBC驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读
TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读

4. 高级查询

其实高级查询与jdbc关系不大,但是这个还是需要注意

4.1 子查询

这里面有几个我遇到的比较麻烦的问题

1. 如何分组查询,根据某个字段查询,去数据最大的一条

EXPLAIN SELECT
    * 
FROM
    record e1 
WHERE
    not EXISTS ( SELECT 1 FROM record e2 WHERE e2.type_id = e1.type_id AND e1.score < e2.score );

上面这个查询语句,分组字段是type_id, 排序字段是 score,取得的是根据 type_id分组,socre 最大的一条,
查询时,发现扫描了这个表两次,不是最优的解决方案,另外如果最大的有多条的话,也会一起查询出来

EXPLAIN SELECT
    * 
FROM
    enjoy e1 
WHERE
    e1.score = ( SELECT max(e2.score) FROM enjoy e2 WHERE e2.tag = e1.tag );

第二个方法与第一个方法没有多大差别,结果也是完全一样的

2. 如何避免查询索引字段时,不是查询的索引而是全表检索

这个就与索引的机制相关了,不做过多扩展

  • 应尽量避免在where 子句中对字段进行null 值判断
  • 尽量避免在where 子句中使用!=或<>操作符
  • 可以在LIKE操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形
  • 尽量避免在where 子句中使用or 来连接条件

参考文档

完整java开发中JDBC连接数据库代码和步骤
数据库 事务的特性ACID

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容

  • JDBC基础知识 一、采用JDBC访问数据库的基本步骤: A.载入JDBC驱动程序 B.定义连接URL ...
    java日记阅读 3,827评论 0 20
  • JDBC简介 SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。JDBC...
    奋斗的老王阅读 1,492评论 0 51
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,400评论 0 4
  • 什么是JDBC API以及何时使用它? Java数据库连接API允许我们使用关系数据库.JDBC API接口和类是...
    淡定_蜗牛阅读 462评论 0 1
  • 本文内容 1.什么是JDBC以及为什么要使用JDBC 2.JDBC核心API的讲解 3.使用JDBC核心API进行...
    Vincilovfang阅读 1,184评论 0 11