锁定老帖子 主题:DAO设计模式(希望大家指正)
精华帖 (5) :: 良好帖 (1) :: 新手帖 (15) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-29
不明白。太罗嗦了。就单纯IOC容器而言,spring可没这么简单。
|
|
返回顶楼 | |
发表时间:2009-08-12
你不觉得在DAO层关闭数据库连接不是那么妥当么
|
|
返回顶楼 | |
发表时间:2009-08-16
一点拙见: 1,LZ的思路我觉得挺明确的,也比较符合大众的口味。 2,澄清下一些历史。 gaojiewyh 写道 的确和spring相似,
但是spring的依赖注入和策略模式不是很相似嘛,提出控制反转后又提出依赖注入,诸多的概念不感觉重复吗?我写代码只是想从中获得提高 IoC 的概念是Michael Mattson在1996提出来的,Martin Fowler 把IOC就称为DI设计模式,后来IoC鼻祖之一 Stefano Mazzocchi 却指出Martin Fowler这个忽悠了大家,仅仅是从字面上理解。 我觉得比较好的理解是:软件的组装部署和配置完全是由非业务逻辑模块反过来主动控制业务逻辑模块来安排。 详细的内容,见BLOG:http://c-j.iteye.com/admin/blogs/443505 而关于这点思想,从LZ的代码上来看,我感觉仅仅体现在了如下几行: try { CourseDAO courseDao = (CourseDAO) DaoFactory.getInstance().getDAO( "courseDao"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 3,Spring我觉得IOC只是他的基础,这个也是很简单的东西,精髓,但Johnson不仅仅只写出了IOC,也同时提供了transaction,并保证了线程安全(据说是用ThreadLocal保证的)。 所以,至少我在LZ的代码里好像没看到保证线程安全的机制。我觉得LZ可以加进去,保证不发生死锁现象。 最后,谢谢LZ提供的DBCP的信息。 |
|
返回顶楼 | |
发表时间:2009-08-18
好像代码上有可以改进点地方.如下
1,mapRow可以用reflect做个通用的接口 public Object mapRow(ResultSet rs,String className) { .... } 2,加上线程同步和final: private final Object lock; public void update(String sql, final Object[] args, final boolean isGeneralKey) throws DaoException { synchronized (lock) { ... } } |
|
返回顶楼 | |
发表时间:2010-04-19
基于接口的模板回调, 代码比较精典! 我以前就用这个方法给项目搭过框架的!,但是因为很多人不理解这些回调,故最终放弃了这种方式
|
|
返回顶楼 | |
发表时间:2010-05-17
请发个org.apache.commons.dbcp包给我,谢谢。
我用jdk1.5的,上apache下载的文件出错啊。 邮箱:380546033@qq.com |
|
返回顶楼 | |
发表时间:2010-05-19
最后修改:2010-05-19
楼主的这玩意有个比较致命的问题,在DAO层就关闭了数据库连接,那么Manager中无法进行比较复杂的事务控制,结合一下Spring可能会有更好的方式。
1年多前的一个项目我也做过跟楼主类似的东西,但有个前提,那个项目禁止使用Hibernate,我提过多次建议都无效,最后没辙为了节省一下简单增删改查的代码量写了几个类。 1.Entity:所有实体实现这个接口。 public interface Entity<IDClass extends java.io.Serializable> { public IDClass getId(); public void setId(IDClass id); } 2.GenericDao:所有Dao实现这个接口。 public interface GenericDao<E extends Entity> { /** * 保存一个实体 * @param entity 实体对象 */ public void save(E entity); /** * 删除一个实体 * @param id 主键 */ public void delete(Serializable id); /** * 删除一个实体 * @param entity 实体对象 */ public void delete(E entity); /** * 批量删除实体 * @param entitiesId 实体编号数组 */ public void deleteBatchByEntitiesId(Object[] entitiesId); /** * 批量删除实体 * @param entities */ public void deleteBatchByEntities(List<E> entities); /** * 查询所有 * @param page 分页对象 * @param sorts 排序对象 * @return 查询结果 */ public List getAll(Page page, Sort... sorts); /** * 按页号和每页记录数查询所有 * @param pageIndex 页号 * @param pageSize 每页记录数 * @param sorts 排序 * @return 查询结果 */ public List getAll(Integer pageSize, Integer pageIndex, Sort... sorts); /** * 通过主键查询 * @param id 主键 * @return 实体对象 */ public E get(Serializable id); /** * 按页号和每页记录数查询结果 * @param ql 查询语句 * @param pageIndex 页号 * @param pageSize 每页记录数 * @param args 查询语句参数 * @return 查询结果 */ public List query(String ql, Integer pageSize, Integer pageIndex, Object ... args); /** * 通过查询语句查询 * @param ql 查询语句 * @param page 分页对象 * @param args 参数 * @return 查询结果 */ public List query(String ql, Page page, Object... args); /** * 通过查询语句查询,无分页操作 * @param ql 查询语句 * @param args 参数 * @return 查询结果 */ public List query(String ql, Object... args); /** * 执行更新 * @param ql 执行更新 * @param args ql参数 * @return 更新所影响的行数 */ public int update(String ql, Object... args); /** * 计算记录数 * @param ql 查询语句 * @param args 查询条件参数 * @return 符合条件的记录数 */ public int count(String ql, Object... args); } 3.EntityMapper:实体类属性到数据库表字段映射。 public class EntityMapper { // 类名 private Class entityClass; // 表明 private String tableName; // 列名为map键对应的字段名 private Map<String, String> columnMapField = new HashMap<String, String>(); // 字段名为map建对应的列名 private Map<String, String> fieldMapColumn = new HashMap<String, String>(); // get方法名称 private Map<String, String> gettersName = new HashMap<String, String>(); // set方法名称 private Map<String, String> settersName = new HashMap<String, String>(); // 被映射的字段个数 private Integer mappedColumnCount; // 数据库字段 private String[] columns; // 类字段 private String[] fields; // 类字段类型 private Class[] fieldsType; // 插入语句 private String insert; // 删除语句 private String delete; // 更新语句 private String update; // 查询语句 private String select; // 查询所有语句 private String all; // 匹配大写字母的正则 private static Pattern p = Pattern.compile("(_|[A-Z]+)"); // 日志 private static Log log = LogFactory.getLog(EntityMapper.class); protected static final Map<String, String> RS_METHOD_NAME; static{ RS_METHOD_NAME = new HashMap<String, String>(); RS_METHOD_NAME.put("byte", "getByte"); RS_METHOD_NAME.put("Byte", "getByte"); RS_METHOD_NAME.put("short", "getShort"); RS_METHOD_NAME.put("Short", "getShort"); RS_METHOD_NAME.put("int", "getInt"); RS_METHOD_NAME.put("Integer", "getInt"); RS_METHOD_NAME.put("long", "getLong"); RS_METHOD_NAME.put("Long", "getLong"); RS_METHOD_NAME.put("float", "getFloat"); RS_METHOD_NAME.put("Float", "getFloat"); RS_METHOD_NAME.put("double", "getDouble"); RS_METHOD_NAME.put("Double", "getDouble"); RS_METHOD_NAME.put("Date", "getTimestamp"); RS_METHOD_NAME.put("boolean", "getBoolean"); RS_METHOD_NAME.put("Boolean", "getBoolean"); RS_METHOD_NAME.put("String", "getString"); } /** * 构造方法初始化类与数据库表的映射关系 * @param className 类名 */ public EntityMapper(Class entityClass) throws SQLException{ this.entityClass = entityClass; this.tableName = convertName(entityClass.getSimpleName()); // 获得该实体类中所有字段名并按规则转换成数据库字段名装入到Map中 Field[] fields = entityClass.getDeclaredFields(); Map<String, String> fieldsMap = new HashMap<String, String>(); for(int i = 0; i < fields.length; i++){ fieldsMap.put(this.convertName(fields[i].getName()), fields[i].getName()); } Connection conn = null; ResultSet rs = null; try { conn = ((ConnectionProvider)Platform.getInstance().getBean("ConnectionProvider")).getConnection(); // 获得数据库中该表所有字段的元数据 DatabaseMetaData meta = conn.getMetaData(); rs = meta.getColumns(null, null, this.tableName, null); if(log.isDebugEnabled()){ log.debug(entityClass.getName() + " -> " + this.tableName); } List<String> columnsName = new ArrayList<String>(); List<String> fieldsName = new ArrayList<String>(); List<Class> fieldsType = new ArrayList<Class>(); String columnName = null; String fieldName = null; // 循环将数据库中与实体类中公有的字段放入Map while(rs.next()){ columnName = rs.getString(4); if((fieldName = fieldsMap.get(columnName)) != null){ if(log.isDebugEnabled()){ log.debug(entityClass.getName() + "." + fieldName + " -> " + this.tableName + "." + columnName); } this.columnMapField.put(columnName, fieldName); this.fieldMapColumn.put(fieldName, columnName); this.gettersName.put(fieldName, this.convertName("get", fieldName)); this.settersName.put(fieldName, this.convertName("set", fieldName)); columnsName.add(columnName); fieldsName.add(fieldName); fieldsType.add(this.entityClass.getDeclaredField(fieldName).getType()); } } this.columns = (String[])columnsName.toArray(new String[columnsName.size()]); this.fields = (String[])fieldsName.toArray(new String[fieldsName.size()]); this.fieldsType = (Class[])fieldsType.toArray(new Class[fieldsType.size()]); this.mappedColumnCount = this.columns.length; // 初始化CRUD语句 this.createInsert(); this.createDelete(); this.createUpdate(); this.createSelect(); } catch(Exception ex) { log.error("初始化实体映射类失败...",ex); } finally { try{ if(rs != null){ rs.close(); conn.close(); } }catch(Exception ex){ log.error(ex); } } } /** * 通过结果集创建实体类 * @param rs 结果集 * @return 实体类 */ public Entity createFromResultSet(ResultSet rs){ try{ Entity entity = null; if(rs.next()){ entity = (Entity)this.entityClass.newInstance(); for(int i = 0; i < fields.length; i++){ if(log.isDebugEnabled()){ Object value = rs.getClass().getMethod(RS_METHOD_NAME.get(fieldsType[i].getSimpleName()), String.class).invoke(rs, columns[i]); log.debug(this.entityClass.getName() + "." + fields[i] + " -> " + value + " : " + (value == null ? "null" : value.getClass().getName())); } this.entityClass.getMethod(this.setterName(fields[i]), fieldsType[i]).invoke(entity, rs.getClass().getMethod(RS_METHOD_NAME.get(fieldsType[i].getSimpleName()), String.class).invoke(rs, columns[i])); } } return entity; }catch(Exception ex){ log.warn("通过结果集创建实体类" + this.entityClass.getName() + "对象时发生错误!"); return null; } } /** * 将字段名转换为方法名 * @param prefix 前缀 * @param fieldName 字段名 * @return 方法名 */ private String convertName(String prefix, String fieldName){ char[] tmp = fieldName.toCharArray(); tmp[0] = (tmp[0] + "").toUpperCase().charAt(0); return prefix + new String(tmp); } /** * 将Java命名规则转换成数据库中的命名规则 * @param name Java命名 * @return 数据库命名 */ private String convertName(String name){ Matcher m = p.matcher(name); StringBuffer t = new StringBuffer(); while(m.find()){ m.appendReplacement(t, "_$1"); } m.appendTail(t); //类名一般第一个字母大写,所以会多一个'_'符号,如果有符号则需要删除掉 if(t.charAt(0) == '_'){ t.deleteCharAt(0); } return t.toString().toUpperCase().replaceAll("_+", "_"); } /** * 初始化插入语句 */ private void createInsert(){ StringBuffer insert = new StringBuffer("insert into "); StringBuffer values = new StringBuffer("values("); insert.append(this.tableName); insert.append("("); for(int i = 0; i < this.columns.length; i++){ insert.append(this.columns[i]); insert.append(", "); values.append("?, "); } insert.delete(insert.length() - 2, insert.length()); values.delete(values.length() - 2, values.length()); this.insert = insert.append(") ").append(values).append(")").toString(); log.debug(this.entityClass.getName() + " insert SQL -> " + this.insert); } /** * 初始化删除语句 */ private void createDelete(){ StringBuilder delete = new StringBuilder("delete from "); delete.append(this.tableName); delete.append(" t where t.ID = ?"); this.delete = delete.toString(); log.debug(this.entityClass.getName() + " delete SQL -> " + this.delete); } /** * 初始化更新语句 */ private void createUpdate(){ StringBuilder update = new StringBuilder("update "); update.append(this.tableName); update.append(" t set "); for(int i = 0; i < this.columns.length; i++){ if(this.columns[i].equals("ID")){ continue; } update.append("t."); update.append(this.columns[i]); update.append(" = ?, "); } update.deleteCharAt(update.length() - 2); update.append("where t.ID = ?"); this.update = update.toString(); log.debug(this.entityClass.getName() + " update SQL -> " + this.update); } /** * 创建查询语句 */ private void createSelect(){ StringBuilder select = new StringBuilder("select "); for(int i = 0; i < this.columns.length; i++){ select.append("t."); select.append(this.columns[i]); select.append(", "); } select.deleteCharAt(select.length() - 2); select.append(" from "); select.append(this.tableName); select.append(" t"); this.all = select.toString(); select.append(" where t.ID = ?"); this.select = select.toString(); log.debug(this.entityClass.getName() + " all SQL -> " + this.all); log.debug(this.entityClass.getName() + " select SQL -> " + this.select); } /** * 通过类字段名取得get方法 * @param field 字段名 * @return get方法名 */ public String getterName(String field){ return this.gettersName.get(field); } /** * 通过类字段名取得set方法名 * @param field 字段名 * @return set方法名 */ public String setterName(String field){ return this.settersName.get(field); } /** * 通过类字段名取得数据库列明 * @param field 字段名 * @return 列名 */ public String getColumn(String field){ return this.fieldMapColumn.get(field); } /** * 通过数据库列明取得类字段名 * @param column 列明 * @return 字段名 */ public String getField(String column){ return this.columnMapField.get(column); } /** * @return the tableName */ public String getTableName() { return tableName; } /** * @return the entityClass */ public Class getEntityClass() { return entityClass; } /** * @return the delete */ public String getDelete() { return delete; } /** * @return the insert */ public String getInsert() { return insert; } /** * @return the select */ public String getSelect() { return select; } /** * @return the update */ public String getUpdate() { return update; } /** * @return the columns */ public String[] getColumns() { return columns; } /** * @return the fields */ public String[] getFields() { return fields; } /** * @return the mappedColumnCount */ public Integer getMappedColumnCount() { return mappedColumnCount; } /** * @return the all */ public String getAll() { return all; } /** * @return the fieldsType */ public Class[] getFieldsType() { return fieldsType; } } 4.GenericDaoDefault:简单扩展了一下Spring的JdbcDaoSupport,统一处理了几个比较令人厌恶的琐碎代码。 public class GenericDaoDefault<E extends Entity> extends JdbcDaoSupport implements GenericDao<E> { // 泛型类型 protected Class entityClass; // 实体描述类 private EntityMapper entity; // 删除order by字句使用的正则表达式 private static Pattern removeOrderByPattern = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE); // 分页处理器 private PaginationProcessor paginationProcessor; // 主键生成器 private IDGenerator iDGenerator; /** * 构造方法 */ public GenericDaoDefault(){ try{ this.entityClass = GenericUtils.getGenericClass(this.getClass()); if(this.entityClass != null){ this.entity = new EntityMapper(this.entityClass); } }catch(Exception ex){ logger.error("初始化" + this.getClass() + "失败", ex); } } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#executeUpdate(java.lang.String, java.lang.Object[]) */ public int update(String ql, Object... args) { if(logger.isDebugEnabled()){ logger.debug("Update SQL: \"" + ql + "\""); } return this.getJdbcTemplate().update(ql, args); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#delete(java.io.Serializable) */ public void delete(Serializable id) { this.update(this.entity.getDelete(), id); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#delete(com.funstool.platform.common.dao.Entity) */ public void delete(E entity) { this.delete(entity.getId()); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#get(java.io.Serializable) */ public E get(Serializable id) { try{ Connection conn = super.getConnection(); if(logger.isDebugEnabled()){ logger.debug("Select SQL: \"" + this.entity.getSelect() + "\""); } PreparedStatement ps = conn.prepareStatement(this.entity.getSelect()); ps.setObject(1, id); ResultSet rs = ps.executeQuery(); return (E)this.entity.createFromResultSet(rs); } catch(SQLException ex) { logger.warn("获取实体错误", ex); return null; } } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#get(java.lang.String, com.funstool.platform.common.dao.Page, java.lang.Object[]) */ public List query(String ql, Page page, Object... args) { ql = (String)this.paginationProcessor.analyse(ql, page); if(logger.isDebugEnabled()){ logger.debug("Query SQL: \"" + ql + "\""); } page.setRecord(super.getJdbcTemplate().queryForList(ql, args)); return page.getRecord(); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#get(java.lang.String, java.lang.Object[]) */ public List query(String ql, Object... args) { if(logger.isDebugEnabled()){ logger.debug("Query SQL: \"" + ql + "\""); } return super.getJdbcTemplate().queryForList(ql, args); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#getAll(com.funstool.platform.common.dao.Page, com.funstool.platform.common.dao.Sort[]) */ public List getAll(Page page, Sort... sorts) { String sql = null; if(sorts != null && sorts.length > 0){ StringBuilder s = new StringBuilder(this.entity.getAll()); s.append(" order by "); for(int i = 0; i < sorts.length; i++){ if(sorts[i] == null){ continue; } s.append(sorts[i].toString()); s.append(", "); } s.delete(s.length() - 2, s.length()); sql = (String)this.paginationProcessor.analyse(s.toString(), page); if(logger.isDebugEnabled()){ logger.debug("Select SQL: \"" + sql + "\""); } page.setRecord(super.getJdbcTemplate().queryForList(sql)); return page.getRecord(); } else { sql = (String)this.paginationProcessor.analyse(this.entity.getAll(), page); if(logger.isDebugEnabled()){ logger.debug("Select SQL: \"" + sql + "\""); } page.setRecord(super.getJdbcTemplate().queryForList(sql)); return page.getRecord(); } } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#save(java.lang.Object) */ public void save(E entity) { String[] fields = this.entity.getFields(); Object[] values = new Object[fields.length]; try{ if(entity.getId() == null){ entity.setId(iDGenerator.generate(super.getConnection())); for(int i = 0; i < fields.length; i++){ values[i] = this.entityClass.getMethod(this.entity.getterName(fields[i])).invoke(entity); } this.update(this.entity.getInsert(), values); } else { int idx = 0; for(int i = 0; i < fields.length; i++){ if(fields[i].equals("id")){ continue; } values[idx++] = this.entityClass.getMethod(this.entity.getterName(fields[i])).invoke(entity); } values[values.length - 1] = entity.getId(); this.update(this.entity.getUpdate(), values); } } catch(Exception ex) { logger.warn("保存实体错误", ex); throw new RuntimeException(ex); } } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#count(java.lang.String, java.lang.Object[]) */ public int count(String ql, Object... args) { String sql = "select count(*) " + removeSelect(this.removeOrderBy(ql)); if(logger.isDebugEnabled()){ logger.debug("Count SQL: \"" + sql + "\""); } return super.getJdbcTemplate().queryForInt(sql, args); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#deleteBatchByEntitiesId(java.util.List) */ public void deleteBatchByEntitiesId(Object[] entitiesId) { StringBuilder str = new StringBuilder("delete from "); str.append(this.entity.getTableName()); str.append(" t where t.id in ("); for(int i = 0; i < entitiesId.length; i++){ str.append("?,"); } str.deleteCharAt(str.length() - 1); str.append(")"); this.update(str.toString(), entitiesId); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#deleteBatchByEntities(com.funstool.platform.common.dao.EntityListWrapper) */ public void deleteBatchByEntities(List<E> entities) { StringBuilder str = new StringBuilder("delete from "); str.append(this.entity.getTableName()); str.append(" t where t.id in ("); for(int i = 0; i < entities.size(); i++){ str.append("?,"); } str.deleteCharAt(str.length() - 1); str.append(")"); if(!(entities instanceof EntityListWrapper)){ entities = new EntityListWrapper(entities); } this.update(str.toString(), ((EntityListWrapper)entities).getEntitiesId()); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#getAll(java.lang.Integer, java.lang.Integer, com.funstool.platform.common.dao.Sort[]) */ public List getAll(Integer pageSize, Integer pageIndex, Sort... sorts) { Page page = new Page(pageSize, pageIndex, false); return this.getAll(page, sorts); } /* * (non-Javadoc) * @see com.funstool.platform.common.dao.GenericDao#query(java.lang.String, java.lang.Integer, java.lang.Integer, java.lang.Object[]) */ public List query(String ql, Integer pageSize, Integer pageIndex, Object... args) { Page page = new Page(pageSize, pageIndex, false); return this.query(ql, page, args); } /** * 删除ql语句中的order by字句 * @param ql 查询语句 * @return 删除后的查询语句 */ private String removeOrderBy(String ql){ if(ql != null && !"".equals(ql)){ Matcher m = removeOrderByPattern.matcher(ql); StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, ""); } m.appendTail(sb); return sb.toString(); } return ""; } /** * 去除ql语句中的select子句 * @param ql 查询语句 * @return 删除后的语句 */ private String removeSelect(String ql) { Assert.hasText(ql); int beginPos = ql.toLowerCase().indexOf("from"); Assert.isTrue(beginPos != -1, " sql : " + ql + " must has a keyword 'from'"); return ql.substring(beginPos); } public void setPaginationProcessor(PaginationProcessor paginationProcessor) { this.paginationProcessor = paginationProcessor; } public void setIDGenerator(IDGenerator idGen) { this.iDGenerator = idGen; } } |
|
返回顶楼 | |
发表时间:2010-05-19
/** * 查找多条记录对象 * * @param sql * @param args * @param rowMapper * @return * @throws DaoException */ 这个方法弱弱的问一下楼主,如果是你写的一句SQL根据条件查询出来是100条,此时你如何确定你返回的List里面 就是100个元素? public List<Object> Query(String sql, Object[] args, RowMapper rowMapper) throws DaoException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; List<Object> results = new ArrayList<Object>(); try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); rs = ps.executeQuery(); Object obj = null; while (rs.next()) { obj = rowMapper.mapRow(rs); results.add(obj); } return results; } catch (SQLException e) { throw new DaoException(e.getMessage(), e); } finally { try { JDBCUtils.free(rs, ps, conn); } catch (SQLException e) { throw new DaoParameterException(e.getMessage(), e); } } } /** * 更新操作 * * @param sql * @param args * @param isGeneralKey * @throws DaoException */ update方法都是清一色的void试问下你如果判断是否更改成功或者是你更改的条数? 有时候update不成功,但是他也不一定会catch到Exception,你想修改的效果没有达到,但程序没任何异常咋办? public void update(String sql, Object[] args, boolean isGeneralKey) throws DaoException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.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 { try { JDBCUtils.free(rs, ps, conn); } catch (SQLException e) { throw new DaoParameterException(e.getMessage(), e); } } } } |
|
返回顶楼 | |
发表时间:2010-06-22
看了以后有种行云流水的感觉!!
2个字:流畅! |
|
返回顶楼 | |
发表时间:2010-06-22
代理量太大了,看得我头发晕!
spring在一定程度上会引起类的爆炸效应 一个不做j2ee的java程序员飘过。。。 |
|
返回顶楼 | |