最近由于公司要开发个报表系统,由于涉及到超大数据量的统计,频繁的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的所有属性,然后利用写属性进行填充。相比于采用回调而言,反射很难控制读取的数据库字段的数量,所以灵活性不如回调。