03.数据库开发 - 第1章.JDBC

JDBC基础

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

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

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

           常见客户端工具:

                     Mysql Workbench(图形化工具操作简单,只能进行简单查询)

                     JDBC (驱动程序jar包)

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

市面上多种数据库不尽相同,学习成本高,数据库迁移的移植性好的工具非常有必要

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

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

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

扫描二维码关注公众号,回复: 3503925 查看本文章

 

体系架构:

上层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.163.173.30:3306/cloud_study

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

(NB:子协议不同,自协议的格式也有不同)

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

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

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

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

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

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

                     ResultSet rs = stms.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):

                     3个get最好使用ColumnName,因为列可能会变化,而且使用columnName会更直观。

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

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

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

 

安装JDBC:

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

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

           对于MySQL数据库:

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

                     http://dev.mysql.com/downloads/connector/j

构建完整的Java web程序:

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

           构建步骤:

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

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

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

                     获取执行结果:ResultSet对象

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

 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class HelloJDBC {

	static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	static final String DB_URL = "jdbc:mysql://localhost:3306/cloud_study";
	static final String USER = "root";
	static final String PASSWORD = "nidongde";

	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、创建Statement
			stmt = conn.createStatement();
			// 4、执行SQL语句
			rs = stmt.executeQuery("select userName from user");
			// 5、获取执行结果
			while (rs.next()) {
				System.out.println("Hello" + rs.getString("userName"));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (rs != null) {
					rs.close();
				}
				if (stmt != null) {
					stmt.close();
				}
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				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

 

JDBC进阶

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

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

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

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

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

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

                     游标的使用:

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

                                          DB_URL:jbdc:mysql://<IP>:<PORT>/<database>?useCursorFetch=true

                                2. PreparedStatement接口(继承自Statement)

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

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

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

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

public static void helloworld() throws ClassNotFoundException {
		Connection conn = null;
		PreparedStatement pmpt = null;
		ResultSet rs = null;
		
		// 1、装载驱动程序
		Class.forName(JDBC_DRIVER);
		// 2、建立数据库连接
		try {
			DB_URL = DB_URL + "?useCursorFetch=true";
			conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
			// 3、创建Statement
			String sql = "select userName from user where sex = ?";
			pmpt = conn.prepareStatement(sql);
			pmpt.setFetchSize(2);
			pmpt.setString(1, "男");
			// 4、执行SQL语句
			rs = pmpt.executeQuery();
			// 5、获取执行结果
			while (rs.next()) {
				System.out.println("Hello" + rs.getString("userName"));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			
			try {
				if (rs != null) {
					rs.close();
				}
				if (pmpt != null) {
					pmpt.close();
				}
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

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

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

           解决方法:

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

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

// 1、装载驱动程序
		Class.forName(JDBC_DRIVER);
		// 2、建立数据库连接
		try {
			conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
			// 3、创建Statement
			String sql = "select * from user_note";
			pmpt = conn.prepareStatement(sql);
			// 4、执行SQL语句
			rs = pmpt.executeQuery();
			while (rs.next()) {
				// 5、获取对象流
				InputStream in = rs.getBinaryStream("blog");
				// 6、将对象流写入文件
				File f = new File("/Users/liujingjie/test.txt");
				OutputStream out = null;
				try {
					out = new FileOutputStream(f);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
				int temp = 0;
				try {
					while ((temp = in.read()) != -1) { // 边读边写
						out.write(temp);
					}
					in.close();
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				System.out.println("ok"); // 假如没异常,这个ok会打印出来,没其他特殊含义。
			}

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

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

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

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

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

                     批处理的使用:

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

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

                                executeBatch(): 执行这个单元的一批SQL语句

                                clearBatch(): 执行完毕,即可清空Batch中的SQL语句。


public class HelloJDBC4 {
	static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
	static final String DB_URL = "jdbc:mysql://localhost:3306/cloud_study";
	static final String USER = "root";
	static final String PASSWORD = "xjj5211314";

	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 + "',null);");
			}
			// execute Batch
			stmt.executeBatch();
			// clear Batch
			stmt.clearBatch();

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 4. 清理环境
			try {
				if (conn != null) {
					conn.close();
				}
				if (stmt != null) {
					stmt.close();
				}
			} catch (SQLException e) {
				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%’;

                  

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

                                          character_set_database: database级别的编码

                     另外在创建Table以及Column的时候这2个级别的编码

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

                     意思就是加入table与Database编码不同,以table为准。

设置JDBC的编码:

           DB_URL = DB_URL + “characterEncoding=utf-8”;

作业:

现有一个在线交易系统,有一张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

// 1. 装载驱动程序
		Class.forName(JDBC_DRIVER);
		// 2. 建立数据库连接
		try {
			conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
			// 3. 执行SQL语句
			String sql = "select ProductName,Inventory from Product where id = ?";
			pmpt = conn.prepareStatement(sql);
			pmpt.setInt(1, i);
			rs = pmpt.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getString("ProductName")+":"+rs.getString("Inventory"));
			}

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 4. 清理环境
			try {

 

猜你喜欢

转载自blog.csdn.net/liujingjie2010/article/details/82935346