JDBC 笔记
个人微信公众号: 程序猿的月光宝盒
对应pdf版:https://download.csdn.net/download/qq_22430159/10754554
没有积分的可关注公众号后台回复[JDBC]
Day1--JDBC概述+JDBC完成CRUD+DAO设计
1.JDBC概述
1.1 什么是持久化(persistence):
持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。
保存数据:
内存中: 掉电之后,数据就没了.
磁盘中: 掉电之后,数据依然存在.
大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。
持久化的主要应用是将内存中的数据存储在 关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。
JPA:JavaEE的规范,Java persistence api: Java的持久化API. Hibernate实现了该规范.(xml/注解)
--------------------------------------------------------------------------------------------------------------------
在Java中,数据库存取技术 只能通过JDBC 访问数据库:
JDBC访问数据库的形式主要有两种:
1).直接使用JDBC的API去访问数据库服务器(MySQL/Oracle).
2).间接地使用JDBC的API去访问数据库服务器.
第三方O/R Mapping工具,如Hibernate, MyBatis等.(底层依然是JDBC)
JDBC是java访问数据库的基石,其他技术都是对jdbc的封装.
1.2 JDBC(Java DataBase Connectivity):
是一种用于执行SQL语句的Java API(接口,类,方法),可以为多种关系数据库提供统一访问, 它由一组用Java语言编写的类和接口组成。 JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序.
JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统, 这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
总结: JDBC本身是java连接数据库的一个标准,是进行数据库连接的抽象层,由java编写的一组类和接口组成,接口的实现由各个数据库厂商来完成.
--------------------------------------------------------------------------------
JDBC的版本:
JDBC隶属于JavaSE的范畴,伴随着JavaSE的版本升级.
Java6开始:JDBC4.0: (了解),JDBC4.0有一个新特性-无需加载注册驱动.
Java7开始:JDBC4.1:
---------------------------------------------------------------
JDBC的API在哪里?
----->JDK的API中.
java.sql包装的就是JDBC的API.
各大数据库厂商就会对JDBC的API提供实现类.--->驱动包
注意: 在我们使用Java代码来操作JDBC的时候.
1:我们运用到的API(接口)全部来自于java.sql包,绝对不能出现来自于驱动包中.
2):使用到的全部是接口.
千万不要引入com.mysql..Xxx类.
2. JDBC完成CRUD(增:Create 删:Delete 改:Update 查:Retrieve)
2.1 获取数据库连接
操作JDBC的准备:
1.拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar
2.build path,告诉项目去哪里去找字节码文件.
--------------------------------------------------------------------------------贾琏
操作JDBC的第一步,获取JDBC的连接对象:Connection.
步骤1: 加 载注册驱动.
Class.forName(“com.mysql.jdbc.Driver ”);
为什么说这行代码就在完成加载注册驱动的操作.
1):把com.mysql.jdbc.Driver这份字节码文件加载进JVM.
2):把字节码加载进JVM之后,就会立刻执行该类的静态代码块.
步骤2: 获取连 接对象,通过DriverManager的静态方法(getConnection).
Connection conn = DriverManager.getConnection (String url,String username,String password);
参数:
url : jdbc:mysql://数据库服务器安装电脑的主机IP:端口/哪一个数据库名称
连接本机: jdbc:mysql://localhost:3306/jdbcdemo
如果连接的数据库服务器在本机,并且端口是3306,则可以简写:
jdbc:mysql:///jdbcdemo
username: 所连接数据库服务器的用户账号(root)
password: 所连接数据库服务器的用户密码(admin)
验证已经获取连接:可以在MySQL控制台,使用命令:show processlist; 查看MySQL运行进程.
-----------------------------------------------------------------------------------------------------
从Java6(JDBC4.0)开始,可以不再加载注册驱动,直接通过DriverManager获取连接对象.
为啥不再需要加载注册驱动了?
从Java6开始,规范要求每一个JDBC驱动的包,都必须带有META-INF/services/java.sql.Driver文件.
开发建议:依然还是建议手动的加载注册驱动. 如此,可以兼容之前的JDK版本,在JavaWeb中必须手动加载.
2.2 JDBC相关的API
操作JDBC的步骤:
贾琏欲执事:
---------------------------------------------------
1):加载注册驱动. 贾
2):获取连接对象. 琏
3):创建/获取语句对象 欲
4):执行SQL语句 执
5):释放资源 事
---------------------------------------------------
1. Connection接口的常用方法:
Statement createStatement() 创建一个 Statement 对象来将 SQL 语句发送到数据库。
PreparedStatement prepareStatement(String sql) :获取预编译语句对象.
参数:sql,并不是一个静态SQL,而是带有占位符的SQL(?).
void close():关闭连接对象
2. Statement接口的常用方法:
用于执行静态 SQL (写死的SQL,可以执行运行的SQL )语句并返回它所生成结果的对象。
int executeUpdate (String sql):可以执行DML(增删改)和DDL语句,如果是执行DDL什么都不返回,执行DML返回受影响的行数.
ResultSet executeQuery (String sql) :执行给定的 DQL(查询)语句,该语句执行之后返回一个 ResultSet 对象。
void close():关闭语句对象
3.PreparedStatement接口的常用方法:
是Statement的子接口,表示预编译 的 SQL 语句的对象.
设置占位符参数(告诉SQL中的?到底表示哪一个值) :
void setXxx(int parameterIndex, Xxx value): xxx表示数据类型,比如:String,int,Long等.
parameterIndex:设置第几个占位符?(从1开始 ).
value:需要设置的参数值.
int executeUpdate ():可以执行DML(增删改)和DDL语句,如果是执行DDL什么都不返回,执行DML返回受影响的行数.
ResultSet executeQuery () :执行给定的 DQL语句,该语句执行之后返回一个 ResultSet 对象。
注意:此时不需要传递SQL参数.
void close():关闭语句对象
4. ResultSet接口的常用方法:
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成.
ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;因为该方法在 ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。(类似迭代器操作)
-------------------------------------------------------------------------------------
boolean next(): 先判断光标是否能向下移动,如果可以,则往下移动.
xxx:表示数据类型,比如:int,long,String等,根据当前列的数据类型来选择.
xxx getXxx(int columnIndex) : 获取当前光标行的第N列的数据,从1开始计算.
xxx getXxx(String columnName) :获取当前光标行的指定列名的列的数据,推荐.
void close():关闭结果集对象
2.3 创建表和异常处理
2.3.1 创建一张t_student表:id/name/age:
SQL:
CREATE TABLE t_student(id BIGINT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(20),age INT);
2.3.2 异常处理和关闭资源:
3. DML操作
在t_student表中,插入,修改和删除学生信息.
此时的操作模板和上述DDL一模一样,仅仅只是SQL语句不一样.
4. DQL操作
5. DAO思想
5.1 为什么得需要DAO?也就是说DAO出现之后需要解决什么问题?
解决数据库操作的代码重复.
5.1.2 代码重复不影响对与错,但是影响维护成本,因为如果需要修改代码就得修改N个地方.
所以在开放中,我们应该要遵循DRY(Don't repeat yourself )(字面意思来看:"不要重复自己")原则.
--------------------------------------------------------------------------------------------------------------------
操作数组 : 是把数据存储在内存中.
操作数据库:是把数据存储在数据库文件中(硬盘).
--------------------------------------------------------------------------------------------------------------------
5.1.3 需求:我现在需要定义一个数据来存储数据.
在封装ArrayList之前,每一个客户端往数组中保存数据,都得去检查容量和扩容操作,如此就重复了.
解决方案:把数组的相关操作封装到ArrayList类中,把容量检查和扩容存储在add方法中,此后,客户端就只管直接调用方法即可,不在关系繁琐的重复操作了.
5.2 什么是DAO:主要就包括CRUD(增删改查操作).
DAO(Data Access Object) 是一个数据访问接口,数据访问:顾名思义就是与数据库打交道。夹在业务逻辑与数据库资源中间。
在核心J2EE模式中是这样介绍DAO模式的:为了建立一个健壮的J2EE应用,应该将所有对数据源的访问操作抽象封装在一个公共API中。
用程序设计的语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。
在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口在逻辑上对应这个特定的数据存储。
上图的DAO设计的示意图中,分析:
首先:以及解决了客户端功能代码重复问题.
-------------------------------------------------------------------
提出新的问题: 设计save和get方法 .
void save(String name,Integer age );
问题:如果需要保存的学生有多个信息,此时参数就有N多.
设计方法时,参数最好不要超过5,6个.
问题如何解决?
String get(Long id):根据学生的主键来查询学生信息
此时因为需要返回学生的多个信息,此时的返回类型设计是不合理的.
问题如何解决?
解决方案: 使用封装思想,把学生的多个信息封装成一个对象.
保存操作:
void save(Student stu );
查询操作:
Student get(Long id);
6.DAO设计
7.小结
Day2--重构设计上+预编译语句对象+事务管理操作+批处理操作+大数据类型操作+获取自动生成的主键
1. JDBC的重构设计上
重构(Refactoring)就是通过调整程序代码,改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
1.1 问题1:每个DAO方法中都会写:驱动名称/url/账号/密码,不利于维护.
如果现在我们从MySQL迁移到Oracle中去,此时就得修改每一个DAO方法的 驱动名称/url/账号/密码.
解决方案:
使用成员变量来表示,成员变量的作用域在整个类中有效.
此时DAO代码:
问题1已经解决(通过在DAO类中定义成员变量).
1.2 问题2:我们在开放中会存在N个DAO实现类,那么此时每一个DAO实现类中都得提供连接数据库的四个基本要素的成员变量.
解决方案:
现在需要完成在多个类中共享驱动名称/url/账号/密码四个信息,我们可以定义一个类(JdbcUtil),把这四个信息存储在该类中,并且使用public static修饰.
此时DAO代码:
1.3 问题3: 问题2是完美解决了,但是存在一些遗憾:
1):JdbcUtil中的四个字段应该私有化起来,体现封装.
2):其实在DAO实现类中,仅仅需要获取一个Connection对象即可 ,至于该对象如何创建,可以不关心.
解决方案:
我们在JdbcUtil类中提供一个静态方法,用于返回Connection对象.
代码如下图.
此时DAO代码:
conn = JdbcUtil.getConn();
1.4 我们分析右图JdbcUtil类中的getConn方法的代码
发现,每次调用getConn方法都需要加载注册驱动,而我们其实就只需要在最初加载一次即可.
解决方案:
在静态代码块中去加载和注册数据库驱动即可.
代码如下图.
1.5 问题5:每一个DAO方法最后,都需要释放资源,该代码没有技术含量,又臭又长.
DML操作: 关闭Connection和Statement对象.
DQL操作: 关闭Connection和Statement以及ResultSet对象.
解决方案:
在JdbcUtil类中提供close方法用于关闭三个资源对象.
此时DAO代码:
关闭DML操作资源:JdbcUtil.close(conn,st,null);
关闭DQL操作资源: JdbcUtil.close(conn,st,rs);
1.6 问题6:在JdbcUtil类中提供了四个字段分别表示连接数据库的四要素(驱动类名,URL,账号,密码), 存在硬编码,如果需要修改连接的数据库,就只能来修改该源代码.
解决方案:
我们一般把数据库的连接信息存放到属性文件中(db.properties).
接下来再通过Properties类来加载资源文件,并读取其中的信息即可.
1.7 问题7:在DAO方法中拼接SQL语句,很恶心,稍后使用PreparedStatement解决.
1.8 问题8:在每一个DAO方法中都创建一个新的Connection对象,使用之后,就立刻释放了,也就是说没有充分利用Connection对象,而创建Connection对象的成本非常大, 期待:数据库连接池技术(DataSource).
1.9 问题9:DML操作模板是相同的,DQL操作模板也是相同的. 期待:重构设计下.
2. 预编译语句对象
具体的笔记,参考day01的”JDBC相关的API”:
3. 预编译语句对象 VS 静态语句对象
Statement和PreparedStatement的区别:
PreparedStatement 的优点:
1).PreparedStatement 代码的可读性和可维护性. (SQL模板,使用占位符表示参数)
2).PreparedStatement 能最大可能提高性能(预编译),MySQL不支持PreparedStatement的性能优化.
3).PreparedStatement 能保证安全性.
可以防止SQL注入:演示登陆操作
选择:使用PreparedStatement.
4. 事务管理操作
5. 批处理操作
批量操作(batch):当需要成批插入或者更新记录时。
可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率.
JDBC的批量处理语句包括下面两个方法:
addBatch(String sql):添加需要批量处理的SQL语句或是参数;
executeBatch();执行批量处理语句;
通常我们会遇到两种批量执行SQL语句的情况:
多条SQL语句的批量处理; :Statement
一个SQL语句的批量传参; :PreparedStatement
需求:同时向t_student表,插入5000条数据.
在JDBC中,MySQL不支持批量操作.
-------------------------------------------------------------------------
Statement 批处理 : 一次性可以执行多条sql语句,需要编译多次。
应用场景:系统初始化 (创建表,创建数据等)
添加sql语句,st.addBatch(sql) --添加sql语句
批量处理sql语句,int[] st.executeBatch()
清除缓存: st.clearBatch();
-------------------------------------------------------------------------
PreparedStatement 批处理 : 执行一条sql语句,编译一次,执行sql语句的参数不同。
应用场景:表数据初始化
添加批量参数:psmt.addBatch() --添加实际参数,执行之前,需要执行psmt.setXxx()设置实际参数
执行批处理:int[] psmt.executeBatch()
清除缓存:pstm.clearBatch();
6. 大数据类型操作
7. 获取自动生成的主键
8. 小结
Day3--连接池:DataSource+重构设计下
1.为什么需要连接池
1.2 连接池概述:
在Java中,连接池使用javax.sql.DataSource接口来表示连接池/数据源.
注意:DataSource和JDBC一样仅仅只是一个接口,由各大服务器厂商来实现(Tomcat,JBoss等).
常用的DataSource的实现:
C3P0: Hibernate推荐的,但是该连接池在07年之后就不再更新了,不建议使用:性能太太差了.
DBCP: Apache组织的项目,Spring推荐的. 真心不错.
Druid: 阿里巴巴的项目(德鲁伊),世界上最好连接池.
DataSource(数据源)和连接池(Connection Pool)是同一个.
使用连接池和不使用连接池的区别在哪里?
如何获取连接对象:
没有连接池: 通过DriverManager来获取,直接和DBMS连接.
存在连接池: 直接通过连接池来获取连接对象.
Connection conn = DataSource对象.getConnection();
释放连接对象:
没有连接池: conn.close():和数据库服务器(DBMS)断开连接.
存在连接池: conn.close():把Connection对象归还给连接池,并没有和DBMS断开.
2. 连接池的操作和配置
不同的连接池:创建DataSource对象的方式和配置不一样.
-------------------------------------------------------------------------------------
操作连接池: 如何获取DataSource对象.
2.1 DBCP的基本使用
环境准备:
commons-dbcp-1.4.jar
commons-pool-1.5.6.jar
参考文档:
\commons-dbcp-1.3-src\doc\BasicDataSourceExample.java
使用dbcp.properties文件来解除代码中的硬编码:
dbcp.properties
------------------------------------------------------------------------
#BasicDataSource中的属性名=值
在Java代码中读取dbcp.properties文件,并创建DataSource对象.
2.3 Druid连接池使用
Druid连接池(德鲁伊),阿里巴巴的连接池,号称世界上最好的连接池.
https://github.com/alibaba/druid/wiki
Java语言领域性能最好的数据库连接池
类似于DBCP连接池.
准备环境:druid-1.0.15.jar
Druid和DBCP的连接属性是完全相同的,创建连接池的对象不同.
3.重构设计下-JDBC操作模板:JdbcTemplate
3.1 DML操作模板
针对于DML操作(增删改)来说,不同的是:
1):SQL不同:不同操作都需要SQL,仅仅是SQL内容不同.
2):占位符参数不同:不同的操作都需要设置占位符参数,仅仅是占位符参数的个数和类型不同.
重构的思想: 大家来找茬,要做重构至少得是两段代码.
把相同的结构提取出去,把不同内容的使用参数表示.
3.2 DQL(查询)操作模板
3.2.1 DQL(查询)操作模板(SB版本)
上图抽取的DQL操作模板,仅仅只能适用于操作t_student表和Student对象.
如果现在需要操作另一张表(t_teacher),把t_teacher表中的每一行数据封装成Teacher对象.
此时JdbcTemplate的query方法再不再通用.
我们不应该把处理结果集的代码放入到JdbcTemplate中,因为针对于不同的表处理结果集的方式是不一样的.
(因为表的列是不同的)
解决方案:因为不同DAO处理不同的表(处理不同的结果集),所以我们应该把处理结果集的行为交给每一个DAO的实现类,不应该存放在JdbcTemplate中.
制定处理结果集的规范:
3.2.2 DQL(查询)操作模板(普通版本)
3.2.3 DQL(查询)操作模板(文艺版本)
3.2.4 通用的结果集处理器
把不同的表的每一行数据,应该封装到不同的对象中去.
t_tudent -> Student
t_teacher -> Teacher
....
如果一来,我们需要为不同的对象编a写不同的结果集处理器(对象对应的表结构不一致.)
解决方案:编写通用的结果集处理器.
BeanHanlder: 把结果集中唯一的一条数据封装成一个JavaBean对象.
BeanListHandler: 把结果集中每一行数据封装成一个JavaBean对象,再把多个JavaBean对象存储到List集合中.
必须要制定的规范:
1):保证表中的列名和JavaBean中的属性相同.
2):保证表中的列的类型 和 JavaBean中属性的类型要对应. VARCHAR--->String/DECIMAL--->BigDecimal.