jdbc笔记.docx
《jdbc笔记.docx》由会员分享,可在线阅读,更多相关《jdbc笔记.docx(27页珍藏版)》请在冰豆网上搜索。
![jdbc笔记.docx](https://file1.bdocx.com/fileroot1/2023-2/23/aeffbfc9-daba-4c76-bb1c-a0daf6979d18/aeffbfc9-daba-4c76-bb1c-a0daf6979d181.gif)
jdbc笔记
JDBC第一天
一、JDBC原理概述
1,JDBC是一套协议,是JAVA开发人员和数据库厂商达成的协议,也就是由Sun定义一组接口,由数据库厂商来实现,
并规定了JAVA开发人员访问数据库所使用的方法的调用规范。
2,JDBC的实现是由数据库厂商提供,以驱动程序形式提供。
3,JDBC在使用前要先加载驱动。
JDBC对于使用者要有一致性,对不同的数据库其使用方法都是相同的。
4、driver开发必须要实现Driver接口。
JDBC驱动程序的类型
目前比较常见的JDBC驱动程序可分为以下四个种类:
(1)JDBC-ODBC桥加ODBC驱动程序
JavaSoft桥产品利用ODBC驱动程序提供JDBC访问。
注意,必须将ODBC二进制代码(许多情况下还包括数据库客户机代码)加载到使用该驱动程序的每个客户机上。
因此,这种类型的驱动程序最适合于企业网(这种网络上客户机的安装不是主要问题),或者是用Java编写的三层结构的应用程序服务器代码。
(2)本地API
这种类型的驱动程序把客户机API上的JDBC调用转换为Oracle、Sybase、Informix、DB2或其它DBMS的调用。
注意,象桥驱动程序一样,这种类型的驱动程序要求将某些二进制代码加载到每台客户机上。
(3)JDBC网络纯Java驱动程序
这种驱动程序将JDBC转换为与DBMS无关的网络协议,之后这种协议又被某个服务器转换为一种DBMS协议。
这种网络服务器中间件能够将它的纯Java客户机连接到多种不同的数据库上。
所用的具体协议取决于提供者。
通常,这是最为灵活的JDBC驱动程序。
有可能所有这种解决方案的提供者都提供适合于Intranet用的产品。
为了使这些产品也支持Internet访问,它们必须处理Web所提出的安全性、通过防火墙的访问等方面的额外要求。
几家提供者正将JDBC驱动程序加到他们现有的数据库中间件产品中。
(4)本地协议纯Java驱动程序
这种类型的驱动程序将JDBC调用直接转换为DBMS所使用的网络协议。
这将允许从客户机机器上直接调用DBMS服务器,是Intranet访问的一个很实用的解决方法。
由于许多这样的协议都是专用的,因此数据库提供者自己将是主要来源,有几家提供者已在着手做这件事了。
据专家预计第(3)、(4)类驱动程序将成为从JDBC访问数据库的首方法。
第
(1)、
(2)类驱动程序在直接的纯Java驱动程序还没有上市前会作为过渡方案来使用。
对第
(1)、
(2)类驱动程序可能会有一些变种,这些变种要求有连接器,但通常这些是更加不可取的解决方案。
第(3)、(4)类驱动程序提供了Java的所有优点,包括自动安装(例如,通过使用JDBC驱动程序的appletapplet来下载该驱动程序)。
5、JDBC的API
java.sql包和javax.sql包
DriverManager类(驱动管理器),它可以创建连接,它本身就是一个创建Connection的工厂(Factory)。
Driver接口入口
Connection接口,会根据不同的驱动产生不同的连接
Statement接口,发送sql语句
ResultSet接口(结果集),是用来接收select语句返回的查寻结果的。
其实质类似于集合。
以上的资源都需要释放,释放的是数据库的资源
JDBC应用步骤
1,注册加载一个driver驱动
2,创建数据库连接(Connection)
3,创建一个Statement(发送sql)
4,执行sql语句
5,处理sql结果(select语句)
6,关闭Statement
7,关闭连接Connection。
注意:
6,7两个步骤是必须要做的,因为这些资源是不会自动释放的,必须要自己关闭
访问Oracle的数据库的驱动名字叫o加到环境变量PATH中。
jdbc14.jar,这个jar文件中出访的驱动程序的.class文件
要使用这个驱动程序,要先将他
一,注册加载驱动driver,也就是强制类加载,下面是三种可行的方案:
1、Class.forName(driver);
driver="oracle.jdbc.driver.OracleDriver";
2、Driverd=newDriver类();
Driverd=neworacle.jdbc.driver.OracleDriver();
DriverManager.registerDriver(d);
3、编译时利用虚拟机的系统属性
java–Djdbc.drivers=oracle.jdbc.driver.OracleDriver类名(文件)
Oracle的Driver的全名oracle.jdbc.driver.OracleDriver
mysql的Driver的全名com.mysql.jdbc.Driver
SQLServer的Driver的全名com.microsoft.jdbc.sqlserver.SQLServerDriver
二,创建连接
DriverManager.getConnection(Stringurl,Stringusername,Stringpassword);
Connection连接是通过DriverManager的静态方法getConnection(.....)来得到的,这个方法的实质是把参数传到实际的Driver中的connect()方法中来获得数据库连接的。
Oracle的URL值是由连接数据库的协议和数据库的IP地址及端口号还有要连接的库名(DatebaseName)
OracleURL的格式
jdbc:
oracle:
thin:
(协议)@XXX.XXX.X.XXX:
XXXX(IP地址及端口号):
XXXXXXX(所使用的库名)
例:
jdbc:
oracle:
thin:
@192.168.0.39:
1521:
TARENADB
MySqlURL的写法
例:
jdbc:
mysql:
//192.168.8.21:
3306/test
SQLServerURL的写法
例:
jdbc:
microsoft:
sqlserver:
//192.168.8.21:
1433
java-Djdbc.drivers=驱动全名类名
使用系统属性名,加载驱动-D表示为系统属性赋值
使用Connection对象获得一个Statement,Statement中的executeQuery(Stringsql)方法可以使用select语句查询,并且返回一个结果集ResultSet通过遍历这个结果集,
可以获得select语句的查寻结果,ResultSet的next()方法会操作一个游标从第一条记录的前边开始读取,直到最后一条记录。
executeUpdate(Stringsql)方法用于执行DDL和DML语句,可以update,delete操作。
注意:
要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为Statement和ResultSet是需要连接是才可以使用的,
所以在使用结束之后有可能起他的Statement还需要连接,所以不能现关闭Connection。
作业:
修改StudentDao的设计以及实现和测试程序,来完成从命令行传递学生的信息。
JDBC第二天
一、提问
如何进行代码复用
继承复用、组合复用
私有复用:
一个方法在一个类的内部使用
工具方法:
使用静态方法,使用类名直接调用
二、Statement
execute(sql);当不知道执行的SQL语句是什么类型的时候执行,返回值是boolean
executeQuery(sql);执行查询语句
executeUpdate(sql);执行更新语句
三、PreparedStatement
可以使用参数替代sql语句中的某些参数使用"?
"代替,他先将带参数的sql语句发送到数据库,进行编译,然后PreparedStatement会将参数发送给数据库。
在使用PreparedStatement时,在设置相应参数时,要指明参数的位置和类型,以及给出参数值
根据不同的参数类型使用不同的setXXX(参数的位置,参数值)来设置参数
例:
publicvoidinsert(Students){
Connectioncon=ConnectionFactory.getConnection();//建立连接
Stringsql="insertintostudent(id,name)values(?
?
)";
PreparedStatementps=null;
try{
ps=con.prepareStatement(sql);//创建一个PreparedStatement
intindex=1;
ps.setInt(index++,s.getStuId());//为参数赋值
ps.setString(index++,s.getName());
ps.executeUpdate();
}catch(SQLExceptione){
e.printStackTrace();
}finally{
if(ps!
=null)
try{
ps.close();
}catch(SQLExceptione){
e.printStackTrace();
}
if(con!
=null)
try{
con.close();
}catch(SQLExceptione){
e.printStackTrace();
}
}
}
CallableStatement是可以用非sql语句来访问数据库,他是通过调用存储过程(PL/SQL)来访问数据库的。
可以直接使用连接来调用prepareCall(...)方法,来执行这个存储过程,"..."是存储过程的名字。
对于系统时间要去数据库时间
TimeStamp和Date都可以保存时间
TimeStamp可以保存时、分、秒的数据,Date只保存日期年月的信息。
SQLException是检查异常必须处理要么throws,要么try{}catch(){}
getErrorCode()可以获得错误码,可以对错误进行查询。
四、源数据
JDBC中有两种源数据,一种是数据库源数据,另一种是ResultSet源数据。
源数据就是描述存储用户数据的容器的数据结构。
ResultSetrs=ps.executeQuery();
ResultSetMetaDatam=rs.getMetaData();
getColumnCount(),获得实际列数
getColumnName(intcolnum),获得指定列的列名
getColumnType(intcolnum),获得指定列的数据类型
getColumnTypeName(intcolnum),获得指定列的数据类型名
//打印结果集
publicstaticvoidprintRS(ResultSetrs)throwsSQLException{
ResultSetMetaDatarsmd=rs.getMetaData();
while(rs.next()){
for(inti=1;i<=rsmd.getColumnCount();i++){
StringcolName=rsmd.getColumnName(i);
StringcolValue=rs.getString(i);
if(i>1){
System.out.print(",");
}
System.out.print(name+"="+value);
}
System.out.println();
}
}
五、数据库源数据
DatabaseMetaData
getURL(),获得连接数据库的URL
getDatabaseProductName()获得数据库产品的名称
getDriverVersion()获得JDBC驱动程序的String形式的版本号
getTables()获得数据库中该用户的所有表
getUserName()获得数据库用户名。
六、事务(Transaction)
事务是针对原子操作的,要求原子操作不可再分,要求原子操作必须同时成功同时失败。
事务是捆绑的原子操作的边界。
JDBC中使用事务,先要使用连接调用setAutoCommite(false)方法,把自动提交(commit)置为false。
打开事务就要关闭自动提交。
不用事务时要把setAutoCommite(true)
在处理事务时,在发送sql语句后执行成功并确认时,就在try块中使用连接调用commit()方法来发送提交信息,
在发送sql语句后执行失败时,会在catch语句块中使用连接调用rollback()方法来发送回滚信息,也可以在需要时做回滚操作(主观原因)。
七、JDBC事务并发产生的问题和事务隔离级别
1,脏读(dirtyread),读取到了没有提交的数据。
2,不可重复读(UnPrpeatableRead),两次读取到了不同的数据,就是要保持在同一时间点上两次读取到的数据相同,
不能够使查询数据时进行改变。
3,幻读(phantom),在两次查询同一时间点数据时,数据数量发生改变,要保持在同一时间点上两次读取到的数据相同。
脏读:
就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读:
是指在一个事务内,多次读同一数据。
在这个事务还没有结束时,另外一个事务也访问该同一数据。
那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。
这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻觉读:
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。
同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。
那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
事务隔离级别:
TRANSACTION_NONE不使用事务。
TRANSACTION_READ_UNCOMMITTED可以读取未提交数据。
TRANSACTION_READ_COMMITTED可以避免脏读,不能够读取没提交的数据,最常用的隔离级别大部分数据库的默认隔离级别
TRANSACTION_REPEATABLE_READ可以避免脏读,重复读取,
TRANSACTION_SERIALIZABLE可以避免脏读,重复读取和幻读,(事务串行化)会降低数据库效率
以上的五个事务隔离级别都是在Connection类中定义的静态常量,使用setTransactionIsolation(intlevel)方法可以设置事务隔离级别。
有关事务的隔离级别的介绍:
事务(Transaction)是并发控制的基本单位。
所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
例如,银行转账工作:
从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。
所以,应该把它们看成一个事务。
事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
针对上面的描述可以看出,事务的提出主要是为了解决并发情况下保持数据一致性的问题。
事务具有以下4个基本特征。
●Atomic(原子性):
事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。
●Consistency(一致性):
只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。
●Isolation(隔离性):
事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。
同时,并行事务的修改必须与其他并行事务的修改相互独立。
●Durability(持久性):
事务结束后,事务处理的结果必须能够得到固化。
数据库肯定是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。
●更新丢失(Lostupdate):
两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。
这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
●脏读取(DirtyReads):
一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。
这是相当危险的,因为很可能所有的操作都被回滚。
●不可重复读取(Non-repeatableReads):
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。
●两次更新问题(Secondlostupdatesproblem):
无法重复读取的特例。
有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。
这就会造成第一次写操作失效。
●虚读(PhantomReads):
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。
这是因为在两次查询过程中有另外一个事务插入数据造成的。
数据库的隔离级别
为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
●未授权读取(ReadUncommitted):
允许脏读取,但不允许更新丢失。
如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。
该隔离级别可以通过“排他写锁”实现。
●授权读取(ReadCommitted):
允许不可重复读取,但不允许脏读取。
这可以通过“瞬间共享读锁”和“排他写锁”实现。
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
●可重复读取(RepeatableRead):
禁止不可重复读取和脏读取,但是有时可能出现幻影数据。
这可以通过“共享读锁”和“排他写锁”实现。
读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
●序列化(Serializable):
提供严格的事务隔离。
它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为ReadCommitted,它能够避免脏读取,而且具有较好的并发性能。
尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
通过前面的介绍已经知道,通过选用不同的隔离等级就可以在不同程度上避免前面所提及的在事务处理中所面临的各种问题。
所以,数据库隔离级别的选取就显得尤为重要,在选取数据库的隔离级别时,应该注意以下几个处理的原则:
首先,必须排除“未授权读取”,因为在多个事务之间使用它将会是非常危险的。
事务的回滚操作或失败将会影响到其他并发事务。
第一个事务的回滚将会完全将其他事务的操作清除,甚至使数据库处在一个不一致的状态。
很可能一个已回滚为结束的事务对数据的修改最后却修改提交了,因为“未授权读取”允许其他事务读取数据,最后整个错误状态在其他事务之间传播开来。
其次,绝大部分应用都无须使用“序列化”隔离(一般来说,读取幻影数据并不是一个问题),此隔离级别也难以测量。
目前使用序列化隔离的应用中,一般都使用悲观锁,这样强行使所有事务都序列化执行。
剩下的也就是在“授权读取”和“可重复读取”之间选择了。
我们先考虑可重复读取。
如果所有的数据访问都是在统一的原子数据库事务中,此隔离级别将消除一个事务在另外一个并发事务过程中覆盖数据的可能性(第二个事务更新丢失问题)。
这是一个非常重要的问题,但是使用可重复读取并不是解决问题的唯一途径。
假设使用了“版本数据”,Hibernate会自动使用版本数据。
Hibernate的一级Session缓存和版本数据已经为你提供了“可重复读取隔离”绝大部分的特性。
特别是,版本数据可以防止二次更新丢失的问题,一级Session缓存可以保证持久载入数据的状态与其他事务对数据的修改隔离开来,因此如果使用对所有的数据库事务采用授权读取隔离和版本数据是行得通的。
“可重复读取”为数据库查询提供了更好的效率(仅对那些长时间的数据库事务),但是由于幻影读取依然存在,因此没必要使用它(对于Web应用来说,一般也很少在一个数据库事务中对同一个表查询两次)。
也可以同时考虑选择使用Hibernate的二级缓存,它可以如同底层的数据库事务一样提供相同的事务隔离,但是它可能弱化隔离。
假如在二级缓存大量使用缓存并发策略,它并不提供重复读取语义(例如,后面章节中将要讨论的读写,特别是非严格读写),很容易可以选择默认的隔离级别:
因为无论如何都无法实现“可重复读取”,因此就更没有必要拖慢数据库了。
另一方面,可能对关键类不采用二级缓存,或者采用一个完全的事务缓存,提供“可重复读取隔离”。
那么在业务中需要使用到“可重复读取”吗?
如果你喜欢,当然可以那样做,但更多的时候并没有必要花费这个代价。
八,异常的处理
try{}
catch(SQLException){}
try{}
catch(Exception){}
作业:
自己写一个Sqlplus应用
JDBC第三天
一、JDBC2.0的新特性
可滚动结果集(可双向滚动)
批处理更新***
可更新结果集
1、批处理更新
Statement
addBatch(Stringsql),方法会在批处理缓存中加入一条sql语句
executeBatch(),执行批处理缓存中的所有sql语句。
PreparedStatement
addBatch()将一组参数添加到此PreparedStatement对象的批处理命令中。
executeBatch()将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
PreparedStatement中使用批量更新时,要先设置好参数后使用addBatch()方法加入缓存。
注意:
批量更新中只能使用更新或插入语句
execute(Stringsql),这个方法的返回值是boolean类型,如果返回true就表示sql是一个select语句,可以通过getResultSet()获得结果集,如果是false,sql就是DML语句或者是DDL语句,可以通过getUp