各位大牛,早上好,有个问题一直在困扰我,希望能得到解答。
我在开发一个项目,没有使用框架,技术选型使用的是Servlet和JSP,与数据库通信使用的JDBC,就这么简单。
架构使用的三层架构与MVC,页面使用JSP,控制器Servlet,模型是Service,Service持有Dao层的对象,写有一个DBHelper:
public class DBHelper { public static ComboPooledDataSource cpds = null; static{ cpds = new ComboPooledDataSource(); try { cpds.setDriverClass("com.mysql.jdbc.Driver"); cpds.setJdbcUrl("jdbc:mysql:///xin"); cpds.setUser("root"); cpds.setPassword("java"); cpds.setMaxPoolSize(20); cpds.setMinPoolSize(3); cpds.setInitialPoolSize(5); } catch (PropertyVetoException e) { e.printStackTrace(); } } public static Connection getConnection(){ try { if(cpds != null){ return cpds.getConnection(); } } catch (SQLException e) { e.printStackTrace(); } return null; } private DBHelper(){ } /** * 关闭资源 * @param con 数据库连接 * @param rs 结果集 * @param ps 卡车 */ public static void close(Connection con,ResultSet rs, PreparedStatement ps){ try { if(ps != null){ ps.close(); ps = null; } } catch (SQLException e) { e.printStackTrace(); }finally{ try { if(rs != null){ rs.close(); rs = null; } } catch (SQLException e) { e.printStackTrace(); }finally{ try { if(con != null){ con.close(); con = null; } } catch (SQLException e) { e.printStackTrace(); } } } } /** * 执行增删改SQL语句 * @param sql * @param params * @return */ public static int executeUpdate(StringBuilder sql, Object...params){ PreparedStatement ps = null; Connection con = null; int result = 0; try { con = getConnection(); ps = con.prepareStatement(sql.toString()); for(int i = 0; i < params.length ; i ++){ ps.setObject(i + 1, params[i]); } result = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally{ close(con, null, ps); } return result; } /** * 执行查询的SQL语句 * @param sql * @param params * @return */ public static ResultSet executeQuery(Connection conn, StringBuilder sql, Object...params){ PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql.toString()); for(int i = 0; i < params.length ; i ++){ ps.setObject(i + 1, params[i]); } rs = ps.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } return rs; } }
比如我有两个类,它们之间是有主外键关系的:
public class Dept { private int id; private List<Employee> list; public int getId() { return id; } public void setId(int id) { this.id = id; } public List<Employee> getList() { return list; } public void setList(List<Employee> list) { this.list = list; } }
public class Employee { private int id; private String name; private Dept dept; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } }
public class EmployeeDao { /** * 根据员工的Id来查询对应的员工对象 * @param id * @return */ public Employee find(int id){ Connection conn = null; ResultSet rs = null; Employee e = null; try { StringBuilder sql = new StringBuilder("select * from t_employee where id = ?;"); conn = DBHelper.getConnection(); rs = DBHelper.executeQuery(conn, sql, id); if(rs.next()){ e = new Employee(); e.setId(rs.getInt("id")); e.setName(rs.getString("name")); //外键对象的查询 e.setDept(new DeptDao().find(rs.getInt("dept_Id"))); } } catch (SQLException ex) { ex.printStackTrace(); } finally{ DBHelper.close(conn, rs, null); } return e; } /** * 根部部门Id来查询对应的部门下的所有员工 * @param id * @return */ public List<Employee> findAll(int id){ Connection conn = null; ResultSet rs = null; List<Employee> lists = new ArrayList<Employee>(); try { StringBuilder sql = new StringBuilder("select * from t_employee where dept_Id = ?;"); conn = DBHelper.getConnection(); rs = DBHelper.executeQuery(conn, sql, id); Employee e = null; while(rs.next()){ e = new Employee(); e.setId(rs.getInt("id")); e.setName(rs.getString("name")); //外键对象的查询 e.setDept(new DeptDao().find(rs.getInt("dept_Id"))); lists.add(e); } } catch (SQLException ex) { ex.printStackTrace(); } finally{ DBHelper.close(conn, rs, null); } return lists; } }这是部门Dao:
public class DeptDao { /** * 根据部门的Id查询对应的部门对象 * @param id * @return */ public Dept find(int id){ Connection conn = null; ResultSet rs = null; Dept d = null; try { StringBuilder sql = new StringBuilder("select * from t_dept where id = ?;"); conn = DBHelper.getConnection(); rs = DBHelper.executeQuery(conn, sql, id); if(rs.next()){ d = new Dept(); d.setId(rs.getInt("id")); d.setName(rs.getString("name")); //装载一对多的集合 d.setList(new EmployeeDao().findAll(rs.getInt("id"))); } } catch (SQLException e) { e.printStackTrace(); } finally{ DBHelper.close(conn, rs, null); } return d; } }这样写的Dao,在service层调用有这么几个问题:
首选就是死循环,这个我可以解决,让部门的Dao不去加载对应的员工对象集合就可以了。
其次就是连接中途被关闭的问题,比如,我调用查询单个员工的方法,由于员工对象在装载的过程中需要调用部门的Dao查询部门,而在部门的Dao中关闭了连接,这样会倒置员工对象继续查询报错。
最后我使用了数据库连接池,也解决了这个问题,但是我总感觉这不是良好的设计!!!
在这种场景下应该怎么去设计这些东西呢?
我有个思路:
组合查询,也就是多写几个Dao,有装载外键对象的,有不装载的,装载的使用组合查询,不知这样好是不好!
分开两个类来处理。
Entity:和数据库中字段一一对应,比如你的员工和部门,是多对一关系,
所以员工类里面有一个deptId即可。如果是多对多关系那就建立一个中间表的类。
操作Entity中的字段就相当于操作数据库的字段。
VO:用来做显示,和界面上的元素一一对应。
UserVO{
privateintid;
privateStringdeptId;
privateStringdeptName;
}
差不多是这样,传入到Dao层的是Entity,传出的是VO,通过关联查询的SQL语句为VO赋值,传递回界面。
你那种做法可能是用惯了hibernate的做法,不知道能不能理解。
最好的设计--去掉pojo和DAO
现在可能无法理解
慢慢就理解了
您的意思是不要考虑这些东西,直接考虑代码与数据库的关系是吗?拼接SQL,然后直接使用DBHelper执行?如果使用pojo与Dao,就要使用Hibernate是吗?最纯粹的才是最简便的,可惜我的场景就是这样的,头疼!分开两个类来处理。
Entity:和数据库中字段一一对应,比如你的员工和部门,是多对一关系,
所以员工类里面有一个deptId即可。如果是多对多关系那就建立一个中间表的类。
操作Entity中的字段就相当于操作数据库的字段。
VO:用来做显示,和界面上的元素一一对应。
UserVO{
privateintid;
privateStringdeptId;
privateStringdeptName;
}
差不多是这样,传入到Dao层的是Entity,传出的是VO,通过关联查询的SQL语句为VO赋值,传递回界面。
你那种做法可能是用惯了hibernate的做法,不知道能不能理解。
看了看楼主的设计,感觉挺合理的啊-。-有一个问题,不知楼主能否帮忙解答
为什么 executeUpdate(StringBuildersql,Object...params)没有传入Connection对象,而 executeQuery(Connectionconn,StringBuildersql,Object...params)传入了呢?
这样设计的道理是什么?
这个很简单,1对多在多的一方设置一个外键字段,为了实现实体查询方便、添加一个List临时字段,查询的时候多的可以查询放入该List即可同理其他1对1多对多也一样,hibernate也是这样思想的嗯,你说的思路我明白,如果这样做,就要自己写一个类似Hibernate的懒加载机制,不然会死循环的看了看楼主的设计,感觉挺合理的啊-。-有一个问题,不知楼主能否帮忙解答
为什么 executeUpdate(StringBuildersql,Object...params)没有传入Connection对象,而 executeQuery(Connectionconn,StringBuildersql,Object...params)传入了呢?
这样设计的道理是什么?
明白,我现在数据库的dao设计都是采用了楼主这样的设计。不过executeUpdate也传人了,因为这样可以在dao层同一connection进行事物控制,禁用autoCommit,等事物完成再提交。是啊,你说的很对,所以后续扩展了这个DBHelper类,重载了几个方法,也加入了执行事务的方法,执行存储过程的方法等等。版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。