世界微尘里,吾宁爱与憎
从零开始使用JDBC
从零开始使用JDBC

从零开始使用JDBC

以MySQL为例

基本原理

JDBC服从三层模型,即客户端不直接调用数据库,而是调用服务器上的中间件层,而中间件层完成数据库的查询操作,客户端和中间层之间的通信一般是通过HTTP来实现的,JDBC管理者中间层和后台数据库之间的通信,而JDBC与后台服务器中间的通讯是通过驱动管理器进行的,驱动管理器则通过驱动程序与实际的数据库进行通讯

总结起来就是,客户在客户端通过可视化对中间层传达自己的要求,而中间层使用JDBC通过驱动程序(比如MySQL)来与位于服务端的数据库通讯

数据库URL

在连接数据库时,我们必须使用与数据库类型相关的参数,例如主机名,端口号和数据库名,而在JDBC向驱动管理器传递这些参数时,把这些参数都集成在数据库URL当中

JDBC URL一般语法为

jdbc:subprotocol:other stuff

这里的subprotocol指用于连接到数据库的具体驱动程序,比如MySQL,而subprotocol取决于使用了什么驱动程序,具体格式随使用驱动程序的不同而不同

1.导入jar包

访问MySQL数据库时需要用到第三方的库,在使用JDBC之前,需要将MySQL的jar包放到项目的lib文件夹下并导入

2.注册驱动器类

许多JDBC的jar文件会自动注册驱动器类,在这种情况下,可以跳过手动注册的步骤,如果驱动程序jar文件不支持自动注册,那就需要找出数据库提供商使用的JDBC驱动器类的名字,比如MySQL为

com.mysql.jdbc.Driver

通过在Java程序中加入以下代码来初始化驱动器

Class.forName("com.mysql.jdbc.Driver");

另一种初始化驱动器类的方法是设置jdbc.drivers的参数,这里不做介绍

3.建立与数据库的连接

根据之前提到的JDBC URL格式,使用MySQL进行通讯的具体URL格式为

"jdbc:mysql:[数据库所处于的ip]:[数据库的端口号]/[数据库名称]?characterEncoding=[编码方式]

构造出具体的JDBC URL后,我们可以使用DriverManager类的getConnection方法来建立与数据库的连接

Connection c = DriverManager.getConnection(
   "jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root", "admin");

这里的root和admin分别是数据库账号与数据库密码,是getConnection方法的另外两个参数

4.使用JDBC语句

在执行SQL语句之前,首先要创建一个Statement对象,而在创建这个对象时,需要用到之前建立连接是getConnection方法返回的对象

Statement s = c.createStatement;

接着,要把执行的SQL语句放入字符串中.例如:

String sql = "insert into hero values(null,'提莫'=313.0f,50)";

然后,调用Statement接口中的excute/excuteUpdate/excuteQuery方法

s.excute(sql);

excuteUpdate一般用于INSERT,UPDATE,DELETE之类的语句,会返回手SQL语句影响的行数而excuteQuery用于执行SELECT查询操作,因为它会返回一个ResultSet对象,可以利用这个对象对所有查询结果进行每次一行的迭代遍历,excute通常只用于由用户提供的交互式查询

5.关闭连接

在相关操作进行完成后,对数据库需要进行关闭

先关闭Statement,再关闭Connection

s.close();
c.close();

Statement预编译

相对于使用Statement来传递sql语句来说,有一种在某些方面上表现更为优秀的类,就是Statement预编译

PreparedStatement类

和Statement类一样,PreparedStatement也是用来执行sql语句的,但与创建Statement不同的是,这里创建PreparedStatement时需要根据具体的sql语句来创建

比如如果使用Statement类的话,如果有查询某个出版社里的所有图书而不考虑具体的作者,就需要这样写SQL语句

SELECT Book,Price
FROM Books,publishers
WHERE Books.Publisher_id = Publishers.Publisher_id
AND Publishers.Name = ...

如果我们要查询A作者,就要在…里填入A,如果我们要查询B作者,就要在…里填入B,而且进行这两次查询时,都需要完整的写下这段代码,这是很没有必要的

而有一个很好的解决方式就是采用类似于字符格式化那样的操作

String publisherQuery
= "SELECT Books.Price,Books"
+ "FROM Books,Publishers"
+ "WHERE Books.Publisher_id = Publishers.Publisher_id"
+ "AND Publishers.Name = ?"

在预编译语句中,每个宿主变量都用?来表示,如果存在一个或一个以上的变量,那么就在设置变量时要注意?的位置

接下来便要使用这个sql语句建立一个PreparedStatement类

PreparedStatement s = c.prepareStatement(publisherQuery)//c为之前的Connection对象

在执行预编译语句之前,必须使用set方法将变量绑定到实际的值上,针对不同的数据类型也有不同的set方法

String publisher = "A"
stat.setString(1,publisher)

第一个参数是指需要设置的宿主的位置,第二个参数指赋给宿主的值

使用PreparedStatement比使用Statement有以下优点:

  • 有预编译机制,性能比Statement更快
  • 使用参数设置,可读性更好,便于维护
  • 可以防止SQL注入攻击

事务

在我们使用JDBC的时候,有很多时候,会不小心写出很多错误的语句,比如我们要对数据库里的某一个变量进行-1后再+1的操作,而如果+1这一步操作写错了某一个字母,这一步操作就不会执行成功,但-1操作还是可以正常进行并且已经提交了,最后的结果就会成-1而不是期望中的不变

这时,我们就希望可以用一种操作,能对进行的错误语句进行回滚,即数据库自动回复为没有进行执行这些错误语句之前的状态

事务,为了确保数据库完整性,在事务中的多个操作,要么都成功,要么都失败

默认情况下,数据库处于自动提交的模式,即SQL语句一旦被执行便被提交到数据库,所以,要先关闭这种自动提交模式

c.setAutoCommit(false)//c为Connection对象

之后,由于关闭了自动提交,所以需要进行手动提交

String sql1 = "update hero set hp = hp +1 where id = 22";
s.execute(sql1);
String sql2 = "updata hero set hp = hp -1 where id = 22";
s.execute(sql2);
c.commit();// 手动提交

在这种情况下,一旦上一次commit到这次commit中间的这部分sql语句中有任何一句发生了错误后,就会导致整个sql语句执行失败,也就是回滚到了执行第一句sql语句之前

而实际情况中,并不一定要等到sql语句出现错误才进行回滚操作,你也可以自己进行这种回滚操作

c.rollback()

配合rollback,你就可以自己设置具体回滚到哪一句sql语句

通过设置保存点Savepoint

Savepoint svpt = c.setSavepoint;
c.rollback(svpt);

注:

在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务

ORM & DAO

两种设计思想

ORM=Object Relationship Database Mapping

对象和关系数据库的映射

简单说,一个对象,对应数据库里的一条记录

DAO=DataAccess Object

数据访问对象

实际上就是运用了ORM的思路,把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注