- 浏览: 37093 次
- 性别:
- 来自: 常州
文章分类
最新评论
-
夜曲6763:
...
eclipse 手动安装Svn插件 -
我叫_不_开心:
请问楼主,ir这个变量是什么啊?
[ solr - MoreLikeThis ] - MoreLikeThis的原理分析 -
matay:
请问,在找候选词字符串时JaroWinklerDistance ...
[ 搜索引擎 ] - spellChecker原理分析 -
huangfoxAgain:
whiletrue 写道这是篇好文章唉,居然无人评论,谢谢分享 ...
[ 搜索引擎 ] - spellChecker原理分析 -
whiletrue:
这是篇好文章唉,居然无人评论,谢谢分享。
[ 搜索引擎 ] - spellChecker原理分析
简介:
一个纯jdbc的dao模型,参考网上多篇相关技术文章,重点包括:
1.事务处理;
2.模板设计;
3.异常处理;
其中事务和异常方面一直也是争议比较大的地方,希望大家多指正,提出宝贵的意见。
首先是dao的结构描述:
首先是一些基础类,包括:
dbutil: 数据库操作基础类(获取数据库连接、获取事务管理器等)
transactionTemplate: 事务处理模板类
transactionManager: 事务管理器
transactionCallback: 事务处理回调方法(返回结果)
transactionCallbackWithoutResult: 事务处理回调方法(不返回结果)
详细类容请参考源码:
DBUtil:
package com.bts.db; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.apache.log4j.Logger; import com.bts.exception.dao.DaoException; import com.bts.util.LogUtil; import com.bts.util.PropertiesRW; /** * @author huangfox * @serial 数据库连接池 采用dbcp组件。 <br> * */ public class DBUtil { private static Logger logger = Logger.getLogger(DBUtil.class); private static BasicDataSource ds = null; private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); static { try { PropertiesRW proRW = new PropertiesRW("src/dbcpParamter.properties"); Properties conf = proRW.getProperties(); ds = (BasicDataSource) BasicDataSourceFactory .createDataSource(conf); } catch (Exception e) { logger.error(LogUtil.logInfo("初始化数据库连接池时发生异常!", e)); } } /** * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上 * * @return 成功,返回Connection对象,否则返回null * @throws DaoException */ public static synchronized Connection getConnection() throws DaoException { // 先从当前线程上取出连接实例 Connection conn = tl.get(); // 如果当前线程上没有Connection的实例 if (null == conn) { try { // 从连接池中取出一个连接实例 conn = ds.getConnection(); // 把它绑定到当前线程上 tl.set(conn); } catch (SQLException e) { throw new DaoException("获取数据库连接时发生异常!", e); } } return conn; } /** * 获取事务管理器 * * @return 事务管理实例 * @throws DaoException */ public static synchronized TransactionManager getTranManager() throws DaoException { return new TransactionManager(getConnection()); } /** * 关闭数据库连接,并卸装线程绑定 * * @param conn * 要关闭数据库连接实例 * @throws DaoException */ protected static void close(Connection conn) throws DaoException { if (conn != null) { try { conn.close(); } catch (SQLException e) { throw new DaoException("关闭连接时出现异常!", e); } finally { // 卸装线程绑定 tl.remove(); } } } /** * 关闭ResultSet、Statement。 * * @param rs * @param stat * @throws DaoException */ public static void free(ResultSet rs, Statement stat) throws DaoException { try { if (rs != null) rs.close(); if (stat != null) stat.close(); } catch (SQLException e) { throw new DaoException("关闭ResultSet/Statement时出现异常!", e); } } }
TransactionCallback
package com.bts.db; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial 事务回调接口 */ public interface TransactionCallback<T> { /** * 要在事务中回调执行的方法 * * @return 所指定类型的数据 * @throws DaoException */ T doInTransaction() throws DaoException; }
TransactionCallbackWithoutResult:
package com.bts.db; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial 无返回值的事务回调接口 */ public interface TransactionCallbackWithoutResult { /** * 要在事务中回调执行的方法 * @throws DaoException * */ public void doInTransaction() throws DaoException; }
TransactionManager:
package com.bts.db; import java.sql.Connection; import java.sql.SQLException; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial 事务管理器 */ public class TransactionManager { private Connection conn; protected TransactionManager(Connection conn) { this.conn = conn; } /** * 开启事务 * * @throws DaoException */ public void beginTransaction() throws DaoException { try { // 把事务提交方式改为手工提交 conn.setAutoCommit(false); } catch (SQLException e) { throw new DaoException("开始事务时出现异常", e); } } /** * 提交事务并关闭连接 * * @throws DaoException */ public void commitAndClose() throws DaoException { try { conn.commit(); } catch (SQLException e) { DBUtil.close(conn); throw new DaoException("提交事务时出现异常", e); } finally { } } /** * 回滚并关闭连接 * * @throws DaoException */ public void rollbackAndClose() throws DaoException { try { conn.rollback(); } catch (SQLException e) { DBUtil.close(conn); throw new DaoException("回滚事务时出现异常", e); } finally { } } }
TransactionTemplate:
package com.bts.db; import org.apache.log4j.Logger; import com.bts.exception.dao.DaoException; import com.bts.util.LogUtil; /** * @author huangfox * @serial 事务执行模板,可以在service中避免事务的倾入。 */ public class TransactionTemplate { private static Logger logger = Logger.getLogger(TransactionTemplate.class); /** * 在事务里执行回调接口实现类中有返回值的方法 * * @param <T> * 返回值类型 * @param callback * 回调接口 * @return 指定类型的返回值 * @throws DaoException * 数据访问异常 */ public static <T> T execute(TransactionCallback<T> callback) { T result = null; TransactionManager tx = null; try { tx = DBUtil.getTranManager(); // 开启事务 tx.beginTransaction(); // 执行回调方法 result = callback.doInTransaction(); // 提交事务并关闭 tx.commitAndClose(); } catch (DaoException e) { logger.error(LogUtil.logInfo(e)); } finally { try { tx.rollbackAndClose(); } catch (DaoException e) { logger.error(LogUtil.logInfo(e)); } } return result; } /** * 在事务里执行回调接口实现类中没有返回值的方法 * * @param callback * 回调接口 * @throws DaoException * 数据访问异常 */ public static void execute(TransactionCallbackWithoutResult callback) { TransactionManager tx = null; try { tx = DBUtil.getTranManager(); tx.beginTransaction(); callback.doInTransaction(); tx.commitAndClose(); } catch (DaoException e) { logger.error(LogUtil.logInfo(e)); } finally { try { tx.rollbackAndClose(); } catch (DaoException e) { logger.error(LogUtil.logInfo(e)); } } } }
---------------------------------------------------------------
接下来就是dao方面的设计
定义一个接口dao,规范出共有的数据操作。
具体业务也定义一个接口***dao,继承自接口dao,该***dao主要可以定义一些特殊的方法。(该接口的定义需要结合实际情况判断有无必要)!
然后是一个常用的daoTemplate,定义通用的数据库操作。
Dao接口:
package com.bts.dao; import java.util.List; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial dao接口,定义各个业务对象数据库访问接口的公用方法。 * @param <T> */ public interface Dao<T> { /** * 根据查询条件获取具体某条数据 * * @param args * @return * @throws DaoException */ public T select(T args) throws DaoException; /** * 根据查询条件获取所有数据 * * @param args * @return * @throws DaoException */ public List<T> query(T args) throws DaoException; /** * 根据查询条件获取结果总数。 * * @param args * @return * @throws DaoException */ public int getCount(T args) throws DaoException; /** * 插入某一条数据 * * @param one * @throws DaoException */ public void insert(T one) throws DaoException; /** * 插入多条数据 * * @param multi * @throws DaoException */ public void insert(List<T> multi) throws DaoException; /** * 更新某一条数据 * * @param one * @throws DaoException */ public void update(T one) throws DaoException; /** * 更新多条数据 * * @param multi */ public void update(List<T> multi) throws DaoException; /** * 删除某一条数据 * * @param one * @throws DaoException */ public void delete(T one) throws DaoException; /** * 删除多条数据 * * @param multi */ public void delete(List<T> multi) throws DaoException; }
DaoTemplate:
package com.bts.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import com.bts.db.DBUtil; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial <br> * 模板方法,主要抽取dao中jdbc操作的公用方法。 <br> * 另外定义相应的抽象方法,强制子类实现,主要功能包括resultSet对业务对象的转换、查询条件构造等。 * @param <T> */ public abstract class DaoTemplate<T> { /** * 获取符合查询条件的某一条数据。 * * @param sql * @param args * 查询条件 * @return * @throws DaoException */ public T findTemplate(String sql, Object[] args) throws DaoException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); rs = ps.executeQuery(); T obj = null; if (rs.next()) { obj = rs2obj(rs); } return obj; } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { DBUtil.free(rs, ps); } } /** * 获得符合查询条件的结果记录总数。 * * @param sql * @param args * 查询条件 * @return * @throws DaoException */ public int getCountTemplate(String sql, Object[] args) throws DaoException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); rs = ps.executeQuery(); int count = 0; if (rs.next()) { count = rs.getInt(1); } return count; } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { DBUtil.free(rs, ps); } } /** * 获取符合条件的所有数据 * * @param sql * @param args * 查询参数 * @return * @throws DaoException */ public List<T> QueryTemplate(String sql, Object[] args) throws DaoException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; List<T> results = new ArrayList<T>(); try { conn = DBUtil.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); rs = ps.executeQuery(); T obj = null; while (rs.next()) { obj = rs2obj(rs); results.add(obj); } return results; } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { DBUtil.free(rs, ps); } } /** * 更新操作 * * @param sql * @param args * @param isGeneralKey * @throws DaoException */ public void updateTemplate(String sql, Object[] args, boolean isGeneralKey) throws DaoException { Connection conn = null; PreparedStatement ps = null; try { conn = DBUtil.getConnection(); ps = (isGeneralKey ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn .prepareStatement(sql)); for (int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); ps.executeUpdate(); } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { DBUtil.free(null, ps); } } /** * 批量更新操作 * * @param sql * @param args * @param isGeneralKey * @throws DaoException */ public void updateMultiTemplate(String sql, List<Object[]> args, boolean isGeneralKey) throws DaoException { Connection conn = null; PreparedStatement ps = null; try { conn = DBUtil.getConnection(); ps = (isGeneralKey ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn .prepareStatement(sql)); for (int i = 0; i < args.size(); i++) { for (int j = 0; j < args.get(i).length; j++) { ps.setObject(j + 1, args.get(i)[j]); } // ps.addBatch(); } ps.executeBatch(); } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { DBUtil.free(null, ps); } } /** * 准备查询条件。 * * @param sql * @param args * 查询条件 * @return */ public abstract String prepareQuery(String sql, T args); /** * 用于resultSet与具体业务对象转化的方法。 * * @param rs * @return * @throws SQLException */ public abstract T rs2obj(ResultSet rs) throws SQLException; }
TaskDao:(改接口有待讨论)
package com.bts.dao; import com.bts.bean.Task; /** * @author huangfox * * @serial 具体业务对象(Task)的数据访问接口, 继承接口DAO的基础方法,<br> * 并且可以定义在此定义特殊的方法。 * */ public interface TaskDao extends Dao<Task> { // 特殊方法 }
TaskDaoImpl:
package com.bts.dao.impl; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import com.bts.bean.Task; import com.bts.dao.DaoTemplate; import com.bts.dao.TaskDao; import com.bts.exception.dao.DaoException; /** * @author huangfox * @serial * */ public class TaskDaoImpl extends DaoTemplate<Task> implements TaskDao { @Override public Task select(Task args) throws DaoException { String sql = " select * from FeeInfoTasks where ApplicationNumber = ? "; Object[] params = new Object[] { args.getAppNum() }; Task result = new Task(); result = findTemplate(sql, params); return result; } @Override public List<Task> query(Task args) throws DaoException { String sql = " select * from FeeInfoTasks where 1 = 1 "; sql = prepareQuery(sql, args); Object[] params = new Object[] {}; List<Task> result = new ArrayList<Task>(); result = QueryTemplate(sql, params); return result; } @Override public int getCount(Task args) throws DaoException { String sql = " select count(*) from FeeInfoTasks where 1 = 1 "; sql = prepareQuery(sql, args); Object[] params = new Object[] {}; int count = 0; count = getCountTemplate(sql, params); return count; } @Override public void insert(Task one) throws DaoException { String sql = " insert into FeeInfoTasks " + "(ApplicationNumber,NoticeDate,PRI,Status,UpdateTime,FeeCount) values " + "(?,?,?,?,?,?) "; Object[] params = new Object[] { one.getAppNum(), one.getNoticeDate(), one.getPrimary(), one.getStatus(), one.getUpdateTime(), one.getFeeCount() }; updateTemplate(sql, params, false); } @Override public void update(Task one) throws DaoException { String sql = " update FeeInfoTasks set " + "NoticeDate = ? , PRI = ? , " + "Status = ? , UpdateTime = ? , FeeCount = ? " + "where ApplicationNumber =? "; Object[] params = new Object[] { one.getNoticeDate(), one.getPrimary(), one.getStatus(), one.getUpdateTime(), one.getFeeCount(), one.getAppNum() }; updateTemplate(sql, params, false); } @Override public void delete(Task one) throws DaoException { String sql = " delete from FeeInfoTasks where ApplicationNumber = ? "; Object[] params = new Object[] { one.getAppNum() }; updateTemplate(sql, params, false); } @Override public void insert(List<Task> multi) throws DaoException { String sql = " insert into FeeInfoTasks " + "(ApplicationNumber,NoticeDate,PRI,Status,UpdateTime,FeeCount) values " + "(?,?,?,?,?,?) "; List<Object[]> paramList = new ArrayList<Object[]>(); for (int i = 0; i < multi.size(); i++) { Task one = multi.get(i); Object[] params = new Object[] { one.getAppNum(), one.getNoticeDate(), one.getPrimary(), one.getStatus(), one.getUpdateTime(), one.getFeeCount() }; paramList.add(params); } updateMultiTemplate(sql, paramList, false); } @Override public void update(List<Task> multi) { // TODO Auto-generated method stub } @Override public void delete(List<Task> multi) { // TODO Auto-generated method stub } @Override public Task rs2obj(ResultSet rs) throws SQLException { Task task = new Task(); if (rs != null) { task.setAppNum(rs.getString("ApplicationNumber")); task.setNoticeDate(rs.getString("NoticeDate")); task.setPrimary(rs.getInt("PRI")); task.setStatus(rs.getInt("Status")); task.setUpdateTime(rs.getString("UpdateTime")); task.setFeeCount(rs.getInt("FeeCount")); } return task; } @Override public String prepareQuery(String sql, Task args) { if (args != null) { if (args.getAppNum() != null) sql += " and ApplicationNumber = " + args.getAppNum(); if (args.getNoticeDate() != null) sql += " and NoticeDate = " + args.getNoticeDate(); sql += " and PRI = " + args.getPrimary(); sql += " and Status = " + args.getStatus(); if (args.getUpdateTime() != null) sql += " and UpdateTime = " + args.getUpdateTime(); sql += " and FeeCount = " + args.getFeeCount(); } return sql; } }
注意:以上仅为部分实现!
---------------------------------------------------------
到这里,我们就要关心事务处理方面的问题了。
我将事务处理放到了service层,采用的是回调的方式。
参考:http://blog.csdn.net/qjyong/article/details/5513638
TaskService:
package com.bts.service; import java.util.List; import com.bts.bean.Task; import com.bts.dao.TaskDao; import com.bts.dao.impl.TaskDaoImpl; import com.bts.db.TransactionCallback; import com.bts.db.TransactionCallbackWithoutResult; import com.bts.db.TransactionTemplate; import com.bts.exception.dao.DaoException; /** * @author huangfox * */ public class TaskService { public TaskDao dao = null; public TaskService() { super(); this.dao = new TaskDaoImpl(); } /** * 通过applicationNumber获取Task。 * * @param args * @return * @throws DaoException */ public Task getTaskByApplicationNumber(final Task args) { return TransactionTemplate.execute(new TransactionCallback<Task>() { @Override public Task doInTransaction() throws DaoException { return dao.select(args); } }); } /** * 获取所有符合条件的Task。 * * @param args * @return * @throws DaoException */ public List<Task> getTasks(final Task args) { return TransactionTemplate .execute(new TransactionCallback<List<Task>>() { @Override public List<Task> doInTransaction() throws DaoException { return dao.query(args); } }); } /** * 获取所有符合条件的Task的总量。 * * @param args * @return * @throws DaoException */ public int getTasksCount(final Task args) { return TransactionTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction() throws DaoException { return dao.getCount(args); } }); } /** * 添加一个任务。 * * @param one * @throws DaoException */ public void addTask(final Task one) { TransactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransaction() throws DaoException { dao.insert(one); } }); } /** * 添加多个任务。 * * @param tasks */ public void addTaskMutil(final List<Task> tasks) { TransactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransaction() throws DaoException { dao.insert(tasks); } }); } /** * 根据申请号(ApplicationNumber)修改一个task。 * * @param one * @throws DaoException */ public void modifyTask(final Task one) { TransactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransaction() throws DaoException { dao.update(one); } }); } /** * 根据申请号(ApplicationNumber)删除一个Task。 * * @param one * @throws DaoException */ public void deleteTaskByAppNum(final Task one) { TransactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransaction() throws DaoException { dao.delete(one); } }); } }
---------------------------------------------------------------------
如果哪位超级有耐心的看到了着,请你参与讨论这个dao模型中的不足,或者说说你项目中dao是怎么运用的!
重点可以关注:
事务处理
异常处理
设计模式
其他 :P
相关推荐
这种方式非常适合那些需要在一个事务中执行多个 DAO 操作的情况。 ##### 2. 声明式事务界定 与编程式事务界定相比,声明式事务界定是在配置文件或注解中指定事务行为的一种方式。这种方式可以减少代码量并提高可...
这个"dao.rar"压缩包包含了一个DAO模型的实例,非常适合初学者学习和理解。 DAO模式的核心思想是创建一个专门处理数据库操作的对象(即DAO对象),这样业务逻辑组件就可以通过调用DAO接口来执行数据库操作,而无需...
自动事务管理是现代应用中的一个重要特性,它使得开发者无需在每个数据库操作后手动开启和提交事务,而是由框架自动处理。在Hibernate中,我们可以利用Spring框架的AOP(面向切面编程)和Transaction Proxy来实现这...
所有的事务处理操作都会提交给DaoContext接口方法,然后DaoContext接口会调用持久层接口的相应方法来执行事务操作。 - **配置**: 为了配置iBATIS DAO事务管理,开发者需要指定使用哪种事务管理框架,并配置相应的...
1. 统一异常处理:统一异常处理是指在项目中设置一个全局的异常处理器,当程序抛出异常时,这个处理器捕获异常并进行统一的处理。这样可以避免在每个方法中都写try-catch块,使得代码更加简洁,同时也方便管理和维护...
在这个“一个DAO通用模型”中,我们可以深入探讨DAO模式的基本概念、实现原理以及如何使用连接池来优化数据库操作。 1. DAO模式介绍: DAO模式是一种数据访问层的设计模式,它定义了接口来对数据库或其他持久化存储...
此外,我们可以自定义一个`ServiceException`来封装业务层的错误信息,以便于客户端更好地理解和处理。 异常处理方面,我们可以利用Java的异常链机制,保留原始的数据库异常信息,以便于调试。同时,为了提高代码的...
* PROPAGATION_MANDATORY:当前方法必须运行在一个事务上下文中,否则就抛出异常。 * PROPAGATION_NEVER:当前方法不应该运行在一个事务上下文中,否则就抛出异常。 此外,Spring的事务处理还提供了多种事务隔离...
自己写的一个java示例 该示例用jdbc与java事务来实现...该示例包含了一个方便调用的数据库访问工具类,该工具类实现了调用SQL语句,调用PreparedStatement对象,并对可能产生的异常进行了处理,保证了各DAO子类调用的简便性
在实现DAO时,有三个重要的方面需要考虑:事务界定、异常处理和日志记录。 **事务界定**是确保数据库操作原子性的关键。根据J2EE规范,有两种事务界定模型:编程式和声明式。编程式事务管理要求程序员在代码中明确...
通过本文的介绍,我们深入了解了高级DAO编程技巧,特别是在事务界定、异常处理和日志记录这三个方面的应用。这些技巧对于构建高效、稳定的企业级应用至关重要。希望读者能够在实际工作中运用这些知识,提升代码的...
1. **抽象基类**:创建一个通用的DAO基类,包含一些通用的数据库操作,如执行SQL查询、事务管理等。子类可以继承这个基类并覆盖或添加特定的数据库操作。 2. **模板方法设计模式**:利用模板方法模式,将共有的操作...
1)dao类的繁多,很多设计都是一个entity对应一个dao (不同的只有类名和方法名) 2)dao接口需要维护的method庞大。 3)业务逻辑改变时,dao需要同时修改两个类文件(接口和实现类) 在本文中,我将为您展示如何...
自己写的一个java示例 该示例用jdbc与java事务来实现...该示例包含了一个方便调用的数据库访问工具类,该工具类实现了调用SQL语句,调用PreparedStatement对象,并对可能产生的异常进行了处理,保证了各DAO子类调用的简便性
1. **数据库连接**:DAO提供了一种直接与数据库进行通信的方法,允许开发者创建连接、打开和关闭数据库,以及执行查询和事务处理。 2. **对象模型**:DAO 3.5包含一系列的对象,如Database、TableDef、QueryDef、...
DAO(Data Access Objects)是微软在早期开发Access数据库应用程序时引入的一个对象模型,它提供了与数据库交互的方法和接口。DAO3.6是DAO的一个特定版本,主要用于与Access 97数据库进行通信。在这个版本中,DAO...
例如,在一个简单的网上书店购书场景中,`BookStoreManager`服务会涉及到`BookDAO`和`CustomerDAO`的交互,确保库存减少、账户扣款等操作要么全部成功,要么全部回滚。 以下是一个简单的例子: 1. 定义业务接口`...
DAO 是 Access 的一个对象,专门用于访问和操作 Access 数据库,而 ADO 是一种通用的数据访问技术,能够访问和操作各种各样的数据库。DAO 的对象模型是层次结构的,而 ADO 的对象模型是基于 Connection 对象的。ADO ...
通过结合MVC和DAO模式,我们可以实现一个清晰、可维护的用户登录系统。这种方式使得代码结构更有序,易于扩展和测试,同时也便于团队协作。文件"项目07_使用MVC+DAO完成用户登陆(代码+笔记)"应该包含了实现这一功能...