第1章--JDBC

JDBC基础

通过Java Database Connectivity可以实现Java程序对后端数据库的访问

一个完整的数据库部署架构,通常是由客户端和服务器端两部分组成

客户端封装数据库请求,并发送给服务器端,服务器端执行完毕后将结果返回给客户端

常见客户端工具:

MySQL Workbench(图形化工具操作简单,但只能实现简单的查询)

JDBC(驱动程序jar包)

命令行工具(直接敲入SQL语句进行查询)

市面上多种数据库不尽相同,学习成本高,数据库迁移的移植性

-- JDBC(普通的Java类库): 应用程序通过统一的接口,即可实现对任意数据库的访问。

对于数据库厂商来说,JDBC就是一套接口规范,每一个数据库都需要实现JDBC定义的接口,用户通过接口访问数据库即可。

JDBC优势:对数据库的访问简单,开发快捷、省时间,面向不同数据库时的移植性强,在JDBC上可以定制功能强大的框架(如MyBatis)

体系架构:

上层API层负责与Java Web程序之间的通信

JDBC API:

Driver & DriverManager:

Driver是驱动程序的抽象,通过操作Driver接口,可以实现对各个驱动程序的操作

DriverManager是驱动程序的管理类,用户通过Class.forname(DriverName)向DriverManager注册一个驱动程序,

之后通过DriverManager的getConnection方法调用该驱动程序建立到后端数据库的物理连接。

DriverManager.getConnection(DB_URL, USER, PASS));

// USER和PASS在部署数据库时获得

// DB_URL是后端数据库实例的唯一标识符

i.e. jdbc : mysql : //10.164.172.20:3306/cloud_study

协议  子协议      子名称(主机  端口    数据库)

(子协议不同,子名称的格式也略有不同。)

Connection:通过DriverManager的getConnection方法获得的到后端数据库的物理连接

Java应用程序对后端数据库的一条物理连接

通过这些连接,可以执行一些SQL语句:

Statement stmt = conn.createStatement(); // sql statement

Statement:sql语句的容器,用于承载sql语句,在该容器中,可以进行增删改查等操作

通过executeQuery方法,执行数据库查询并得到返回结果的集合,以ResultSet类的对象来表示:

ResultSet rs = stmt.executeQuery("select userName from user");

通过executeUpdate方法,执行数据库更新、删除语句,返回的是int值的对象,代表被影响的数据库记录数量

ResultSet对象表示一个sql语句查询的结果。

关系型数据库:二元表 -- ResultSet对象也是由行和列组成的

ResultSet对象内部有一个指针,指向当前对应的行记录(默认指向第一行记录)

.next():将指针移动到下一行

.previous():将指针移动到下一行

.absolute():将指针定位在某一行

.beforeFirst():将指针移到第一行的之前(通过.next()才能到第一行)

.afterLast():将指针移到最后一行之后

.getString(ColumnName/Index):(index从0开始)获取对应列的值

.getInt(ColumnName/Index):

.getObject(ColumnName/Index):

SQLException:在执行过程中MySQL可能会抛出一些异常

通过捕获SQLException对象来进行异常的处理

下层Driver API层负责与具体的数据库建立连接,一般而言下层的driver都是由数据库厂商提供的。

安装JDBC:

JDBC已经集成在JDK中,可以直接引用,无需安装。

需要安装的是数据库的驱动程序

对于MySQL数据库:

登陆Oracle账号,下载jar包,添加到java web project中。

https://dev.mysql.com/downloads/connector/ 下载Connector/J 5.1.43

构建完整的Java Web程序:

NB. 使用JDBC之前,需要准备一个数据库的后端实例,创建一个user表

构建步骤:

装载驱动程序:向DriverManager注册一个驱动程序Driver

建立数据库连接:DriverManager.getConnection()

执行SQL语句:Statement.execute...()

获取执行结果:ResultSet对象

清理环境:关闭Connection, Statement, ResultSet对象

public class HelloJDBC {

    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/helloworld";
static final String USER = "matt";
static final String PASSWORD = "matt"; public static void helloworld() throws ClassNotFoundException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
// 1. 装载驱动程序
Class.forName(JDBC_DRIVER);
// 2. 建立数据库连接
try {
conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
// 3. 执行SQL语句
       stmt = conn.createStatement();
rs = stmt.executeQuery("select userName from user");
// 4. 获取执行结果
while(rs.next()) {
System.out.println("Hello" + rs.getString("userName"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// 5. 清理环境
try {
if (conn != null) {
// 若conn创建成功
conn.close();// conn.close()也可能有异常
}
if (stmt != null) {
stmt.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// ignore
e.printStackTrace();
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
helloworld();
}
}

代码找茬:

public static void test() throws ClassNotFoundException, SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
ResultSet rs1 = null;
// 1. 装载驱动程序
Class.forName(JDBC_DRIVER);
// 2. 建立数据库连接
conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
// 3. 创建statement
stmt = conn.createStatement();
// 4. 执行SQL语句
rs = stmt.executeQuery("select userName from user");
// 5. 获取执行结果
while(rs.next()) {
System.out.println("Hello" + rs.getString("userName"));
}
}

1. try-catch block

2. close the connection to the database in finally block with another try-catch block

MySQL安装与初始化:

MySQL安装:http://www.jianshu.com/p/fd3aae701db9

MySQL之终端(Terminal)管理MySQL: http://www.cnblogs.com/GarveyCalvin/p/4297221.html

MySQL之终端(Terminal)管理数据库、数据表、数据的基本操作: http://www.cnblogs.com/GarveyCalvin/p/4297282.html

Maven+MySQL入门: https://www.kancloud.cn/digest/javaframe/125574

JDBC MYSQL CONNECTION TUTORIAL:http://theopentutorials.com/tutorials/java/jdbc/jdbc-mysql-connection-tutorial/#Java_MySQL_Connector

安装与初始化:

在https://dev.mysql.com/downloads/mysql/下载安装

(安装过程中会告诉一个临时密码)安装路径为/usr/local/mysql/bin/mysql

配置环境变量PATH="$PATH":/usr/local/mysql/bin

启动MySQL server的服务,需要到system preference中查看MySQL选项,点击start MySQL server

之后在终端输入 mysql -uroot -p和密码a(UhqWd;t4rU即可

试着输入命令,返回:

ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

solution:https://dev.mysql.com/doc/refman/5.6/en/alter-user.html

输入SET PASSWORD = PASSWORD('root'); // 设密码为root

返回Query OK, 0 rows affected, 1 warning (0.00 sec)

创建用户:

insert into mysql.user(Host,User,Password) values ("localhost","matthew",password("1234"));

返回错误:ERROR 1054 (42S22): Unknown column 'Password' in 'field list'

原因:https://stackoverflow.com/questions/30692812/mysql-user-db-does-not-have-password-columns-installing-mysql-on-osx

尝试解决:

use mysql;

show tables;  // 返回所有的table名,里面有一个为user table

describe user;  // 查询user table,发现里面有Host,有User,但是没有Password field。

// the password field is named ' authentication_string'.

解决方案:(documentation)https://dev.mysql.com/doc/refman/5.6/en/create-user.html

CREATE USER 'matt'@'localhost' IDENTIFIED BY 'matt';

以root身份登陆后创建数据库:

create database helloworld;

查看所有的数据库:show databases;  // 看是否创建成功

授权matt拥有该数据库的所有权限:GRANT privileges ON dbname.tablename TO 'username'@'host'

grant all privileges on helloworld.* to matt@localhost identified by 'matt';

刷新系统权限表:

flush privileges;

切换用户:

exit;

mysql -u matt -p;

使用helloworld数据库:use helloworld;

查看该数据库中的所有表:show tables;

给数据库helloworld初始化数据:

CREATE TABLE User ( ID int AUTO_INCREMENT PRIMARY KEY, userName varchar(20) not null);

INSERT INTO User VALUES (null, 'ZhangSi');

INSERT INTO User VALUES (null, 'LiSan');

INSERT INTO User VALUES (null, 'GuoYi');

在Eclipse中创建Java project:

HelloJDBC, 新建类文件。

Then, you need to add the downloaded Java MySQL Connector JAR in client project’s classpath . To do this, right click on your Java Project (JDBCMySQLSample) -> Properties -> Buildpath -> Libraries -> Add External JAR and select “mysql-connector-java-5.1.14-bin.jar” JAR file.

Run the project:

JDBC进阶

业务场景一:过滤条件弱,一次可能读出较多记录

业务场景二:需要海量数据读取

产生结果:Java内存的溢出异常

原因分析:Java程序是运行在JVM中的,而JVM有内存大小限制,当我们把数据库中的记录一次性全部读入到内存中,必须考虑内存中是否放得下这些数据。

解决方法:将数据分批读入内存并处理。

游标:提供一种客户端读取部分服务器端结果集的机制

游标的使用:

1. 开启游标:在DB_URL中加入参数 useCursorFetch=true

DB_URL: jdbc:mysql://<IP>:<Port>/<database> ?useCursorFetch=true

2. PreparedStatement接口(继承自Statement)

使用PreparedStatement对象替换原来的Statement对象

需要在创建时传入sql语句(sql语句是参数格式化的,即用?表示参数)

后续通过setString()等来设置这些参数

SetFetchSize()接口可以帮助实现游标的功能:设置客户端每次从服务器端取回的记录数量

PreparedStatement ptmt = null;
String sql = "select * from user where sex = ?";
ptmt = conn.preparedStatement(sql);
ptmt.setFetchSize(10);
ptmt.setString(1, "男");
rs = ptmt.executeQuery();

业务场景三:某一条记录的数据为大字段

产生结果:和多条记录读取相同,出现Java内存你的溢出异常

解决方法:

流方式(与游标相似):将大字段的数据按照二进制流的方式进行划分

流方式的使用:ResultSet.getBinaryStream();

while (rs.next()){
// 获取对象流
InputStream in = rs.getBinaryStream("blog");
File f = new File(FILE_URL);
OutputStream out = null;
out = new FileOutputStream(f);
int temp = 0;
while((temp = in.read()) != -1) {
// 边读边写
out.write(temp);
}

in.close();
out.close();
}

业务场景四:数据录入:大量数据的插入操作

产生结果:插入数据的速度太慢

原因分析:每次进行executeUpdate()操作时,都是一次客户端到服务端发送sql的过程,发送和接受sql浪费了很多时间,降低了效率

解决方法:一次发送多条sql语句

批处理:一次提交多条SQL语句,节省网络开销

批处理的使用:

Statement:.addBatch();  .executeBatch();  .clearBatch();

addBatch():batch指执行sql的一个单元,addBatch即将一条条sql加入到batch这个单元中

executeBatch():执行一批sql语句

clearBatch():执行完毕,即可清空batch中的sql语句

private static void insertUsers(Set<String> users) throws ClassNotFoundException {
Connection conn = null;
Statement stmt = null; // 1. 装载驱动程序
Class.forName(JDBC_DRIVER);
// 2. 建立数据库连接
try {
conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
// 3. 执行SQL语句
stmt = conn.createStatement(); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// add Batch
for (String user: users) {
stmt.addBatch("insert into User values (null,'" + user +"');");
}
// execute Batch
stmt.executeBatch();
// clear Batch
stmt.clearBatch();
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } catch (SQLException e) {
e.printStackTrace();
} finally {
// 4. 清理环境
try {
if (conn != null) {
// 若conn创建成功
conn.close();// conn.close()也可能有异常
}
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
// ignore
e.printStackTrace();
} } } public static void main(String[] args) throws ClassNotFoundException {
Set<String> users = new HashSet<String>();
users.add("GuoYi");
users.add("ZhangSi");
users.add("LiSan");
insertUsers(users);
}

业务场景五:中文字符集

JDBC字符集需要和数据库的字符集相同

数据库的内部编码:

mysql> show variables like '%character%';

+--------------------------+-----------------------------------------------------------+

| Variable_name            | Value                                                     |

+--------------------------+-----------------------------------------------------------+

| character_set_client     | utf8                                                      |

| character_set_connection | utf8                                                      |

| character_set_database   | latin1                                                    |

| character_set_filesystem | binary                                                    |

| character_set_results    | utf8                                                      |

| character_set_server     | latin1                                                    |

| character_set_system     | utf8                                                      |

| character_sets_dir       | /usr/local/mysql-5.7.19-macos10.12-x86_64/share/charsets/ |

+--------------------------+-----------------------------------------------------------+

8 rows in set (0.02 sec)

里面有个character_set_server:server级别的编码

character_set_database:database级别的编码

(还可以设置表级别Table、字段级别column等级别的编码)

优先级:Server -> Database -> Table -> column

设置JDBC的编码:

DB_URL = DB_URL + "characterEncoding=urt8";

JDBC 单元测试

本次得分为:70.00/70.00, 本次测试的提交时间为:2017-08-21, 如果你认为本次测试成绩不理想,你可以选择再做一次。
1单选(5分)

以下哪项不是使用JDBC为我们编写应用程序访问数据库带来的好处?

  • A.JDBC提供的仅仅是基础接口,基于这些接口,我们可以定制更加灵活方便的数据库框架。
  • B.JDBC屏蔽了数据库客户端与服务器端繁琐的交互协议,降低了开发者使用数据库的门槛。
  • C.JDBC 限定了数据库的使用方式,对于一些特殊的数据库协议,JDBC无法支持。5.00/5.00
  • D.应用程序使用标准的JDBC API接口,可以访问多个数据库,使得应用程序访问数据库的接口具有跨数据库特性。
2单选(5分)

有关JDBC的描述,哪项是不正确的?

  • A.JDBC只是一个抽象的调用规范,底层程序实际上依赖各个数据库的驱动程序。
  • B.JDBC的体系架构分为API层和Driver层:API层负责提供统一的接口给应用程序调用;Driver层负责实现到具体数据库的通信协议。
  • C.JDBC提供了图形化的操作页面,程序员通过页面操作即可完成对数据库的管理。5.00/5.00
  • D.JDBC仅能在Java程序中使用,不能在C++程序中使用。
3单选(5分)

一个正确的JDBC操作步骤包括:

  1. 释放资源

  2. 获得数据库物理连接

  3. 执行SQL命令、

  4. 注册JDBC Driver

  5. 创建Statement

  6. 如果有结果集,处理结果集

  • A.423561
  • B.42356
  • C.243561
  • D.4253615.00/5.00
4单选(5分)

下列哪个方法用于获取关系数据库二元表中某一列的值?

  • A..getString()5.00/5.00
  • B..absolute()
  • C..previous()
  • D..next()
5单选(5分)

当一次向数据库插入多条数据时,应该使用JDBC的哪项高级功能?

  • A.游标
  • B.设置字符集
  • C.流方式
  • D.批处理5.00/5.00
6单选(5分)

当需要读取某个大字段内容时,应该采用哪种JDBC的高级功能实现?

  • A.游标
  • B.批处理
  • C.设置字符集
  • D.流方式5.00/5.00
7多选(40分)

使用游标时,需要设置?

  • A.在JDBC的URL参数中,增加useCursorFetch=true13.33/40.00
  • B.使用PreparedStatment接口的ExecuteQuery方法13.33/40.00
  • C.使用Statement接口的ExecuteQuery方法
  • D.需要设置setFetchSize接口13.33/40.00

JDBC 单元作业

1(100分)现有一个在线交易系统,有一张Product商品表,表中共有3个字段:
    • Id: auto_increment,自增主键,商品唯一标识;

    • ProductName:varchar(100),商品名称;

    • Inventory: int 商品库存;

表中已经插入了一些商品,请编写一段Java程序,尝试读取商品ID为1的商品记录,输出商品名称和库存数量。

注意:程序中已经定义了一些常量,DRIVER_NAME、DB_URL、DB_USER_NAME、DB_PASSWORD,大家在编写程序时,可以直接引用。

已有商品
Id ProductName Inventory
1 bread 11
2 milk 8
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException; public class ProductProcessing { // static final String DRIVER_NAME = "";
// static final String DB_URL = "";
// static final String DB_USER_NAME = "";
// static final String DB_PASSWORD = ""; public static void processing() throws ClassNotFoundException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName(DRIVER_NAME);
conn = DriverManager.getConnection(DB_URL, DB_USER_NAME, DB_PASSWORD);
stmt = conn.createStatement();
rs = stmt.executeQuery("select ProductName, Inventory from Product where Id=1"); while (rs.next()) {
System.out.println(rs.getString("ProductName") + ": " + rs.getString("Inventory"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(conn != null) {
conn.close();
}
if(stmt != null) {
stmt.close();
}
if(rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
} }
public static void main(String[] args) throws ClassNotFoundException {
processing();
}
}


Java开发工程师(Web方向) - 03.数据库开发 - 第1章.JDBC的更多相关文章

  1. Java开发工程师(Web方向) - 03.数据库开发 - 第2章.数据库连接池

    第2章--数据库连接池 数据库连接池 一般而言,在实际开发中,往往不是直接使用JDBC访问后端数据库,而是使用数据库连接池的机制去管理数据库连接,来实现对后端数据库的访问. 建立Java应用程序到后端 ...

  2. Java开发工程师(Web方向) - 03.数据库开发 - 第3章.SQL注入与防范

    第3章--SQL注入与防范 SQL注入与防范 经常遇到的问题:数据安全问题,尤其是sql注入导致的数据库的安全漏洞 国内著名漏洞曝光平台:WooYun.org 数据库泄露的风险:用户信息.交易信息的泄 ...

  3. Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

    第5章--MyBatis MyBatis入门 Abstract: 数据库框架的工作原理和使用方法(以MyBatis为例) 面向对象的世界与关系型数据库的鸿沟: 面向对象世界中的数据是对象: 关系型数据 ...

  4. Java开发工程师(Web方向) - 03.数据库开发 - 第4章.事务

    第4章--事务 事务原理与开发 事务Transaction: 什么是事务? 事务是并发控制的基本单位,指作为单个逻辑工作单元执行的一系列操作,且逻辑工作单元需满足ACID特性. i.e. 银行转账:开 ...

  5. Java开发工程师(Web方向) - 03.数据库开发 - 期末考试

    期末考试 编程题 本编程题包含4个小题,覆盖知识点从基础的JDBC.连接池到MyBatis. 1(10分) 有一款在线教育产品“天天向上”主要实现了在手机上查看课程表的功能.该产品的后端系统有一张保存 ...

  6. Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器

    第2章.IoC容器 IoC容器概述 abstract: 介绍IoC和bean的用处和使用 IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, ...

  7. Java开发工程师(Web方向) - 04.Spring框架 - 第1章.Spring概述

    第1章.Spring概述 Spring概述 The Spring Framework is a lightweight solution and a potential one-stop-shop f ...

  8. Java开发工程师(Web方向) - 02.Servlet技术 - 第3章.Servlet应用

    第3章.Servlet应用 转发与重定向 转发:浏览器发送资源请求到ServletA后,ServletA传递请求给ServletB,ServletB生成响应后返回给浏览器. 请求转发:forward: ...

  9. Java开发工程师(Web方向) - 02.Servlet技术 - 第2章.Cookie与Session

    第2章--Cookie与Session Cookie与Session 浏览器输入地址--HTTP请求--Servlet--HTTP响应--浏览器接收 会话(session):打开浏览器,打开一系列页面 ...

随机推荐

  1. Lambda收集器示例

    Collectors常用方法 工厂方法 返回类型 作用 toSet Set 把流中所有项目收集到一个 Set,删除重复项 toList List 收集到一个 List 集合中 toCollection ...

  2. $Self~Problem~C~:~Samsara$

    题目背景: 在这个\(Canman\)界的人都知道,世界上最伟大的修道者 -- \(Felling\),曾经结束了\(Canman\)的无垠盏之灾,守护了\(Canman\)的和平.在无垠盏之灾的最后 ...

  3. TCP Three-way Handshake

    TCP Connection Management Out-of-Order and Duplication(复制) Problem Old segment from previous connect ...

  4. GIT 报错:Result too large 解决办法

    在使用bower install命令下载前端依赖的js插件时,git出错了,报错信息如下: bower ECMDERR Failed to execute "git ls-remote -- ...

  5. SimpleProfile_GetParameter && SimpleProfile_SetParameter

    /********************************************************************* * @fn SimpleProfile_GetParame ...

  6. write函数过程解析

    write函数作为用户向终端或者文件进行写数据的重要函数,有着重要的作用. |------|          |---------|   |---------| |----------| | wri ...

  7. redis存储数据的时候

    使用redis存储数据的时候,有时候为了查看的方便,通常会有层级或者说是目录, 这时候我们在set的时候,需要将key值使用“:”的符号来区分层级关系,比如:set(“a:b”, “123”),那么在 ...

  8. Cornerstone|SVN

    SQLite-database disk image is malformed missing from working copy mac下CornerstoneSVN出错 Description _ ...

  9. awk命令用法

    awk:把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理,是一个强大的文本分析工具,在对数据分析并生成报告时很有优势. awk有3个不同版本: awk.nawk和gawk, ...

  10. PHP Fatal error: Call to undefined function think\finfo_open()

    PHP Fatal error:  Call to undefined function think\finfo_open() php.ini      extension=php_fileinfo. ...