本文已参与「新人创作礼」活动,一起开启掘金创作之路。
引言
各位掘友好,这次给大家归纳整理了JDBC相关知识。本文主要讲述JDBC的原理部分,其中穿插一些数据库的相关知识,另外还有SSM框架中POJO层,DAO层的理解与总结。
通过阅读这篇文章,大家能够快速理解并掌握JDBC的理论知识,并为日后系统地学习框架作一个小小的铺垫。
JDBC的起源
早期SUN公司想编写一套可以连接所有数据库的API,但是刚一开始就发现了问题,各个厂商的数据库服务器存在巨大的差异。后来SUN公司与数据库厂商们商议,最终,由SUN负责提供一套访问数据库的规范,以及连接数据库的协议标准,而各数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API。
SUN公司提供的规范就是JDBC,而各大厂商提供的可以访问其数据库的API称之为驱动。
这里只需记住一点,JDBC是接口,JDBC驱动实现了接口,没有驱动无法实现数据库连接。
简述
在讲解具体的方法前,我们应当知道一次完整的数据库操作是什么样的。
即
- 连接数据库
- 对数据库进行操作(CRUD)
- 关闭数据库连接
连接数据库
连接数据库需要的信息:
- 数据库的地址URL jdbc:数据库://服务器ip
地址:端口号/数据库名?时区设置…
- 数据库的用户名 默认用户名是root
- 数据库的密码
- 数据库的驱动 可在此网站中搜索并下载 对应的数据库驱动
注意下载的版本要与自己使用的一致
以MySql为例
URL:jdbc:mysql://127.0.0.1:port/dbname?......
注:127.0.0.1:数据库服务器所在的ip地址 即localhost (表示数据库服务器在本地)
port:端口号,默认是3306
dbname:要访问数据库的名称
8.0的驱动:com.mysql.cj.jdbc.Driver
5.x版本的数据库驱动:com.mysql.jdbc.Driver
Connnection对象 是java中用来表示数据库连接的对象
连接数据库的方法
步骤:
1.安装驱动(加载驱动类)Class.forName(driver)
2.获取数据库连接 DriverManager.getConnection(url,username,password) 返回Connection类型对象
操作数据库
相关的Java对象和常用方法
Statement对象(可能会出现sql注入的情况,不推荐):
sql语句对象
常用的方法:
- execute(sql): 执行sql语句,返回是否执行成功,成功返回true,失败返回false
- executeUpdate(sql): 执行sql语句(DML操作),返回成功数量
- executeQuery(sql): 执行sql语句(查询),返回 从数据库中查询的数据集合(结果集)
- 当做数据库数据更新时(增加,修改,删除).使用executeUpdate()
- 当做查询操作的时候,使用executeQuery();
使用步骤:
//建立连接
Connection connection=...;
//获得语句处理对象
Statement statement=connection.createStatement();
//构建sql语句(通过拼接)
String sql=".......";
//执行语句
statement.executeUpdate(sql)/executeQuery(sql)...;
复制代码
PreparedStatement对象(推荐):
预编译的sql语句对象(可避免sql注入问题)
常用的方法:
- setString(参数位置 , 参数内容) setInt(参数位置 , 参数内容)…
- executeUpdate() 返回值是一个整数,指的是受影响的行数
- executeQuery() 返回ResultSet对象,用于存放数据库响应的查询结果
这里2、3的使用方法与与statement中的类似,区别在于有无sql参数
使用步骤:
//建立连接
Connection connection = …;
//构建sql语句
String sql="........";
//获得语句处理对象
PreparedStatement preparedStatement=connection.prepareStatement(sql);
//设置参数
preparedStatement.setString(...,...);
//执行语句
preparedStatement.executeUpdate()/executeQuery()...
复制代码
ResultSet对象
使用executeUpdate()进行数据查询后返回的结果集,是存储查询结果的对象。
结果集并不仅仅具有存储的功能,同时还具有操纵数据的功能,可以完成对数据的更新等。
结果集读取数据的方法是getXXX(),如getString(参数)。
参数可以是整型表示第几列(是从1开始的),也可以是列名,返回对应的数据类型的值。
如果对应那一列为null,XXX是对象的话返回null,如果XXX是数字类型,如Float等则返回0,boolean返回false。
XXX可以代表的类型有:基本数据类型如整型(int),布尔型(Boolean),浮点型(Float,Double)等,另外还包括一些特殊的类型,如日期类型(java.sql.Date),时间类型(java.sql.Time),时间戳类型 (java.sql.Timestamp)等。
- 基本的ResultSet
这种ResultSet起到的作用就是查询结果的存储,只能读取一次,不能够来回的滚动读取。
这种结果集的创建方式如下:
Statement st = connection.CreateStatement
ResultSet rs = st.excuteQuery(sql);
复制代码
如果获得这样一个结果集,只能调用它的next()方法,逐个读取数据。
- 可滚动的ResultSet
这个类型支持前后滚动取得纪录next()、previous(),回到第一行first(),同时还支持要去的ResultSet中的第几行 absolute(int n),以及移动到相对当前行的第几行relative(int n),要实现这样的ResultSet在创建Statement时用如下的方法。
Statement st = connection. createStatement (int resultSetType, int resultSetConcurrency)
ResultSet rs = st.executeQuery(sql)
复制代码
其中
resultSetType 是设置 ResultSet 对象的类型可滚动,或者是不可滚动:
- ResultSet.TYPE_FORWARD_ONLY 只能向前滚动
- ResultSet.TYPE_SCROLL_INSENSITIVE
- ResultSet.TYPE_SCROLL_SENSITIVE
后面两个方法都能够实现任意的前后滚动,支持结果集backforward ,random ,last ,first 等操作, 二者的区别在于前者对数据库中数据做出的修改是不敏感的,而后者对于修改敏感。(session暂且不讲)
resultSetConcurency 是设置 ResultSet 对象能否被修改,取值如下:
- ResultSet.CONCUR_READ_ONLY 设置为只读类型的参数。
- ResultSet.CONCUR_UPDATABLE 设置为可修改类型的参数。
例如:
Statement st = conn.createStatement(Result.TYPE_SCROLL_INSENITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rs = st.excuteQuery(sqlStr) ;
复制代码
用这个 Statement 执行的查询语句得到的就是可滚动的 ResultSet 。
POJO层
之所以考虑在这篇文章讲述POJO,是因为我希望能提供给大家一个更大的视野,不仅仅局限于JDBC,初步认识整个后端开发框架。本人笔力有限,路过的大佬请不吝赐教。若有什么问题,可在评论区讨论交流。
什么是POJO
Plain Ordinary Java Object 即简单Java对象,POJO的内在含义是指那些没有从任何类继承,没有实现任何接口,也没有被其他框架侵入的Java对象。
简而言之,POJO可以看作是与数据库中的表相映射的Java对象。
一个POJO持久化以后就是PO(persistent object),可以把数据库中一条记录作为一个PO对象处理,多个记录可以用PO的集合表示。
POJO的意义
使开发者专注于业务逻辑和脱离框架的单元测试。因为它的简单和灵活,使得POJO能够任意扩展,从而胜任多个场合,让一个模型贯穿多个层。
在此之前还没接触这个掘友可能暂时无法体会其中的好处,日后我会陆续发一些文章,帮助大家理解。
POJO与PO区别
POJO是由new创建,由GC回收。但是PO对象是insert数据库创建,由数据库delete删除,持久对象生命周期和数据库密切相关。持久对象往往只能存在一个Connection中,Connection关闭后,持久对象就不存在了
如何去写
POJO已经说完了,我们回归JDBC,有了上面的铺垫,具体应该怎么操作相信大家心里已经有答案。
我们可以将数据库中的每张数据表对应一个Java实体类(POJO),实体类的属性 对应 数据表的字段,让数据库表中的数据暂存在实体类对象中。
这样做的话,不必每次都访问数据库拿到想要的数据,直接通过对象获取即可
dao层
Dao(Data access object),即数据访问层。封装了一些对数据库的操作,具体可到某个表、某个实体的增删改查,映射到某个java对象(POJO)。
为什么dao这一层
一个庞大的项目一定是分层的,如MVC模式。这么做是为了降低各层的耦合度,使得各个层职责边界清晰,便于对后续代码进行维护扩展,同时也是为了整个团队提高开发效率。
如何使用
而实际操作上,我们可以写一个接口,用来处理程序和数据库之间的交互,把具体的操作业务放在接口的实现类中。
可以调用此接口来进行数据的处理,而不用关心此接口的具体实现类是哪个类,结构清晰。
三种实现方式
这里为大家介绍三种实现JDBC的方式,供大家参考。
1.0
JDBC的原理部分和需要用的一些对象方法都已经说完,将其组合运用起来就是我们JDBC的1.0版本。
2.0
在1.0版本的基础上,我们做些改进。
可以将连接数据库的参数信息放在properties配置文件中,以键值对的形式(key=value)存放。 同时,引入static代码块,通过Properties和文件流,初始化参数。
这么一来,可以提高代码的利用率,每次修改只需更改对应的参数就行了。
需注意properties文件格式:
- 不可有空格
- 未写完之前不允许回车
3.0
3.0版本,我们可以更进一步。借助第三方工具,使用连接池,如Apache开发的DBCP2,hibernate工作组维护的C3P0,还有阿里巴巴的druid等等,帮助我们自动管理数据库的连接和释放。
需要注意的是,使用第三方工具时,要严格按照其规定格式。例如,在使用druid时,properties文件参数名key是固定的
使用连接池的好处
- 数据库资源得到重用
- 减少数据库连接建立和释放的资源开销,提高系统响应速度,降低I/O开销
- 进行统一的数据库管理,减少JVM垃圾,减少数据库的过载
数据库的进阶操作
事务处理
学习事务处理之前,首先得了解数据库的ACID特性
- 原子性:事务可以看做一个不可再分的整体事务中的操作要么都发生,要么都不发生
- 一致性:事务一旦完成,所有的数据需要保持一致
- 隔离性:多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- 持久性:事务的操作最终会持久化到数据库中
随着业务场景不断复杂,单一操作已经不能满足我们的需求,需要将多个操作或者命令一起执行。
这就要求所有命令全部成功执行才意味着该事务的成功,任何一个命令的失败都意味着该事务失败(例如:转账)
那么具体该如何操作呢?请接着往下看
- 在执行sql语句之前需设置事务手动提交
connection.setAutoCommit(false);
此时,使用executeUpdate() 等语句,数据并未持久化 - 经判定后 事务执行成功 提交事务
connection.commit();
若事务执行出现异常,数据需要回滚
connection.rollback(); - 当本次操作结束后,为防止影响后续操作,还需将事务的提交方式设置为自动提交
connection.setAutoCommit(true);
前辈们已经写好了方法,我们只要会用就行了。
Batch 批处理
当我们要进行大量的DML操作时,我们可以使用批处理
这样的话,可以减少资源的浪费,一次执行多条sql语句,提高效率,缩短sql语句执行时间
别忘了,每当执行完一次批处理,就清空批处理
步骤\语句 | Statement | PreparedStatement |
---|---|---|
添加(不宜添加太多,防止内存溢出) | statement.addBatch( sql ) | preparedStatement.addBatch() |
执行(批处理执行后,会返回int[ ]) | statement.executeBatch() | preparedStatement.executeBatch() |
清空 | statement.clearBatch() | preparedStatement.clearBatch() |
END
希望各位掘友看完这篇文章后,有所收获。有什么问题的话,可以在评论区留言。如果觉得文章写得还不错的话,就给个赞呗,您动动手指就是对我莫大的鼓励!