转自:http://blog.163.com/lzh_7@126/blog/static/3234589820123422646210/
JDBC(Java Database Connectivity)
JDBC(Java Database Connectivity)
jdbc调用存储过程
1、jdbc中对于执行存储过程支持是通过CallableStatement接口来实现的,该接口的实现类实例来调用并执行存储过程。“public interface CallableStatementextends PreparedStatement”。Connection类提供了创建CallableStatement对象的方法以调用数据库存储过程。//CallableStatement prepareCall(String sql) throws SQLException。//注意:调用存储过的字符串参数格式为形如:"{call p_Register(?,?,?)}",1、注意大括号,2、注意使用call来调用存储过程,3、存储过程参数使用括号括起来,4、同样支持占位符"?",5、语句后没有分号。
2、CallableStatement接口是PreparedStatement的子接口,PreparedStatement又是Statement接口的子接口。
//加载驱动(Load Driver),DriverManager负责管理所有可加载的驱动,并且有它获取合适的Connection对象,然后在Connection对象上创建关联的Statement或者PreparedStatement或者CallableStatement实现类的对象,然后执行Statement中的SQL语句。最后处理执行结果,或者查询得到ResultSet,或者Update得到受影响的行数。
22:49 2012/3/31
为了使Java编程的程序不依赖于具体的数据库,Java提供了专门用于数据库的API,即JDBC。//这又是Java夸数据库库平台的本质原因。JDBC中基本上定义的都是接口,具体的实现都留给了各个数据库厂商,比如:Oracle、MySQL、SQLServer、DB2等,像Oracle中提供的classes12.jar就是对JDBC中接口的具体实现。Sun公司JDK中自带Odbc Driver。//所以使用Odbc连接其他数据库时,不再需要第三方的jar包,因为JDK中本身就提供了jar包。
//sun提供的Odbc相关的class文件都在sun.jdbc.odbc包下面,其中Driver完全限定名为“sun.jdbc.odbc.JdbcOdbcDriver”。
(一)连接数据库
应用程序为了和数据库进行交互信息,首先必须和数据库建立连接。
在Java中和数据库建立连接通常有两种方式:1、加载纯Java数据库驱动程序。2、建立JDBC-ODBC桥接器。///使用JDBC的应用程序无论是采用哪种方式连接数据库,都不会影响操作数据库的逻辑代码,这有利于代码的维护和升级。//也就是说在JDBC中写的各种SQL业务在从一种数据库换成另一种数据库时,都可以不用重写代码。
如果采用纯Java数据库驱动程序连接数据库就需要得到数据库厂商提供的纯Java数据库驱动程序。//一般情况下都是各个数据库厂商提供Java数据库驱动程序,以访问他们自己的数据库。
使用JDBC-ODBC桥接方式的机制是:应用程序之需建立JDBC和ODBC之间的连接,即所谓的建立JDBC-ODBC桥接器,而和数据库的连接由ODBC去完成。//细想,ODBC也应该是一套访问数据库的接口,它也应该有一个DriverManager来管理不同的数据源,建立和不同的数据源连接。//这些Driver又是由谁来提供呢,它们又是用什么语言来实现的呢?
Windows系统中原生自带ODBC数据源管理器,通过该工具可以不同数据库的ODBC数据源,在创建数据源时,要针对不同数据库选择不同驱动程序。
查询操作(SELECT)
JDBC和数据库表进行交互的主要方式是使用SQL语句,JDBC提供的API可以将标准的SQL语句发送给数据库,从而实现和数据库交互。
在特定的连接(Connection)对象上创建语句(Statement)对象,1、Statement conn.createStatement(); 2、创建预处理语句PreparedStatement conn.prepareStatement(String sql); 3、创建调用存储过程语句 CallableStatement conn.prepareCall(String sql);
语句对象调用方法执行查询,或得结果集(ResultSet对象) st.executeQuery(String sql)。///注意:1、该方法返回的是单结果集,2、结果集对象由统一形式的列组织的数据行组成。在SQL中查询到的结果集有几列,对应着JDBC中ResultSet内存中对象就有几列,并且其列的顺序都是和DB查询到的结果列顺序一致,所以在通过列索引rs.getXXX(int columnIndex)时获取字段值时,一定要清楚DB中查询出的结果是怎样一个组织。
//一般情况下,在不清楚列索引对应哪个列时,就通过列名来取字段值,rs.getXXX(String columnName);//ADO.NET也同样采用这两种形式来取字段值。
顺序查询
//在ADO.NET中通过DataSet可以存储多个表,并且可以通过DataTable的Count属性直接获知返回的结果中的记录行数,然后很方便的遍历结果。不知道JDBC怎么获知多个ResultSet呢?
//而JDBC中想要获知一个表中的总列数,也可以通过连接对象获取DatabaseMetaData对象,然后调用getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern),一般是只传tableNamePattern(表名),其余传null即可将表中字段信息以行的形式存储在一个ResultSet中,再通过rs.next()和一个count++即可求出表中所有字段个数。
//不过,我在实际中发现,Oracle的驱动实现中rs.next()一开始就是false。
sun JDK中的Odbc驱动倒是正常实现了的。
//注意:一个连接对象获得的DatabaseMetaData中存储了该连接对应的数据库所有信息。DatabaseMetaData meta = conn.getMetaData();
控制游标
如果要在结果集中前后移动、显示结果集中某条记录、或随机显示若干条记录,这时候需要连接对象返回一个可滚动的结果集对象。
//通过游标可以用Java代码来实现“分页”功能,DB中可以实现“分页”,前端JS也可以实现“分页”。
为了得到一个可滚动的结果集,需要使用下述方法来得到一个Statement对象。
st = conn.createStatement(int resultSetType, int resultSetConcurrency) ;
根据resultSetType、resultSetConcurrency取值情况返回相应类型的结果集。
resultSetType取值决定滚动方式
1、ResultSet.TYPE_FORWORD_ONLY;结果集的游标只能向前滚动。//我猜conn调用createStatement()方法内部就是直接穿的这个参数。
2、ResultSet.TYPE_SCROLL_INSENSITIVE;结果集游标可以上下移动,当数据库变化时,结果集不变化。
3、ResultSet.TYPE_SCROLL_SENSITIVE;结果集游标可以上下移动,当数据库变化时,结果集同步改变。//结果集是敏感的,当前结果集会和数据中表同步信息。
resultSetConcurrency取值决定是否用结果集更新数据库
1、ResultSet.CONCUR_READ_ONLY;不用结果集更新数据库中的表。
2、ResultSet.CONCUR_UPDATABLE;能用结果集更新数据库中的表。
对于滚动查询一般会使用到ResultSet中一下方法
1、public boolean previous();将游标向前移动,当移动到结果集第一行之前时返回false。//该方法恰好和next()方法相反。
2、public void beforeFirst();//将游标移到结果集的初始位置,即在第一行之前。
3、public boolean isBeforeFirst();判断游标是否在第一行之前
4、public void afterLast();//将游标移到结果集最后一行之后。
5、public boolean isAfterLast();判断游标是否在最后一行之后。
6、public boolean first();将游标移到结果集的第一行。
7、public boolean isFirst();判断游标是否指向结果集的第一行。
8、public boolean last();将游标移到结果集的最后最后一行。
9、public boolean isLast();判断游标是否指向结果集的最后一行。
10、public int getRow();//得到当前游标所指的行号,行号从1开始,如果结果集没有行,则返回0。
//注意:oracle classes12.jar驱动中对于rs当前游标在第一行之前,和最后一行之后再调用getRow()都是返回0。
11、public boolean absolute(int row);将游标移到参数指定的行。///如果row取负数,就是倒数的行数,absolute(-1)表示移到倒数一行(即最后一行),absolute(-2)表示移到倒数第2行。当移到第一行前面或者最后一行后面时,就返回false。
条件查询
//对于前端按照组合条件实现查询,1、可以用Java代码来控制组合的查询SQL语句,然后再将SQL语句传到DB中去, 2、另一种方式是用DB来判断传入的参数,根据判断结果来实现组合查询。(一般是用存储过程来实现)
//Java代码中一般采用的方法都是用查询字段是否为空来决定拼接SQL语句。
//DB中存储过程中一般也是采用IS NOT NULL来判断查询字段是否为空。
排序查询(ORDER BY)、模糊查询(LIKE 加通配符"%"或者"_"或者"[]")
更新、添加与删除操作
Statement对象调用方法public int executeUpdate(String sql);//该方法操作SQL中UPDATE、INSERT、DELETE操作。
使用预处理语句(PreparedStatement)
当向数据库中发送一个SQL语句时,数据库中SQL解释器负责将SQL语句生成底层内部命令,然后执行该命令,完成有关数据操作。
如果不断的向数据库提交SQL语句势必增加数据库中SQL解释器的负担,影响执行速度。如果应用程序针对连接的数据库,实现将SQL语句解释为数据库底层内部命令,然后让数据库去执行这个命令,显然减轻数据库的负担,并且提高访问数据库的速度。
//如果使用的非预处理语句,那么频繁的发送SQL语句给DB,就会造成DB中的SQL解释器频繁的解析传来的SQL语句,造成DB负担,影响效率。
连接对象conn调用prepareStatement(String sql)即可对指定的SQL语句进行预编译处理,生成数据库底层内部名命令。
注意:JDBC中列索引都是从1开始。
对于SQL进行预处理时,可以使用通配符“?”来代替字段的值,只要在预处理语句执行之前设置好占位符"?"的具体值即可。
事务(Transaction)
事务是由一组SQL语句组成,所谓事务处理是指应用程序保证事务中的SQL语句要么全部执行,要么一个都不执行。
默认下,通过连接对象获得的Statement对象向数据库提交一个SQL语句都会立刻生效,也就是在数据库中是自动提交的。//如:PL/SQL DEV中点击完"execute"后,在紧接着点击"commit".
可以通过Connection对象调用setAutoCommit(boolean autoCommit);传入一个false参数来改变默认自动提交这个设置。
这样一来,一定要记住对“连接对象”手动调用commit()方法提交到数据库中。
“连接对象”调用rollback()方法回滚事务。
批处理(Batch)
Statement对象先调用调用addBatch(String sql),然后再调用executeBatch()方法就可以执行一批SQL语句,返回一个int[]数组,它表示总共受影响的记录数。
//今天晚上搞了一晚上Oracle Driver中对于SQL语句是严格限制不能加分号结尾的这个问题,加分号就报“ORA-00911: 无效字符”错误,只知道说是会自动添加分号,但是不知道真正原因是什么?
//实际在JDBC-ODBC时,SQL语句中添加分号或者不添加分号都能成功执行,这说明不同数据库厂商的驱动程序在实现细节上有细节上的严格区别。
//实际测试中Sun Odbc Driver对于SQL语句末尾分号不敏感,可加可不加.
0:37 2012/4/3
//实际测试中:Oracle Driver中批处理不能执行SELECT语句,一般只支持INSERT、UPDATE、DELETE语句。
//注意:Oracle Driver中对于可能返回多个结果集的Statement对象在执行execute时,不能通过这种形式的SQL字符串"SELECT * FROM Departments; SELECT Usr_Name,Usr_Pwd,Usr_Mail FROM User_Table"//注意:SQL字符串中一个分号都不能出现,不要以为只要最后不以分号";"结束就会OK,实际上只要出现分号";"就会报"ORA-00911: 无效字符"错误。
13:42 2012/4/3
CachedRowSetImpl
(带缓存的行集实现类)
//MS的ADO.NET中架构思想原生就是这种,不知道是不是源自Sun Jdbc中此处的思想呢?将访问数据库和处理(或者)展现数据分离开来,即实现离线访问数据库。
JDBC使用ResultSet对象装载SQL语句处理后的结果集时,必须时ResultSet对象和数据库连接对象实现了紧密的绑定,而且要求是连接对象是实时和数据库保持连接的,一旦连接对象被关闭,ResultSet对象中数据就立刻消失。///这就意味着,应用程序在使用ResultSet对象中的数据时(获取里面的数据、新增、修改、删除),就必须保持和数据库的连接,直到使用完ResultSet为止。
///我们知道每种数据库在同一时刻都有允许的最大连接数目(连接本身就算是一种资源),因此当多个应用程序连接访问数据库时,应避免长时间占用数据库的连接资源。
com.sun.rowset.CachedRowSetImpl该类实现了CachedRowSet接口,CachedRowSetImpl对象可以保存ResultSet对象中数据,而且CachedRowSetImpl对象不依赖Connection对象,这意味着一旦把ResultSet对象中的数据保存到CachedRowSetImpl对象中后,就可以关闭和数据库的连接。
CachedRowSetImpl类同时也实现了ResultSet接口,因此可以像操作ResultSet对象一样来操作CachedRowSetImpl对象。///将ResultSet对象中的数据保存到CachedRowSetImpl对象中的方法为:CachedRowSetImpl crs = new CachedRowSetImpl(); crs.populate(rs);CachedRowSetImpl对象调用populate方法即可。
13:15 2012/4/4
《Java程序设计精编教程》