JDBC
(Java Database Connectivity)
一、概述:
JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。JDBC的实现由数据库厂商以驱动程序的形式提供。JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
ODBC:基于C语言的数据库访问接口。
JDBC也就是Java版的ODBC。
JDBC的特性:高度的一致性、简单性(常用的接口只有4、5个)。
1.在JDBC中包括了两个包:java.sql和javax.sql。
① java.sql 基本功能。这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。
② javax.sql 扩展功能。它主要为数据库方面的高级操作提供了接口和类。如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。
注:除了标出的Class,其它均为接口。
API | 说明 |
java.sql.Connection | 与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。 |
java.sql.Driver | 每个驱动程序类必需实现的接口,同时,每个数据库驱动程序都应该提供一个实现Driver接口的类。 |
java.sql.DriverManager (Class) | 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。 |
java.sql.Statement | 用于执行静态SQL语句并返回其生成结果的对象。 |
java.sql.PreparedStatement | 继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。 |
java.sql.CallableStatement | 用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。 |
java.sql.ResultSet | 指的是查询返回的数据库结果集。 |
java.sql.ResultSetMetaData | 可用于获取关于ResultSet对象中列的类型和属性信息的对象。 |
2、驱动程序按照工作方式分为四类:
1、 JDBC-ODBC bridge + ODBC 驱动
JDBC-ODBC bridge桥驱动将JDBC调用翻译成ODBC调用,再由ODBC驱动翻译成访问数据库命令。
优点:可以利用现存的ODBC数据源来访问数据库。
缺点:从效率和安全性的角度来说的比较差。不适合用于实际项目。
2、 基于本地API的部分Java驱动
我们应用程序通过本地协议跟数据库打交道。然后将数据库执行的结果通过驱动程序中的Java部分返回给客户端程序。
优点:效率较高。
缺点:安全性较差。
3、 纯Java的网络驱动
(中间协议) (本地协议)
App à JDBC à 纯Java à 中间服务器 à DB
缺点:两段通信,效率比较差
优点:安全信较好
4、 纯Java本地协议:通过本地协议用纯Java直接访问数据库。
二、JDBC编程的步骤
① 注册一个driver ojdbc.jar
注册驱动程序有三种方式:
方式一:Class.forName(“oracle.jdbc.driver.OracleDriver”);
JAVA规范中明确规定:所有的驱动程序必须在静态初始化代码块中将驱动注册到驱动程序管理器中。
方式二:Driver drv = new oracle.jdbc.driver.OracleDriver();
DriverManager.registerDriver(drv);
方式三:编译时在虚拟机中加载驱动
javac –Djdbc.drivers = oracle.jdbc.driver.OracleDriver xxx.java
java –D jdbc.drivers=驱动全名 类名
使用系统属性名,加载驱动 -D表示为系统属性赋值
附 : mysql的Driver的全名com.mysql.jdbc.Driver
SQLServer的Driver的全名com.microsoft.jdbc.sqlserver.SQLServerDriver
② 建立连接
conn=DriverManager.getConnection(“jdbc:oracle:thin:@192.168.0.20:1521:tarena”,”User”,” Pasword”);
Oracle子协议 , IP地址及端口号和数据库实例名 , 用户名,密码 ;
Connection连接是通过DriverManager的静态方法getConnection(.....)来得到的,这个方法的实质是把参数传到实际的Driver中的connect()方法中来获得数据库连接的。
Oracle URL的格式:
jdbc:oracle:thin:(协议)@XXX.XXX.X.XXX:XXXX(IP地址及端口号):XXXXXXX(所使用的库名)
MySql URL的写法例: jdbc:mysql://192.168.8.21:3306/test
③ 获得一个Statement对象
sta = conn.createStatement();
④ 通过Statement执行Sql语句
sta.executeQuery(String sql);返回一个查询结果集。
sta.executeUpdate(String sql);返回值为int型,表示影响记录的条数。
将sql语句通过连接发送到数据库中执行,以实现对数据库的操作。
⑤ 处理结果集
使用Connection对象获得一个Statement,Statement中的executeQuery(String sql) 方法可以使用select语句查询,并且返回一个结果集 ResultSet通过遍历这个结果集,可以获得select语句的查寻结果,ResultSet的next()方法会操作一个游标从第一条记录的前面开始读取,直到最后一条记录。executeUpdate(String sql) 方法用于执行DDL和DML语句,比如可以update,delete操作。
只有执行select语句才有结果集返回。
例: Statement str=con.createStatement(); //创建Statement
Stringsql=”insert into test(id,name) values(1,”+”’”+”test”+”’”+”)”;
str. executeUpdate(sql);//执行Sql语句
String sql=”select * from test”;
ResultSet rs=str. executeQuery(String sql);//执行Sql语句,执行select语句后有结果集
//遍历处理结果集信息
while(rs.next()){
System.out.println(rs.getInt(“id”));
System.out.println(rs.getString(“name”))
}
⑥ 关闭数据库连接(释放资源) 调用.close()
rs.close(); sta.close(); con.close();
ResultSet Statement Connection是依次依赖的。
注意:要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为Statement和ResultSet是需要连接时才可以使用的,所以在使用结束之后有可能其它的Statement还需要连接,所以不能现关闭Connection。
图形演绎编写JDBC程序的一般过程:
三、几个重要接口:
(1) Statement —— SQL语句执行接口
Statement接口代表了一个数据库的状态,在向数据库发送相应的SQL语句时,都需要创建Statement接口或者PreparedStatement接口。在具体应用中,Statement主要用于操作不带参数(可以直接运行)的SQL语句,比如删除语句、添加或更新。
(2) PreparedStatement:预编译的Statement
第一步:通过连接获得PreparedStatement对象,用带占位符(?)的sql语句构造。
PreparedStatement pstm = con.prepareStatement(“select * from test where id=?”);
第二步:设置参数
pstm.setString(1,“ganbin”);
第三步:执行sql语句
Rs = pstm.excuteQuery();
statement发送完整的Sql语句到数据库不是直接执行而是由数据库先编译,再运行。而PreparedStatement是先发送带参数的Sql语句,再发送一组参数值。如果是同构的sql语句,PreparedStatement的效率要比statement高。而对于异构的sql则两者效率差不多。
同构:两个Sql语句可编译部分是相同的,只有参数值不同。
异构:整个sql语句的格式是不同的
注意点:1、使用预编译的Statement编译多条Sql语句一次执行
2、可以跨数据库使用,编写通用程序
3、能用预编译时尽量用预编译
(3) ResultSet —— 结果集操作接口
ResultSet接口是查询结果集接口,它对返回的结果集进行处理。ResultSet是程序员进行JDBC操作的必需接口。
(4) ResultSetMetaData —— 元数据操作接口
ResultSetMetaData是对元数据进行操作的接口,可以实现很多高级功能。Hibernate运行数据库的操作,大部分都是通过此接口。可以认为,此接口是SQL查询语言的一种反射机制。ResultSetMetaData接口可以通过数组的形式,遍历数据库的各个字段的属性,对于我们开发者来说,此机制的意义重大。
JDBC通过元数据(MetaData)来获得具体的表的相关信息,例如,可以查询数据库中有哪些表,表有哪些字段,以及字段的属性等。MetaData中通过一系列getXXX将这些信息返回给我们。
MetaData包括: 数据库元数据 Database MetaData 使用connection.getMetaData()获得
包含了关于数据库整体元数据信息。
结果集元数据 Result Set MetaData 使用resultSet.getMetaData()获得
比较重要的是获得表的列名、列数等信息。
结果集元数据对象:ResultSetMetaData meta = rs.getMetaData();
字段个数:meta.getColomnCount();
字段名字:meta.getColumnName();
字段JDBC类型:meta.getColumnType();
字段数据库类型:meta.getColumnTypeName();
数据库元数据对象:DatabaseMetaData dbmd = con.getMetaData();
数据库名=dbmd.getDatabaseProductName();
数据库版本号=dbmd.getDatabaseProductVersion();
数据库驱动名=dbmd.getDriverName();
数据库驱动版本号=dbmd.getDriverVersion();
数据库Url=dbmd.getURL();
该连接的登陆名=dbmd.getUserName();
四、JDBC异常处理:
JDBC中,和异常相关的两个类是SQLException和SQLWarning。
1. SQLException类:用来处理较为严重的异常情况。
比如:① 传输的SQL语句语法的错误;
② JDBC程序连接断开;
③ SQL语句中使用了错误的函数。
SQLException提供以下方法:
getNextException() —— 用来返回异常栈中的下一个相关异常;
getErrorCode() —— 用来返回代表异常的整数代码 (error code);
getMessage() —— 用来返回异常的描述信息 (error message)。
2. SQLWarning类:用来处理不太严重的异常情况,也就是一些警告性的异常。其提供的方法和使用与SQLException基本相似。
结合异常的两种处理方式,明确何时采用哪种。
A. throws 处理不了,或者要让调用者知道,就throws;
B. try … catch 能自行处理,就进行异常处理。
五、JDBC中使用Transaction编程(事务编程)
1. 事务是具备以下特征(ACID)的工作单元:
原子性(Atomicity)—— 如果因故障而中断,则所有结果均被撤消;
一致性(Consistency)—— 事务的结果保留不变;
孤立性(Isolation)—— 中间状态对其它事务是不可见的;
持久性(Durability)—— 已完成的事务结果上持久的。
原子操作,也就是不可分割的操作,必须一起成功一起失败。
2. 事务处理三步曲:(事务是一个边界)
① connection.setAutoCommit(false); //把自动提交关闭
② 正常的DB操作 //若有一条SQL语句失败了,自动回滚
③ connection.commit() //主动提交
或 connection.rollback() //主动回滚
完整的代码片段:
try{
con.setAutoCommit(false); //step① 把自动提交关闭
Statement stm = con.createStatement();
stm.executeUpdate("insert into person(id, name, age) values(520, 'X-Man', 18)");
stm.executeUpdate("insert into Person(id, name, age) values(521, 'Super', 19)");
//step② 正常的DB操作
con.commit(); //step③ 成功主动提交
} catch(SQLException e){
try{
con.rollback();
} catch(Exception e){ e.printStackTrace(); } //step③ 失败则主动回滚
}
3. JDBC事务并发产生的问题和事务隔离级别
JDBC事务并发产生的问题:
① 脏读(Dirty Reads) 一个事务读取了另一个并行事务还未提交的数据。
② 不可重复读(UnPrpeatable Read) 一个事务再次读取之前的数据时,得到的数据不一致,被另一个已提交的事务修改。
③ 幻读(Phantom Read) 一个事务重新执行一个查询,返回的记录中包含了因为其它最近提交的事务而产生的新记录。
为了避免以上三种情况的出现,则采用
事务隔离级别:
TRANSACTION_NONE | 不使用事务 |
TRANSACTION_READ_UNCOMMITTED | 可以读取未提交数据 |
TRANSACTION_READ_COMMITTED | 可以避免脏读,不能够读取没提交的数据,最常用的隔离级别 大部分数据库的默认隔离级别 |
TRANSACTION_REPEATABLE_READ | 可以避免脏读,重复读取 |
TRANSACTION_SERIALIZABLE | 可以避免脏读,重复读取和幻读,(事务串行化)会降低数据库效率 |
以上的五个事务隔离级别都是在Connection类中定义的静态常量,使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
比如:con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
六、JavaBean的定义:
1、 是一个普通的Java类
2、 在结构上没有预先的规定,不需要容器,不需要继承类或实现接口
3、 要求必须放在包中,要求实现Serializable接口
4、 要求有一个无参的构造方法.
5、 属性的类型必须保持唯一,返回值必须和set方法参数类型一致
6、 对每个属性要有对应的get和set方法。注:隐藏属性可以没有
7、 可以有外观作为显示控制,事件机制。
七、JDBC2.0新特性:
1、Scrollability 结果集可滚动
滚动:可双向支持绝对与相对滚动,对结果集可进行多次迭代。
Con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
TYPE_FORWARD_ONLY:
该常量指示指针只能向前移动的 ResultSet 对象的类型。
TYPE_SCROLL_SENSITIVE:
该常量指示可滚动并且通常受其他的更改影响的 ResultSet 对象的类型。
CONCUR_UPDATABLE:
该常量指示可以更新的 ResultSet 对象的并发模式。
绝对定位:boolean absolute(int row)将游标移动到指定位置。
相对定位:void afterLast()将游标向后移动一位。
void beforeFirst()。将游标向前移动一位。
boolean first()将游标移动到结果集最前
boolean last()将游标移动到结果集末尾。
2、Updatability 结果集可更新。(主要应用于桌面应用)
更新:rs.updateString(“name”,”Tony”);
rs.updateInt(1,”122323”);修改
rs.deleteRow();删除
rs.updateRow();
注:只有在必要的时候(如桌面应用)才用结果集更新数据库,因为使用结果集更新数据库效率低下。可更新结果集还要看数据库驱动程序是否支持,如Oracle就支持MySql不支持。并且只能针对一张表做结果集更新。而且不能有join操作。必须有主健,必须把非空没有默认值的字段查出。处理可更新结果级时不能用select *来执行查询语句,必须指出具体要查询的字段。
3、Batch updates 可批量更新。
将一组对数据库的更新操作发送到数据库统一执行(数据库支持并发执行操作),以提高效率。主要是通过减少数据(Sql语句或参数)在网络上传输的次数来节省时间。
(1)对于Statement的批量更新处理:
stm.addBatch(Sql);
stm.addBatch(Sql);
int[] results=stm.executeBatch();
(2)对于PreparedStatement的批量跟新处理
pstm.setInt(1,11);pstm.setString(2,”haha”);……..
pstm.addBatch()
pstm.setInt(1,12);pstm.setString(2,”gaga”);……..
pstm.addBatch()
int[] results=stm.executeBatch();
注:int[] 中每一个数表示该Sql语句影响到的记录条数。
PreparedStatement的更新操作比Statement的更新操作多了一个设置参数的过
程。
八、SQL3.0规范中的新类型
Array 数组类型,主要用于保存一些类似于数组结构的数据。
Sturct 结构
Blob,大的二进制数据文件,最多存储2G。
Clob,大文本文件对象,最多存储2G。
在使用上述大对象的时候,在使用JDBC插入记录时要先插入一个空的占位对象,然后使用
select blobdata from t_blob where id = " + id + " for update 这样的语法来对获得的大对象,进行实际的写入操作 Blod通过getBinaryOutputStream()方法获取流进行写入。getBinaryStream()方法获得流来获取Blob中存储的数据。
Clob的操作也和、Blob相同。getAsciiStream() 方法用于读取存储的文本对象,getAsciiOutputStream()方法之获得流用来向文件对象写入的。
BLOB与CLOB的异同点:
① 都可以存储大量超长的数据;
② BLOB (Binary Large Object) 以二进制格式保存于数据库中,特别适合保存图片、视频文件、音频文件、程序文件等;
③ CLOB (Character Large Object) 以Character格式保存于数据库中,适合保存比较长的文本文件。
九、SQL数据类型及其相应的Java数据类型
十、面向对象的数据库设计
类的关联,继承在数据库中的体现:
类定义―――>表定义
类属性―――>表字段
类关系―――>表关系
对 象―――>表记录
注: Oid(对象id)―――>业务无关
在数据库中每一条记录都对应一个唯一的id;
Id通常是用来表示记录的唯一性的,通常会使用业务无关的数字类型
字段的个数不会影响数据库的性能,表则越多性能越低。
(一)类继承关系对应表,
1、为每一个类建一张表。通过父类的Oid来体现继承关系。
特点:在子类表中引用父类表的主建作为自己的外建。
优点:方便查询。属性没有冗余。支持多态。
缺点:表多,读写效率低。生成报表比较麻烦。
2、为每一个具体实现类建一个表
特点:父类的属性被分配到每一个子类表中。
优点:报表比较容易
缺点:如果父类发生改变会引起所有子了表随之更改。
并且不支持多态。数据有冗余。
3、所有的类在一张表中体现,加一个类型辨别字段
特点:效率高,查询不方便,用于重复字段不多时。
优点:支持多态,生成报表很简单。
缺点:如果任何一个类发生变化,必须改表。字段多,难以维护。
(二)类关联关系对应表
1,一对一关联,类关系对应成表时有两种做法:
一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。
二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。
2,一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。
3,多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。
十一、JDBC2.0扩展
(一)JNDI(命名路径服务):
定义:是Java的命名和路径服务接口。而JDBC是Java的数据库访问接口。
跟JDBC是平级的关系,是两个独立的JNDI;JDBC存储的数据都是以二维表的接口来大规模存储数据。而JNDI存储的是差异性比较大的Java对象。JDBC取数据时用Sql语言访问数据。JDBC API依赖于驱动程序,而JNDI依赖于服务提供者。JDBC一般把数据存储到关系型数据库,而JNDI一般把数据存储到小型数据库、文件、甚至是注册表中。
(JNDI的方法是在javax.naming包下)
bind(String name, Object obj) 将名称绑定到对象资源,建立指定的字符串和对象资源的关联
lookup(String name) ,通过指定的字符串获得先前绑定的资源
以下是将资源和JNDI命名绑定的方法
//1.连接jndi服务器
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put (Context.PROVIDER_URL, "file:c:/jdbc");
ctx = new InitialContext(env);
//2.造一个oracle数据源对象
OracleDataSource ods = new OracleDataSource();
ods.setDriverType("thin");
ods.setServerName("192.168.0.20");
ods.setNetworkProtocol("tcp");
ods.setDatabaseName("tarena");
ods.setPortNumber(1521);
ods.setUser("openlab");
ods.setPassword("open123");
//3.将数据源对象存入jndi服务器
ctx.bind("ora20",ods);
}catch(Exception e){
e.printStackTrace();
}
}
(二)DataSourse(数据源)
定义:
1、包含了连接数据库所需的信息,可以通过数据源获得数据库连接,有时由于某些连接数据库的信息会变更,所以经常使用包含数据库连接信息的数据源。
2、一个标准的数据库连接工厂,保存与数据库相关的信息,可以将数据库的连接信息放在一个共享的空间进行提取,不用在本地安装。支持JNDI的绑定,支持连接池,支持分布式服务,用getConnection方法可获得与数据库的连接。数据源应该由管理员创建(目的是为了保证数据库的安全)。所以数据源对象一般放在JNDI服务器中。
通过JNDI获得绑定的资源
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put (Context.PROVIDER_URL, "file:c:/jdbc");
ctx = new InitialContext(env);
DataSource ds = (DataSource)ctx.lookup("ora20");
con = ds.getConnection();
(三)连接池:
在内存中用来保存一个个数据库连接的对象。
访问数据库时,建立连接和拆连接需要花费较长时间,通过以连接池直连的方式获取连接,不需要注册驱动程序,可以大量的节省销毁和创建连接的资源消耗提高访问数据库的效率。
注:通过连接池获得的Connection,当执行con.close()时,不是关闭连接,而是表示将连接释放回连接池。连接池是一个很复杂的软件,所以是由服务器厂商实现。
(四)分布式的事务管理器JTA
分布式事务是通过多个异地数据库执行一组相关的操作,要保证原子操作的不可分,也不用再自己写commit,和rollback,全部都交给中间服务器(TM)来处理。(两阶段提交),也 就是在中间服务器发送sql语句等待数据库回应,都回应操作成功才提交,否则同时回滚。
a数据库:T1 提交 ,--> 转帐--> b数据库:T2回滚;没有办法控制
将T1和T2合起来作为一个事务;
必须有第三方;weblogic,websphere 等应用服务器;
T1交给应用服务器,T2也交给应用服务器,每一个监控都有资源管理器;
所有资源管理器都让提交才能提交;
分布式的事务都是通过声明的配置进行的;
针对多个不同数据库同时操作,要保证原子操作的不可分割,
也不用再自己写commit,和rollback,全部都交给中间服务器来处理。
(两阶段提交),也就是在中间服务器发送sql语句等待数据库回应,
都回应操作成功才提交,否则同时回滚。
除非特别大的公司,一般不用分布式事务;
(五)RowSet
行集,这是一个JavaBean(事件机制),它增强了ResultSet的功能,包装了Connection、Statement、ResultSet、DriverManage。通过RowSet可以获得数据源,设置隔离级别,也可以发送查寻语句,也实现了离线的操作遍历,RowSet也支持预编译的Statement。
RowSet中的方法大致上和ResultSet相同,当需要使用时请查阅JAVA API参考文档。
缺点:
不够灵活, 行集不属于jdbc的主流技术,ojdbc14 没有它的实现类;
例子1:
CachedRowSetImpl crs=null;
try{
//1.注册驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//2. 建立数据库连接并执行sql语句
crs = new CachedRowSetImpl();
crs.setUrl("jdbc:oracle:thin:@192.168.0.201:1521:tarena");
crs.setUsername("openlab");
crs.setPassword("open123");
crs.setCommand("select * from ln_student");
crs.execute();
//3.更新操作
crs.moveToInsertRow();
crs.updateInt("id", 99988878);
crs.updateString("name", "tony");
crs.insertRow();
crs.moveToCurrentRow();
crs.acceptChanges();
//4. 处理结果集
JdbcUtil.printRs(crs);
crs.first();
System.out.println(
"=============== crs.first() ===============");
JdbcUtil.printRs(crs);
}catch(Exception e){ e.printStackTrace();}
finally{
JdbcUtil.close(crs);
}
例子2: 分页
public class TestPage {
public static void main(String[] args) {
try {
CachedRowSet crs = new CachedRowSetImpl();
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:thin:@localhost:1521:XE");
ods.setUser("system");
ods.setPassword("langna");
Connection conn = ods.getConnection();
PreparedStatement ps =
conn.prepareStatement("select * from ln_test",
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = ps.executeQuery();
crs.setMaxRows(3);//表示一次可以从数据库中读的最大的行数,好处是当数据库的数据量太大时要设置一下。
crs.setPageSize(3);//一页能够显示多少条记录
crs.populate(rs,1);//运行此代码时,将使用 rs 中从第几行开始的数据填充 crs,游标的位置发生了改变
while(crs.nextPage()){
while(crs.next()){
System.out.println(crs.getString(1));
}
System.out.println("------");
crs.close();//在设置的最大行数比数据库中的总函数少时, 用close来更新下一页的数据;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(十二) 程序设计
1、程序分层
(1)什么叫分层?几种分层的方法?
两种分层的方法:
1)纵向分层:J2EE/JavaEE 按功能划分:
web层: 图形界面,跟用户做I/O的;
技术:Servlet,JSP(JSF/Ajax),Struts
业务层: 业务处理、计算;
POJO(JavaBean),EJB,Spring
持久层: OR/Mapping;对象关系之间的转化;跟数据库打交道
JDBC,Hibernate
2)横向划分:按抽象程度划分;
JDBC:接口集(抽象层)--驱动(实现层)
web:实现层 5
业务:抽象层 1
实现层 3
持久:抽象层 2
实现层 4
业务层的抽象-->持久层的抽象-->业务层的实现-->持久层的实现--> 画界面(web)
(2)为什么要分层:
1) 使得程序符合设计原则;
因为设计原则来源于实践和需求 ;
开闭原则:对扩展开放,对修改关闭;
依赖于抽象:不依赖于具体,减少耦合性;
组件和组件之间抽象耦合:
web层调用业务层的接口;
业务层调用持久性的接口;
2) 物理原因:web服务器和应用服务器和数据库服务器不在同一个地方;
3) 便于维护;便于扩展;便于交付使用;
(十三) 补充:CallableStatement
CallableStatement cs=con.prepareCall("{call ln_pro_test(?)}");//调存储过程, p_test为存储过程的名字;
1) 存储过程是预编译的;
2) 为了安全;在代码中不能出现sql;从外部访问数据库没法访问;只能用存储过程;
可以写到sql文件里 ln.sql:
create or replace procedure ln_pro_test(i in number)as
begin
insert into ln_test values(i,'langna');
end;
@ln.sql;/
存储过程相当于sql 语句,可以当脚本执行;
con=JdbcUtil.getConnection();
cs=con.prepareCall("{call ln_pro_test(?)}");
cs.setInt(1,14);
cs.execute();
例子:
public class TestCall
{
public static void main(String[] args){
Connection con = null;
CallableStatement cs = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
con = JdbcUtil.getConnection();
cs = con.prepareCall("{call ln_pro_test(?)}");
cs.setInt(1,90);
cs.execute();
JdbcUtil.release(cs);
ps = con.prepareStatement(
"select * from ln_test order by id");
rs = ps.executeQuery();
JdbcUtil.printRS(rs);
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtil.release(rs,ps,con);
}
}
}