JDBC基本概念

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afanti222/article/details/78568862

JDBC

JDBC (Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库。比如建立数据库连接、执行SQL语句进行数据的存取操作。
JDBC是Java语言访问数据库的一种规范,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。
原理:
早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动。
JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!

这里写图片描述

JDBC驱动程序

JDBC驱动程序是各个数据库厂家根据JDBC的规范制作的JDBC实现类。有四种类型:

  1. 第一种类型的驱动程序的实现是通过将JDBC的调用全部委托给其它编程接口来实现的。比如ODBC。这种类型的驱动程序需要安装本地代码库,即依赖于本地的程序,所以便携性较差。比如JDBC-ODBC桥驱动程序 。

  2. 第二种类型的驱动程序的实现是部分基于Java语言的。即该驱动程序一部分是用Java语言编写,其它部分委托本地的数据库的客户端代码来实现。同类型1的驱动一样,该类型的驱动程序也依赖本地的程序,所以便携性较差。

  3. 第三种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序通常由某个中间件服务器提供,这样客户端程序可以使用数据库无关的协议和中间件服务器进行通信,中间件服务器再将客户端的JDBC调用转发给数据库进行处理。

  4. 第四种类型的驱动程序的实现是全部基于JAVA语言的。该类型的驱动程序中包含了特定数据库的访问协议,使得客户端可以直接和数据库进行通信

JDBC层次结构

这里写图片描述
- DriverManager:是一个实现类,它是一个工厂类,用来生产Driver对象的。该类的结构设计模式为工厂方法。
- Driver: 驱动程序对象的接口, 指向一个实实在在的数据库驱动程序对象。DriverManager工厂中有个方法:getDriver(String URL),通过这个方法可以得到驱动程序对象,这个方法是在各个数据库厂商按JDBC规范设计的数据库驱动程序包里的类中静态实现的,也就是在静态块中。
- Connection : 接口,指向一个数据库连接对象。通过DriverManager工厂中的getConnection(String URL)方法得到的数据库连接对象。
- Statement/PrepareStatement:用于执行静态的SQL语句的接口,通过Connection中的createStatement/prepareStatement方法得到的。一般使用PrepareStatement,支持预编译,相对性能好,且安全。
- Resultset: 用于指向结果集对象的接口,结果集对象是通过Statement中的execute等方法得到的。

执行次序

这里写图片描述
JAVA使用JDBC访问数据库的步骤:
1. 加载数据库驱动程序,加载的时候需要将驱动程序配置到classpath之中
2. 连接数据库,通过Connection 接口和 DriverManager 类完成
3. 操作数据库,通过Statement、PreparedStatement、ResultSet 三个接口完成
4. 得到结果集
5. 对结果集做相应的处理(增,删,改,查)
6. 关闭数据库,在实际开发中数据库资源非常有限,操作完之后必须关闭

Statement和PrepareStatement区别

PrepareStatement

  1. PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程

Statement

区别

可读性

Statement编写语句方法

stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
  • 1

PreparedStatement编写语句方法

pstmt = con.prepareStatement("insert into tb_name(col1,col2,col2,col4) values (?,?,?,?)");   
pstmt.setString(1,var1);     
pstmt.setString(2,var2);     
pstmt.setString(3,var3);     
pstmt.setString(4,var4);     
pstmt.executeUpdate();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

statement 显得比较乱而且因为引号的原因比较容易出错,而preparedment则比较直观简洁

性能

每种数据库都会对预编译的语句提供最大的性能优化,因为有些编译语句会被重复调用,所以就把被数据库编译后的执行代码缓存起来,当下次调用只要是相同的预编译语句就不需要编译,只要将参数传入预编译的语句执行代码(相当于一个函数)中就会得到执行,因此大大提高了效率(对mysql来说没有多大区别但是对Oracle来说差距就非常明显)
其次是Batch的使用:

//Demo1: 多次执行preparestatement
PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  

for (n = 0; n < 100; n++) {  

  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]);  
  ps.executeUpdate();  
} 

//Demo2: 使用Batch
PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  

for (n = 0; n < 100; n++) {
  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]);  
  ps.addBatch();  
}  
ps.executeBatch();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Demo1中PreparedStatement被用来多次执行INSERT语句. 在这里, 执行了100次INSERT操作, 共有101次网络往返. 其中,1次往返是预储statement, 另外100次往返执行每个迭代.
Demo2中, 当在100次INSERT操作中使用addBatch()方法时, 只有两次网络往返. 1次往返是预储statement, 另一次是执行batch命令. 虽然Batch命令会用到更多的数据库的CPU周期, 但是通过减少网络往返,性能得到提高. 记住, JDBC的性能最大的增进是减少JDBC驱动与数据库之间的网络通讯.
注:Oracel 10G的JDBC Driver限制最大Batch size是16383条,如果addBatch超过这个限制,那么executeBatch时就会出现“无效的批值”(Invalid Batch Value) 异常。因此在如果使用的是Oracle10G,在此bug减少前,Batch size需要控制在一定的限度

安全(防SQL注入)

String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
  • 1

如果我们把[’ or ‘1’ = ‘1]作为变量passwd传入进来,用户名随意,即变为

 select * from tb_name = 'random' and passwd = '' or '1' = '1';
  • 1

因为’1’=’1’肯定成立,所以可以任何通过验证.更有甚者:
把[drop table tb_name;]作为变量passwd传入进来,则:
select * from tb_name = '随意' and passwd = '';drop table tb_name;
有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.
而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement,有可能要对drop,;等做费尽心机的判断和过虑。

总结

1、PreparedStatement能防止SQL注入问题,而Statement是动态拼出SQL,因此不能解决;
2、PreparedStatement对SQL进行预编译,因此如果我们采用绑定变量的SQL(语句类似,数据不同)数据库只需解析一次(生成执行计划),因此效率要高;
3、Statement由于没有采用绑定变量,因此可能每次都需要编译(生成执行计划),因此对于那种语句类似但数据不同的SQL每次都需要编译,效率低;
4、PreparedStatement开销要比Statement高, 因此如果SQL只执行一次,应该使用Statement。
5、在大多数情况下都应该使用PreparedStatement。

常用数据库的驱动程序及JDBC URL

Oracle数据库:

驱动程序包名:ojdbc14.jar
驱动类的名字:oracle.jdbc.driver.OracleDriver
JDBC URL: jdbc:oracle:thin:@ip:port:databasename
说明:驱动程序包名有可能会变。
其中各个部分含义如下:
- ip :数据库服务器的IP地址,如果是本地可写:localhost或127.0.0.1。
- port :数据库的监听端口,需要看安装时的配置,缺省为1521。
- databasename :数据库的SID,通常为全局数据库的名字。

举例:如果要访问本地的数据库mydb,端口1521,那么URL写法如下:
dbc:oracle:thin:@localhost:1521:mydb
下载地址:http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html

SQL Server数据库

驱动程序包名:msbase.jar mssqlserver.jar msutil.jar
驱动类的名字:com.microsoft.jdbc.sqlserver.SQLServerDriver
JDBC URL:jdbc:microsoft:sqlserver://ip:port;DatabaseName=databasename
说明:驱动程序包名有可能会变。
其中各个部分含义如下:
- ip :数据库服务器的IP地址,如果是本地可写:localhost或127.0.0.1。
- port :数据库的监听端口,需要看安装时的配置,缺省为1433。
- databasename :数据库的名字。
举例:如果要访问本地的数据库mydb,端口1433,那么URL写法如下:
jdbc: microsoft: sqlserver:@localhost:1433; DatabaseName =mydb
下载地址:http://www.microsoft.com/downloads/details.aspx

MySQL数据库

驱动程序包名:mysql-connector-java-5.1.40-bin.jar
驱动类的名字:com.mysql.jdbc.Driver
JDBC URL:jdbc:mysql://dbip:port/databasename
说明:驱动程序包名有可能会变
其中各个部分含义如下:
- ip :数据库服务器的IP地址,如果是本地可写:localhost或127.0.0.1。
- port :数据库的监听端口,需要看安装时的配置,缺省为3306。
- databasename :数据库的名字。
举例:如果要访问本地的数据库mydb,端口3306,那么URL写法如下:
jdbc:mysql://localhost:3306/mydb
下载地址:http://dev.mysql.com/downloads/connector/j/

Access数据库

驱动程序包名:该驱动程序包含在JavaSE中,不需要额外安装。
驱动类的名字:sun.jdbc.odbc.JdbcOdbcDriver
JDBC URL:jdbc:odbc:datasourcename
说明:该驱动只能工作在Windows系统中,首先需要在操作系统中建立一个可以访问Access数据库的本地数据源(ODBC),如果名字为mydb,那么URL写法如下:
jdbc:odbc:mydb

参考

http://www.cnblogs.com/lee/archive/2007/08/25/869656.html

(function () {('pre.prettyprint code').each(function () { var lines = (this).text().split(\n).length;varnumbering = $('
    ').addClass('pre-numbering').hide(); (this).addClass(hasnumbering).parent().append(numbering); for (i = 1; i
展开阅读全文

没有更多推荐了,返回首页