《JDBC经典攻坚学习笔记.doc》由会员分享,可在线阅读,更多相关《JDBC经典攻坚学习笔记.doc(16页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、JDBC(Java Data Base Connectivity,java数据库连接),由一些接口和类构成的API。JDBC是J2SE的一部分,由java.sql.javax.sql包组成。JDBC的核心是接口,Driver实现了JDBC的接口。SUN公司设计接口(或者规范、标准),生产厂商来实现这些接口,即它们各自的Driver。JAVA程序包括JAVA应用程序和小应用程序,主要是根据JDBC方法实现对数据库的访问和操作。完成的主要任务有:请求与数据库建立连接;向数据库发送SQL请求;为结果集定义存储应用和数据类型;查询结果;处理错误;控制传输、提交及关闭连接到呢个操作。JDBC管理器为我们
2、提供了一个“驱动程序管理器”,它能够动态地管理和维护数据库查询所需要的所有驱动程序对象,实现JAVA程序与特定驱动程序的连接,从而体现JDBC的“与平台无关”这一特点。它完成的主要任务有:为特定数据库选择驱动程序;处理JDBC初始化调用;为每个驱动程序提供JDBC功能的入口;为JDBC调用执行参数等。驱动程序处理JDBC方法,想特定数据库发送SQL请求,并为JAVA程序获取结果。在必要的时候,驱动程序可以翻译或优化请求,使SQL请求符合DBMS支持的语言。驱动程序可以完成下列任务:建立与数据库的连接;向数据库发送请求;用户程序请求时,执行翻译;将错误代码格式化成标准的JDBC错误代码等。JDB
3、C是独立于数据库管理系统的,而每个数据库系统均有自己的协议与客户机通信,因此,JDBC利用数据库驱动程序来使用这些数据库引擎。JDBC驱动程序有数据库软件商和第三方的软件商提供,因此,根据编程所使用的数据库系统不同,所需要的驱动程序也有所不同。应用程序JDBCDB2 DriverOracleDB2MySQL四类JDBC驱动程序尽管存在数据库语言标准SQL-92,但由于数据库技术发展的原因,各公司开发的SQL存在着一定的差异。因此,当我们想要连接数据库并存取其中的数据时,选择适当类型的JDBC驱动程序是非常重要的。目前JDBC驱动程序可细分为四种类型,如下图所示。不同类型的JDBC驱动程序有着不
4、一样的特性和使用方法。下面将说明不同类型的JDBC驱动程序之间的差异。类型1:JDBC-ODBC Bridge。这类驱动程序的特色是必须在我们的计算机上事先安装好ODBC驱动程序,然后通过JDBC-ODBC Bridge的转换,把JAVA程序中使用的JDBC API转换成ODBC API,进而通过ODBC来存取数据库。类型2:JDBC-Native API Bridge。同类型1一样,这类驱动程序也必须在我们的计算机上先安装好特定的驱动程序(类似ODBC),然后通过JDBC-Native API Bridge的转换,把JAVA程序中使用的JDBC API转换成Native API,进而存取数据
5、库。类型3:JDBC-Middleware。必须在安装数据库管理系统的服务器端加装软件(Middleware),中介软件会负责所有存取数据库时必要的转换。类型4:Pure JDBC Driver。使用这类驱动程序时无需安装任何附加的软件,所有存取数据库的操作都直接有JDBC驱动来完成。用JDBC连接Oracle数据库方法一:在Tomcat中配置Oracle的驱动程序Oracle JDBC驱动程序的位置:H:Oraclejdbclibclasses12.jar将此驱动程序拷贝到Tomcat的安装目录的lib文件之中:C:Program Filesapache-tomcat-6.0.14lib此时
6、Tomcat就可以连接到oracle驱动程序Oracle驱动名称:使用Class.forName()方法加载相应的数据库驱动程序:Class.forName(oracle.jdbc.driver.OracleDriver);oracle.jdbc.driver.OracleDriver我们不必死记,在Oracle驱动程序classes12.jar下面的oralce.jdbc.dreiver包中就可找到相应OracleDriver.class。Oracle连接地址:String URL=”jdbc:oracle:thin:127.0.0.1:1521:myOracle”; 或者:String U
7、RL=”jdbc:oracle:thin:localhost:1521:myOracle”;Connection ct=DriverManager.getConnection(URL);其中jdbc是协议,1521是Oracle默认的端口号,myOracle是数据库的名称与使用JDBC-ODBC类似,只是连接的驱动名称和驱动地址改变。方法二:JDBC-ODBC Bridge方法Oracle驱动名称:使用Class.forName()方法加载相应的数据库驱动程序:Class.forName(sun.jdbc.odbc.JdbcOdbcDriver);Oracle连接地址:String URL=”
8、jdbc:odbc:odbcName”;Connection ct=DriverManager.getConnection(URL);其中odbcName是我们设置的要创建的数据源。连接数据库的步骤:1、注册驱动(只做一次)Class.forName(sun.jdbc.odbc.JdbcOdbcDriver);2、建立连接(Connection)String URL=”jdbc:odbc:odbcName”;Connection ct=DriverManager.getConnection(URL,”scott”,”tiger”);数据库连接(Connection)是非常稀有的资源,用完后必须
9、马上释放,如果Connection不能及时正确的关闭或释放将导致系统脱机。Connection的使用原则是尽量晚的创建,尽量早的释放。3、创建执行SQL的语句(Statement)Statement st=ct.createStatement();4、执行语句(ResultSet)ResultSet rs=st.executeQuery(“select * from emp”);5、处理执行结果While(rs.next()System.out.println(“用户名:”+rs.getString(2);6、关闭和释放资源rs.close();sm.close();ct.close();为了
10、确保JDBC资源不在出现异常或错误等情况下被不正常关闭,我们应该在用完JDBC资源之后关闭且释放他们。JDBC连接池提供了JDBC连接定义和数目有限的连接,如果数量不够,就需要长时间等待。不正常关闭JDBC连接会导致等待回收无效的JDBC连接。只有正常的关闭和释放JDBC连接,JDBC资源才可以被快速的重用,使性能得到改善。为了确保构建的代码在所有的情况下,甚至是异常和错误条件下,都能关闭和释放JDBC资源。一下代码显示了JDBC资源的获得和使用都封装在“Try-Catch-Finally”结构中。其中,在finally子句中处理JDBC资源的关闭,使所有情况下关闭都将发生。为了简洁,此代码中
11、只把异常抛出并为对异常进行处理。public static void main(String args) throws ExceptionString url=jdbc:oracle:thin:localhost:1521:myOracle;Connection ct=null;Statement st=null;ResultSet rs=null;try /1.加载驱动Class.forName(oracle.jdbc.driver.OracleDriver);/2.得到连接ct=DriverManager.getConnection(url, scott,tiger);/创建执行的SQL语
12、句st=ct.createStatement();/执行语句rs=st.executeQuery(select * from emp);/处理执行结果while(rs.next()/用户名System.out.println(用户名:+rs.getString(2); finallytryif(rs!=null)rs.close();finallytryif(st!=null)st.close();finallyif(ct!=null)ct.close();代码优化:新建一个(public final)工具类JdbcUtils,把优化后的代码都放在这个类里面。由于这个类不需要构造实例,因此我们
13、我们定义一个private的构造方法,这样别人就无法new一个对象了。访问这个类有两种方式:一种是单例模式,但是比较复杂;另一种是提供一些静态方法。1、由于驱动只需要注册一次,我们知道静态代码块只有类被装载到虚拟机的时候才被执行一次,因此,我们把注册驱动语句放在一个静态的代码块中。statictry Class.forName(jdbc:odbc:wanghao); catch (ClassNotFoundException e) throw new ExceptionInInitializerError(e);2、当我们的增、删、改、查的操作很多时,如果程序中每个方法都有URL、用户名和密码
14、,我们修改起来就会很麻烦。我们也需要对这一块优化,把他们放到JdbcUtils中,这样就可以实现“一改全改”。private static String url=sun.jdbc.odbc.JdbcOdbcDriver;private static String user=scott;private static String password=tiger;3、建立连接语句我们也可以把它放在JdbcUtils的一个静态方法中,当我们需要链接时,我们就到JdbcUtils中去取。public static Connection getConnection() throws SQLExceptio
15、nreturn DriverManager.getConnection(url, user, password);4、对释放资源部分进行优化。public static void free(ResultSet rs,Statement st,Connection ct)tryif(rs!=null)rs.close();catch(SQLException e)e.printStackTrace();finallytryif(st!=null)st.close();catch(SQLException e)e.printStackTrace();finallyif(ct!=null)try c
16、t.close(); catch (SQLException e) e.printStackTrace();完整程序代码如下所示:package com.wanghao.jdbc;import java.sql.Connection;import java.sql.ResultSet;import java.sql.Statement;public class Base /* * param args * throws Exception */public static void main(String args) throws Exception / TODO Auto-generated
17、method stubtest();static void test()throws ExceptionConnection ct=null;Statement st=null;ResultSet rs=null;try /2.得到连接ct=JdbcUtils.getConnection();/创建执行的SQL语句st=ct.createStatement();/执行语句rs=st.executeQuery(select * from emp);/处理执行结果while(rs.next()/用户名System.out.println(用户名:+rs.getString(2); finallyJ
18、dbcUtils.free(rs, st, ct);/工具类JdbcUtilsimport java.sql.Statement;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;public final class JdbcUtils private JdbcUtils()/private static String url=jdbc:odbc:wanghao;private static String url=jdbc
19、:oracle:thin:localhost:1521:myOracle;private static String user=scott;private static String password=tiger;statictry /Class.forName(sun.jdbc.odbc.JdbcOdbcDriver);Class.forName(oracle.jdbc.driver.OracleDriver); catch (ClassNotFoundException e) throw new ExceptionInInitializerError(e);public static Co
20、nnection getConnection() throws SQLExceptionreturn DriverManager.getConnection(url, user, password);public static void free(ResultSet rs,Statement st,Connection ct)tryif(rs!=null)rs.close();catch(SQLException e)e.printStackTrace();finallytryif(st!=null)st.close();catch(SQLException e)e.printStackTra
21、ce();finallyif(ct!=null)try ct.close(); catch (SQLException e) e.printStackTrace();SQL注入,PreparedStatement和Statement 在SQL中包含特殊字符或SQL的关键字(如:or 1 or)时Statement将出现不可预料的结果(出现异常或查询的结果不正确),可用PreparedStatement来解决。PreparedStatement(从Statement扩展而来)相对Statement的优点:1、没有SQL注入的问题。2、Statement会使数据库频繁编译SQL,可能会造成数据库缓
22、冲区溢出。3、数据库和驱动可以对PreparedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)。为什么要始终使用PreparedStatement代替Statement?一.代码的可读性和可维护性.虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次,大家可以通过下面的例子:stmt.executeUpdate(insert into tb_name (col1,col2,col2,col4) values (+var1+,+var2+,+var3+
23、,+var4+);注意Statement和PreparedStatement语句上的区别:Statement是在执行的时候才传入SQL语句,而PreparedStatement在创建的时候就已经传入了SQL语句。perstmt = con.prepareStatement(insert into tb_name (col1,col2,col2,col4) values (?,?,?,?);perstmt.setString(1,var1);perstmt.setString(2,var2);perstmt.setString(3,var3);perstmt.setString(4,var4);
24、perstmt.executeUpdate();二.PreparedStatement尽最大可能提高性能.每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次
25、操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:insert into tb_name (col1,col2) values (11,22);insert into tb_name (col1,col2) values (11,23);即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.当然并不是所有预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.三.最重要的一点是极大地提高了安全性.即使到目前为
26、止,仍有一些人连基本的恶义SQL语法都不知道.String sql = select * from tb_name where name= +varname+ and passwd=+varpasswd+;如果我们把 or 1 = 1作为varpasswd传入进来.用户名随意,看看会成为什么?select * from tb_name = 随意 and passwd = or 1 = 1;因为1=1肯定成立,所以可以任何通过验证.更有甚者:把;drop table tb_name;作为varpasswd传入进来,则:select * from tb_name = 随意 and passwd =
27、 ;drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement,有可能要对drop,;等做费尽心机的判断和过虑.JDBC中的数据类型与日期问题Java.util.Date.getTime()返回自1970年1月1日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。public static void main(String args) throws SQLExcep
28、tion create(4,zhushidong,new Date(),5500.00f);static void create(int id,String name,Date birthday,float money) throws SQLException Util包里面的Date类型!Connection ct=null;PreparedStatement ps=null;ResultSet rs=null;tryct=JdbcUtils.getConnection();String sql=insert into wanghao(id,name,birthday,money)value
29、s (?,?,?,?);ps=ct.prepareStatement(sql);ps.setInt(1, id);ps.setString(2, name);ps.setDate(3, new java.sql.Date(birthday.getTime();ps.setFloat(4, money);int i=ps.executeUpdate(); System.out.println(i=+i);finallyJdbcUtils.free(rs, ps, ct);Util包里面的Date类型是sql包里Date类型的父类,由程序很容已看出我们企图把一个父类赋值给一个子类,这是不允许的,反
30、过来一个子类赋值给一个父类则是可以的。因此我们需要做这样的new java.sql.Date(birthday.getTime()一个转换。J2EE 三层架构所谓的三层开发就是将系统的整个业务应用划分为表示层业务逻辑层数据访问层,这样有利于系统的开发、维护、部署和扩展。分层是为了实现“高内聚、低耦合”。采用“分而治之”的思想,把问题划分开来各个解决,易于控制,易于延展,易于分配资源。表示层:负责直接跟用户进行交互,一般也就是指系统的界面,用于数据录入,数据显示等。意味着只做与外观显示相关的工作,不属于他的工作不用做。 业务逻辑层:用于做一些有效性验证的工作,以更好地保证程序运行的健壮性。如完成
31、数据添加、修改和查询业务等;不允许指定的文本框中输入空字符串,数据格式是否正确及数据类型验证;用户的权限的合法性判断等等,通过以上的诸多判断以决定是否将操作继续向后传递,尽量保证程序的正常运行。 数据访问层:顾名思义,就是用于专门跟数据库进行交互。执行数据的添加、删除、修改和显示等。需要强调的是,所有的数据对象只在这一层被引用,如System.Data.SqlClient等,除数据层之外的任何地方都不应该出现这样的引用。表示层:1.基于WEB的JSP、Servlet、Struts、Webwork、Spring WEB MVC等;2.基于客户端的Swing、SWT等(rmi,iiop);业务逻辑
32、层:Pojo(Service,Manager)、Domain、Session EJB、Spring用接口隔离,用Domain或DTO传递数据数据访问层:JDBC、Ibatis、Hibernate、JDO、Entity Bean用接口隔离,用Domain或DTO传递数据数据访问层是为业务逻辑层服务的,业务逻辑层操作的是对象而数据库是关系型数据库。我们需要在我们的数据访问层把表和字段屏蔽掉,把它变成对象来传递给我们的业务逻辑。传递要通过Domain Object(域对象),Domain Object是一块比较重要的知识,下面我们来讲一下Domain Object。Domain Object(域对象
33、)的三种模型由于当前O/R Mapping, DAO开发结构的层次划分,导致出现了大量的纯粹数据对象。这些数据对象只带有getter, setter属性,而不具有属于自己的方法,起着Data Transfer Object的作用。 Domain Object 则是重新提出并进一步探讨纯面向对象编程的概念:对象不仅应该具有数据,而且应该具有自己的方法。第一种模型(贫血的Domain Object)只有getter/setter方法的纯数据类,所有的业务逻辑完全由business object来完成(又称TransactionScript),这种模型下的domain object被Martin F
34、owler称之为“贫血的domain object”。下面用举一个具体的代码来说明一般情况下,Domain Object单独放在一个domain包里面。一个Domain Object都会有一张表和他对应。例如下面的Domain 对象Staff就在数据库中对应一张名叫STAFF的表。public class Staff private int id;private String name;private Date birthday;private float money;public int getId() return id;public void setId(int id) this.id
35、= id;public String getName() return name;public void setName(String name) this.name = name;public Date getBirthday() return birthday;public void setBirthday(Date birthday) this.birthday = birthday;public float getMoney() return money;public void setMoney(float money) this.money = money;下面我们定义一个数据访问层
36、的接口,这个接口是给业务逻辑层用的,最终业务逻辑层会用这个接口完成对数据的增、删、改、查操作。接口我们一般也放在一个专门的包里面如:com.wanghao.jdbc.dao,在这个包中有一个StaffDao接口,是专门访问Staff对象用的。package com.wanghao.jdbc.dao;import com.wanghao.jdbc.domain.Staff;public interface StaffDao public void addStaff(Staff staff);public Staff getStaff(int staffId);public Staff findS
37、taff(String loinName,String password);public void update(Staff staff);public void delete(Staff staff);三层架构这样设计有一个好处就是:让我们的业务逻辑层不依赖数据访问层的具体实现,业务逻辑层只使用接口,至于具体实现是什么,它并不关心。这样的好处是:比如上面的StaffDaoJdbcImplement实现,我们是用JDBC实现的。我们也可以把这个实现换成Hibernate来实现,用的还是同一个接口。这种替换如果服从三层架构,就会在不影像到你的业务逻辑层的情况下,且换掉你的数据访问层。接下来我们用
38、JDBC实现这个接口。接口的实现类我们也要转本放一个包里面,如我们的例子就把接口的实现类StaffDaoJdbcImplement放在com.wanghao.jdbc.dao.implement里面。代码如下所示:package com.wanghao.jdbc.dao.implement;import java.sql.*;import com.wanghao.jdbc.JdbcUtils;import com.wanghao.jdbc.dao.DaoException;import com.wanghao.jdbc.dao.StaffDao;import com.wanghao.jdbc.
39、domain.Staff;public class StaffDaoJdbcImplement implements StaffDaopublic void addStaff(Staff staff) Connection ct=null;PreparedStatement ps=null;ResultSet rs=null;tryct=JdbcUtils.getConnection();String sql=insert into staff(id,name,birthday,money) values(?,?,?,?);ps=ct.prepareStatement(sql);ps.setI
40、nt(1, staff.getId();ps.setString(2, staff.getName();ps.setDate(3, new java.sql.Date(staff.getBirthday().getTime();ps.setFloat(4, staff.getMoney();ps.executeUpdate();catch(SQLException e)throw new DaoException(e.getMessage(),e);finallyJdbcUtils.free(rs, ps, ct);public void delete(Staff staff) Connect
41、ion ct=null;PreparedStatement ps=null;ResultSet rs=null;tryct=JdbcUtils.getConnection();String sql=delete from staff where id=+staff.getId();ps=ct.prepareStatement(sql);ps.executeUpdate();catch(SQLException e)throw new DaoException(e.getMessage(),e);finallyJdbcUtils.free(rs, ps, ct);public Staff fin
42、dStaff(String loinName, String password) Connection ct=null;PreparedStatement ps=null;ResultSet rs=null;Staff staff=null;tryct=JdbcUtils.getConnection();String sql=select id,name,money,birthday from staff where name=?;ps=ct.prepareStatement(sql);ps.setString(1, loinName);rs=ps.executeQuery();while(r
43、s.next()staff = mappingStaff(rs);catch(SQLException e)throw new DaoException(e.getMessage(),e);finallyJdbcUtils.free(rs, ps, ct);return staff;public Staff getStaff(int staffId) Connection ct=null;PreparedStatement ps=null;ResultSet rs=null;Staff staff=null;tryct=JdbcUtils.getConnection();String sql=select id,name,money,birthday from staff where id=?;ps=ct.prepareStatement(sql);ps.setInt(1, staffId);rs=ps.executeQuery();while(rs.next()staff = mappingStaff(rs);catch(SQLException e)throw new DaoException(e.