Visual C++ 2008入门经典 第二十一章更新数据源
/* 第二十一章更新数据源 本章主要内容: 1 数据库事务 2 如何使用记录集对像更新数据库 3 如何在更新操作中将数据从记录集传输到数据库 4 如何更新表中的现有行 5 如何添加新行到表中 21.1 更新操作 21.1.1 CRecordset更新操作 在更新数据库表中的字段或者添加全新的行时,也要使用RFX_()这种函数 CRecordset类中,有五个支持更新操作的成员函数 Edit() 调用这个函数将开始更新现有的记录,如果不能更新表,那么这个函数将抛去CDBException异常,如果出现了内存不足的情况,它将抛出CMomoryException异常 AddNew() 调用这个函数将开始添加全新的记录,如果不能把新记录附加到表中,这个函数将抛出CDBException异常 Update()调用这个函数将完成现有记录的更新或者新记录的添加,如果不能更新单个记录,或者出现了错误,这个函数将抛出CDBException异常 Delete() 通过创建和执行SQL DELETE语句,删除当前记录,如果出现错误-如数据库只读的,那么这个函数将抛出CDBException异常,在进行了Delete()操作以后,记录集的所有数据成员都将被设置为空值--相当于没有值集,在对记录集对像执行其他操作之前,必须移动到新记录 CancelUpdate() 取消修改现有记录或者添加新记录的未完成操作 更新现有记录或者添加新记录时事件的基本顺序 更新现有记录 (1) 调用记录集对像的Edit()成员:将记录集字段数据成员的当前值保存到缓冲区中 (2) 将字段数据成员设置为新值 (3) 调用记录集对像的Update()成员:通过与保存值进行比较,检查已修改的字段, 创建和执行SQL INSERT语句,更新已修改字段的DB 丢弃包含字段的旧保存值的缓冲区 更新新记录 (1)调用记录集对像的AddNew()成员:将记录集字段数据成员的当前值保存到缓冲区中 将记录集字段数据成员的当前值设置为PSEUDO_NULL (2) 设置新的字段数据成员值 (3) 调用记录集对像的Update()成员: 检查非NULL字段 创建和执行SQL INSERT语句,更新非NULL字段的DB 由缓冲区恢复旧保存值 数据在记录集数据成员和数据库之间传输时,始终要使用记录集对象的DoFieldExchange()成员,所以RFX_()函数提供了双重功能-将数据写入数据库,以及从数据库读取数据 1 检查操作是否合法 如果可以修改表中由记录集对像表示的记录,那么CRecordset的CanUpdate()成员将返回TRUE,在添加新记录时,可以事先调用户CRecordset的CanAppend()成员进行检查,如果允许在表中添加新记录,这个函数将返回TRUE 2 记录锁定 记录锁定防止其他用户在表行被更新期间访问锁定的记录,更新期间锁定记录的时间由记录集对像中设置的锁定模式确定 CRecordset定义了两种锁定模式 分别称为乐观锁定和非观锁定 CRecordset::optimistic 在乐观锁定模式中,只在执行Update()成员函数时锁定记录,这大大缩短了数据库的其他用户不能访问记录的时间,如果编辑操作需要很长的时间,则悲观锁定模式通常不切合实际,因为其他用户可能需要访问数据库,标准解决方法是使用乐观锁定模式,并引入某种冲突解决机制 CRecordset::pessimistic 在悲观锁定模式中,一调用Edit()函数就锁定记录,在调用Update()函数或者终止更新操作之前,记录将保持锁定状态,其他用户无法访问记录,在准备进行交互或更新时,这显然将严重影响性能,但是为了维护数据的完整性,这种模式在许多情况下是必要的 默认为乐观模式 悲观锁定模式需要调用参数为CRecordset::pessimistic的记录集对象的SetLockingMode()成员 也可以调用参数为CRecordset::optimistic的SetLockingMode()函数,然后对它进行重置 21.1.2 事务 在数据库上下文中,事务的概念是为了在必要时安全的取消操作,事务把数据库的一系列定义明确地更改组合成单个操作,如果出现错误,那么在事务完成之前的任何时间点,可以撤消(或者回滚)所有更改 利用基于事务的操作,数据库系统可以管理事务的处理,记录恢复信息,这样在出现问题时,就可以撤消事务对数据所做的所有操作 CDatabase事务操作 事务是通过CDatabase类对像的成员来管理的,该对像提供到数据库的连接,对于给定的连接,要确定是否支持事务,需要调用CDatabase对像的CanTransact()成员,如果支持事务,返回TRUE CDatabase还有一个CanUpdate()成员,如果数据源是只读的,它将返回false CDatabase对象有三个成员函数参与事务处理 BeginTrans() 在数据库上启动一个事务,在调用CommitTrans()或Rollback()之前,后续的所有记录保操作都是这个事务的一部分,如果事务启动成功,返回true CommitTrans() 提交事务,完成作为事务一部分的所有记录集操作,如果出现错误,这个函数将返回false,在这种情况下,数据源的状态是否确定的 Rollback()回滚调用BeginTrans()函数以来执行的所有记录集操作,并将数据源恢复到调用BeginTrans()函数时的状态 事务中事件的顺序 1 调用BeginTrans()函数,启动事务 2 根据需要调用记录集的Edit() Update() AddNew()函数 3 调用CommitTrans()函数,完成事务 在调用CommitTrans()之后需要调用CDatabase的成员GetCursorCommitBehavior(),在调用Rollback()之后需要调用GetCursorRollbackBehavior()这两个函数将返回下列三个int型数值之一 SQL_CB_PREDERVE 记录集与数据源的连挡不爱提交或回滚操作的影响,所以不用采取任何动作 SQL_CB_CLOSE 需要调用记录集对像的Requery(),恢复记录集中的当前位置 SQL_CB_DELETE 必须通过调用记录对像的Close()成员关闭记录集,如果必要的话,必须重新打开记录集 21.2 简单的更新示例 定制应用程序 21.3 管理更新过程 更新操作的过程: 用户在对话框上允许输入字段中输入数据,然后单击Update按钮完成更新操作,此后,该对话框返回到最初中的"只读"模式状态,所有编辑控件都成为只读控件,如果用户不希望进行更新操作,则可以单击Cancel按钮而不是Update按钮 需要在Edit Order按钮被单击之后做下面的事情 A 将Edit Order按钮上的文本修改为Update,使之现在成为完成更新操作的按钮 B 使Cancel按钮出现在对话框上,换句话说,使该按钮可见 C 在COrderDetailsView类中将"已经进入编辑模式"的情史记录下来,这是必要的,因为要为两项不同的用途使用同一个按钮,所以要根据所处理模式切换Edit Order和Update这两个标签 D 允许那些对应于希望更新的字段的编辑控件接受键盘输入 21.3.1 实现更新模式 1 启用禁用编辑控件 2 修改按钮标签 3 按制Cancel按钮的可见性 4 禁用Record菜单 5 执行更新 6 实现取消操作 基本完成,但有bug,找不出来原因 21.4 向表中添加行 21.4.1 订单录入过程 21.4.3 创建记录集 21.4.4 创建记录集视图 21.4.5 给对话框资源添加控件 21.4.6 实现对话框切换 21.4.7 创建订单ID 1 存储新订单的ID 2 创建新订单的ID 3 启动ID创建过程 21.4.8 存储订单数据 设置日期 21.4.9 为订单选择产品 21.4.10 添加新订单 */