最近由于公司要开发个报表系统,由于涉及到超大数据量的统计,频繁的sql语句,就没有在用hibernate,又重拾起了jdbc,在写jdbc助手类时自我感觉有个不错的建议和大家分享下:
上面的代码是个查询单个实例方法中的片段,可以看到除了填充bean(Admin类)外其他代码都是公用代码,可以放到jdbc助手类中,这就要解决不同bean的填充问题。
采用回调函数代替上面填充bean的部分是个可行的选择。
下面是具体的实现:
数据库中有个简单的users表,用于存放客户信息:
com.persist.User类是个实体类对应Users表:
package com.persist; import java.io.Serializable; import java.util.Date; public class User{ private int dbid; private String userName;//昵称 private String password;//登录密码 private String email;//邮箱 public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getDbid() { return dbid; } public void setDbid(int dbid) { this.dbid = dbid; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
com.dao.GenericDao类是数据访问层(dao)定义的jdbc助手类,用于完成数据访问的基本操作。find方法是获得单个实例的查询方法,其中会调用回调函数call()。代码最底端定义了内部接口PopulateCallable,用于完成回调操作,call是回调函数。
package com.dao; import java.io.Serializable; 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 javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; public class GenericDao<T> { /** * 查询单个实例 * @param sql * @param params * @param callback * @return * @throws SQLException * @throws NamingException */ protected T find(String sql, Object[] params, PopulateCallable<T> callback) throws SQLException, NamingException{ T entity = null; Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; try { con = this.getConnection(); stmt = con.prepareStatement(sql); setParams(stmt, params); rs = stmt.executeQuery(); while(rs.next()){ entity = callback.call(rs); } }finally{ closeResultSet(rs); closeStatement(stmt); closeConnection(con); } return entity; } /** * 获得数据库connection * @return * @throws SQLException * @throws NamingException */ protected Connection getConnection() throws SQLException, NamingException{ Context initContext = new InitialContext(); Context envContext = (Context)initContext.lookup("java:/comp/env"); DataSource ds = (DataSource)envContext.lookup("jdbc/myweb"); Connection con = ds.getConnection(); return con; } /** * 关闭ResultSet * @param rs */ public void closeResultSet(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 关闭Statement * @param stmt */ public void closeStatement(Statement stmt){ try { if(stmt != null){ stmt.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 归还连接到连接池 * @param con */ public void closeConnection(Connection con){ try { if(con != null && !con.isClosed()){ con.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 设置参数 * @param stmt * @param params * @throws SQLException */ public void setParams(PreparedStatement stmt, Object[] params) throws SQLException{ for(int i = 0; i<params.length;){//int, short, long, float, double, boolean, byte, char, List stmt.setObject(i+1, params[i]); i++; } } protected interface PopulateCallable<T> extends Serializable { T call(ResultSet rs)throws SQLException, NamingException; } }
com.dao.UserDao类负责User的的数据访问,继承自助手类com.dao.GenericDao:
package com.dao; import java.sql.ResultSet; import java.sql.SQLException; import javax.naming.NamingException; import com.persist.User; public class UserDao extends GenericDao<User>{ public User find(int dbid) throws SQLException, NamingException{ String sql = "select * from users t where t.dbid=?"; Object[] params = new Object[]{dbid}; User entity = super.find(sql, params, new GenericDao.PopulateCallable<User>(){ public User call(ResultSet rs) throws SQLException, NamingException{ User item = new User(); item.setDbid(rs.getInt("dbid")); item.setUserName(rs.getString("username")); item.setPassword(rs.getString("password")); item.setEmail(rs.getString("email")); return item; } }); return entity; } }
find方法负责从数据库中获得单个实例。这个方法是核心方法。其中在调用super.find()方法时,生成了PopulateCallable接口的匿名类,作为参数传入。回调函数call负责具体的封装bean的操作,由助手类com.dao.GenericDao类调用。
package com.service; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.naming.NamingException; import com.dao.GenericDao; import com.dao.UserDao; import com.persist.User; public class UserService { public User get(int dbid) throws SQLException, NamingException{ User entity = null; UserDao dao = new UserDao(); entity = dao.find(dbid); return entity; } }com.service.UserService类是个简单的业务类。
除了回调函数外,也可以使用反射,利用java.beans.BeanInfo类获得bean的所有属性,然后利用写属性进行填充。相比于采用回调而言,反射很难控制读取的数据库字段的数量,所以灵活性不如回调。