JDBC03 DAO模式NetCTOSS V1 DAO实现AccountService.docx
《JDBC03 DAO模式NetCTOSS V1 DAO实现AccountService.docx》由会员分享,可在线阅读,更多相关《JDBC03 DAO模式NetCTOSS V1 DAO实现AccountService.docx(8页珍藏版)》请在冰豆网上搜索。
JDBC03DAO模式NetCTOSSV1DAO实现AccountService
1.JDBC高级编程
1.1.JDBC高级编程
1.1.1.事务简介
事务(Transaction):
数据库中保证交易可靠的机制。
JDBC支持数据库中的事务概念,并且在JDBC中,事务默认是自动提交的。
可能存在的问题:
某些业务逻辑必须在一个事务中完成。
如图-1所示。
图-1事务示意图
事务特性介绍:
ACID
∙原子性(Atomicity):
事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行
∙一致性(Consistency):
事务在完成时,必须使所有的数据都保持一致状态
∙隔离性(Isolation):
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离
∙持久性(Durability):
事务完成之后,它对于系统的影响是永久性的
事务是数据库的概念,JDBC支持事务,本质还是在数据库中实现的。
1.1.2.JDBC事务API
JDBC中和事务相关API:
∙Connection.getAutoCommit():
获得当前事务的提交方式,默认为true
∙Connection.setAutoCommit():
设置事务的提交属性,参数是true:
自动提交;false:
不自动提交
∙Cmit():
提交事务
∙Connection.rollback():
回滚事务
1.1.3.JDBC标准事务编程模式
JDBC处理事务的通常模式:
∙先将事务的自动提交关闭;
∙执行事务中的若干SQL语句;
∙事务提交;SQL失败则回滚;
∙恢复JDBC的事务提交状态;释放资源。
1.try{
2.//1.定义用于在事务中执行的SQL语句
3.Stringsql1="updateaccountsetamount=amount-"+amount+"whereid='"+from+"'";
4.Stringsql2="updateaccountsetamount=amount+"+amount+"whereid='"+to+"'";
5.autoCommit=con.getAutoCommit();//2.获得自动提交状态
6.con.setAutoCommit(false);//3.关闭自动提交
7.stmt.executeUpdate(sql1);//4.执行SQL语句
8.stmt.executeUpdate(sql2);
9.mit();//5.提交
10.con.setAutoCommit(autoCommit);//6.将自动提交功能恢复到原来的状态
11.//其他语句;
12.}catch(SQLExceptione){
13. conn.rollback();//异常时回滚
14.}
1.2.批量更新
1.2.1.批量更新的优势
批处理:
发送到数据库作为一个单元执行的一组更新语句。
批处理降低了应用程序和数据库之间的网络调用,相比单个SQL语句的处理,批处理更为有效。
1.2.2.批量更新API
∙addBatch(Stringsql)
∙Statement类的方法,可以将多条sql语句添加Statement对象的SQL语句列表中
∙addBatch()
∙PreparedStatement类的方法,可以将多条预编译的sql语句添加到PreparedStatement对象的SQL语句列表中
∙executeBatch()
∙把Statement对象或PreparedStatement对象语句列表中的所有SQL语句发送给数据库进行处理
∙clearBatch()
∙清空当前SQL语句列表
1.2.3.防止OutOfMemory
如果PreparedStatement对象中的SQL列表包含过多的待处理SQL语句,可能会产生OutOfMemory错误。
所以需要及时处理SQL语句列表。
1.for(inti=0;i<1000;i++){
2.sql=“insertintoemp(empno,ename)values(emp_seq.nextval,'name"+i+"'")";
3.//将SQL语句加入到Batch中
4.stmt.addBatch(sql);
5.//根据需要设置及时处理的条件
6.if(i%500==0){
7.stmt.executeBatch();//及时处理
8.stmt.clearBatch();//清空列表
9.}
10.}
11.//最后一次列表不足500条,处理
12.stmt.executeBatch();
1.3.返回自动主键
1.3.1.关联数据插入
在主表/从表关联关系下,插入数据时需要保证数据完整性。
关联数据插入时的流程:
图-2关联数据插入流程
1.3.2.通过序列产生主键(Oracle)
不同数据库的主键自增方式不同,在oracle中,建议主键通过序列获得。
在SQL语句中,指定由哪个序列为表产生主键:
1.Stringsql=
2.“insertintodept(deptno,dname,loc)values(dept_seq.nextval,?
?
)”;
如果仅仅是单表操作,不需要返回刚刚插入的主键值,但如果有关联关系的表操作,需要获得刚刚插入的主键值。
1.3.3.JDBC返回自动主键API
方法一:
先通过序列的nextval获取序列的下一个值,再作为参数插入到主表和从表。
1.//1.获得主键的SQL语句
2.Stringsql="selectdept_seq.nextvalasidfromdual";
3.//执行上条SQL语句,通过ResultSet获得主键值,省略
4.
5.//2.利用刚刚得到的主键值插入主表
6.Stringsql1=“insertintodept(deptno,dname,loc)values(?
?
?
)";
7.//执行insert语句,省略
8.
9.//3.利用刚刚得到的主键值,作为外键插入从表
10.Stringsql2=“insertintoemp(empno,ename,deptno)values(?
?
?
)”;
11.//执行insert语句,省略
这种方式操作简单,但需要额外多一次访问数据库,影响性能。
方法二(建议):
利用PreparedStatement的getGeneratedKeys方法获取自增类型的数据,性能良好,只要一次SQL交互。
1.//1.插入主表SQL,使用序列作为主键
2.sql="insertintodept(deptno,dname,loc)values(dept_seq.nextval,?
?
)";
3.//2.定义stmt时,第二个参数是GeneratedKeys的主键的字段名列表,类型是字符串数组
4.stmt=con.prepareStatement(sql,newString[]{"deptno"});
5.//3.将占位符赋值
6.stmt.setString(1,“Research”);
7.stmt.setString(2,“beijing”);
8.//4.执行插入主表的insert语句
9.stmt.executeUpdate();
10.//5.获得主键值
11.rs=stmt.getGeneratedKeys();
12.rs.next();
13.intdeptno=rs.getInt
(1);
14.//6.将刚刚得到的主表主键值,作为外键插入到从表中。
15.Stringsql2=“insertintoemp(empno,ename,deptno)values(?
?
?
)”;
1.4.分页查询
1.4.1.JDBC实现Oracle分页查询
利用数据库的分页SQL语句,实现在Java程序中数据表的分页。
在Oracle中使用rownum,以获得分页结果集:
1.Stringsql="select*from(selectrownumrn,empno,ename,job,mgr,hiredate,sal,comm,deptnofrom(select*fromemporderbyempno))wherernbetween?
and?
";
两个占位符分别是结果集的起点和终点,计算后,替代SQL中的占位符:
1. intbegin=(page-1)*pageSize+1;
2. intend=begin+pageSize-1;
其中:
∙page:
返回第几页
∙pageSize:
每页多少条
1.stmt=con.prepareStatement(sql);
2.stmt.setInt(1,begin);
3.stmt.setInt(2,end);
4.rs=stmt.executeQuery();
这种分页方式,每次只向数据库请求一页的数据量,内存压力小适合大数据量数据表。
另一种分页策略介绍:
基于缓存的分页技术(也被称作假分页),一次性把数据全部取出来放在缓存中,根据用户要看的页数(page)和每页记录数(pageSize),计算把哪些数据输出显示,将可滚动结果集的指针移动到指定位置。
这种方式只访问数据库一次,第一次取数比较慢,以后每页都从缓存中取,比较快,比较适合小数据量,如果数据量大,对内存压力较大。
1.4.2.JDBC实现MySQL分页查询
不同数据库获取部分结果集的SQL是有区别的,在MySQL中的实现方式:
1.select*fromtlimitbegin,pageSize;
其中:
∙begin:
从第几条开始显示
∙pageSize:
每页多少条
在Java程序中,MySQL和Oracle分页的方式,仅限于SQL语句不同。
2.DAO
2.1.什么是DAO
2.1.1.DAO封装对数据的访问
DAO(DataAccessObject):
数据访问对象,是建立在数据库和业务层之间,封装所有对数据库的访问。
目的:
数据访问逻辑和业务逻辑分开。
图-3DAO在多层结构中的位置
为了建立一个健壮的Java应用,需将所有对数据源的访问操作抽象封装在一个公共API中,需要:
∙建立一个接口,接口中定义了应用程序中将会用到的所有事务方法
∙建立接口的实现类,实现接口对应的所有方法,和数据库直接交互
在应用程序中,当需要和数据源交互时则使用DAO接口,不涉及任何数据库的具体操作。
DAO通常包括:
1.一个DAO工厂类;
2.一个DAO接口;
3.一个实现DAO接口的具体类;
4.数据传递对象(实体对象(Entity)或值对象(ValueObject,简称VO)).
2.1.2.实体对象
DAO层需要定义对数据库中表的访问。
对象关系映射(ORM:
Object/RelationMapping)描述对象和数据表之间的映射,将Java程序中的对象对应到关系数据库的表中:
∙表和类对应
∙表中的字段和类的属性对应
∙记录和对象对应
2.2.编写DAO
2.2.1.查询方法
1. publicAccountfindById(Integerid)throwsSQLException{
2. Connectionconn=getConnection();
3. Stringsql=SELECT_BY_ID;//预先定义好的SQL查询语句
4. PreparedStatementps=conn.prepareStatement(sql);
5. ps.setInt(1,id);//传入参数
6. ResultSetrs=ps.executeQuery();
7. Accountaccount=null;
8. while(rs.next()){//处理结果集
9. account=newAccount();
10. account.setId(rs.getInt("ACCOUNT_ID"));
11. //设置account对象所有的属性,略 }
12. returnaccount;
13. }
2.2.2.更新方法
1. publicbooleanupdate(Accountaccount)throwsSQLException{
2. Connectionconn=getConnection();
3. Stringsql=UPDATE_STATUS;//预先定义好的SQL语句
4. PreparedStatementps=conn.prepareStatement(sql);
5. ps.setInt(1,account.getId());//传入参数
6.ps.setString(2,account.getStatus());
7. intflag=ps.executeUpdate();
8. return(flag>0?
true:
false);
9. }
2.2.3.异常处理机制
多层系统的异常处理原则:
∙谁抛出的异常,谁捕捉处理,因为只有异常抛出者,知道怎样捕捉处理异常;
∙尽量在当前层中捕捉处理抛出的异常,尽量不要抛出到上层接口;
∙尽量在每层中封装每层的异常类,这样可准确定位异常抛出的层;
∙如异常无法捕捉处理,则向上层接口抛出,直至抛给JVM,尽量避免;