各位大牛,早上好,有个问题一直在困扰我,希望能得到解答。
我在开发一个项目,没有使用框架,技术选型使用的是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类,重载了几个方法,也加入了执行事务的方法,执行存储过程的方法等等。