现在的位置: 首页 > 综合 > 正文

简单JDBC封装类

2013年08月02日 ⁄ 综合 ⁄ 共 12964字 ⁄ 字号 评论关闭

为什么要做这个封装类:

以前经常使用Hibernate来操作数据库,但是时间长了以后发现,Hibernate的大多数功能其实我根本就用不上,而且,Hibernate的查询执行效率的确很让人纠结
所以,我又回到了JDBC的怀抱,而我又是个懒人,喜欢一个函数就能直接调用的那种,JDBC本身方法对我来说,要打的字太多了...@_@

 

入正题:

首先,画一个结构的草图

 

包结构:

先贴代码:

1.IConnectionProvider 接口

package org.sol.util.db;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 用于获取连接的接口
 * @author SOL
 *
 */
public interface IConnectionProvider {
	public Connection getConnection(String sourceName) throws SQLException;
}

2.JDBCProvider JDBC数据提供源

package org.sol.util.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 标准JDBC连接获取代理
 * @author HUYAO
 *
 */
public class JdbcProvider implements IConnectionProvider {
	private String DBDriver;
	private String DBUrl;
	private String username;
	private String password;
	
	public JdbcProvider(String DBDriver,String DBUrl,String username,String password) throws ClassNotFoundException {
		this.DBDriver = DBDriver;
		this.DBUrl = DBUrl;
		this.username = username;
		this.password = password;
		
		Class.forName(DBDriver);
	}
	
	@Override
	public Connection getConnection(String sourceName) throws SQLException {
		return DriverManager.getConnection(DBUrl + ";DatabaseName=" + sourceName,username,password);
	}

	public String getDBUrl() {
		return DBUrl;
	}

	public void setDBUrl(String dBUrl) {
		DBUrl = dBUrl;
	}

	public String getDBDriver() {
		return DBDriver;
	}

	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}
	
}

3.Tomcat数据提供源

package org.sol.util.db;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
 * TOMCAT JNDI连接池获取代理
 * @author SOL
 *
 */
public class TomcatProvider implements IConnectionProvider {
	@Override
	public Connection getConnection(String sourceName) throws SQLException {
		try {
			Context ctx = new InitialContext();
			return ((DataSource)ctx.lookup("java:comp/env/" + sourceName)).getConnection();
		} catch (Exception e) {
			throw new SQLException(e);
		}
	}
}

4.DataConsole 功能封装类

package org.sol.util.db;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DataConsole {
	/**
	 * 连接
	 */
	private Connection connection;
	/**
	 * 预处理对象
	 */
	private PreparedStatement ps;
	/**
	 * 存储过程处理对象
	 */
	private CallableStatement cs;

	/**
	 * 结果集对象
	 */
	private ResultSet rs;
	/**
	 * 数据源
	 */
	private String sourceName;
	
	/**
	 * 连接获取代理
	 */
	private IConnectionProvider connectionProvider;

	/**
	 * 事务超时事件
	 */
	private int queryTime;
	
	private static final Log log = LogFactory.getLog(DataConsole.class);
	
	/**
	 * 生成一个JDBC封装对象
	 * @param connectionProvider 连接提供代理
	 * @param sourceName 数据源名称,connectionProvider根据此名称切换数据源
	 * @param queryTime 事务处理超时时间 0:无超时
	 */
	public DataConsole(IConnectionProvider connectionProvider,String sourceName,int queryTime) {
		this.connectionProvider = connectionProvider;
		this.sourceName = sourceName;
		this.queryTime = queryTime;
	}
	
	/**
	 * 统计语句查询 直接返回唯一值
	 * @param sql
	 * @param objs
	 * @return
	 * @throws SQLException
	 */
	public Object findReturn(String sql,Object... objs) throws SQLException {
		log.debug("Query return value:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));

		try {
			getConnection();
			
			ps = connection.prepareStatement(sql);
			ps.setQueryTimeout(queryTime);
			
			if(objs != null) {
				for(int i = 0 ; i < objs.length; i ++)
					ps.setObject(i + 1, objs[i]);
			}
			rs = ps.executeQuery();
			if(rs.next())
				return rs.getObject(1);
			else
				return null;
		} finally {
			close();
		}
	}
	
	/**
	 * 执行存储过程 首字段用于处理返回值 所以存储过程写法必须是 {?=call PRODUCENAME(?,?...,?)}
	 * @param call 存储过程
	 * @param returnType 返回参数类型 [Types.XXXX]
	 * @param objs 参数列表
	 * @return
	 * @throws SQLException
	 */
	public Object callWithReturn(String call,int returnType,Object... objs) throws SQLException {
		log.debug("Call return value:[" + call + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
		try {
			getConnection();

			cs = connection.prepareCall(call);
			cs.setQueryTimeout(queryTime);
			
			cs.registerOutParameter(1, returnType);
			for(int i = 0; i < objs.length; i ++)
				cs.setObject(i+2,objs[i]);
			
			cs.execute();
			return cs.getObject(1);
		} finally {
			close();
		}
	}
	
	/**
	 * 用于执行返回列表的存储过程  并映射到对象列表上
	 * @param <X> 
	 * @param clazz 映射对象
	 * @param sql 查询语句
	 * @param smap 映射配置表<字段名,类型>
	 * @param objs 参数列表
	 * @return
	 * @throws Exception
	 */
	public <X> List<X> call(Class<X> clazz,String sql,Map<String,Class<?>> smap,Object... objs) throws Exception {
		log.debug("Call Query produce:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
		try {
			rs = call(sql, objs);
			
			List<X> list = new ArrayList<X>();
			
			while(rs != null && rs.next()) {
				X obj = returnObject(clazz,smap);
				
				list.add(obj);
			}
			
			return list;
		} catch (Exception e) {
			close();
			throw e;
		} finally {
			close();
		}
	}
	
	private ResultSet call(String sql,Object... params) throws SQLException {
		getConnection();
		
		cs = connection.prepareCall(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
				ResultSet.CONCUR_READ_ONLY);
		cs.setQueryTimeout(queryTime);
		
		cs.registerOutParameter(1, Types.REAL);
		for(int i = 0; i < params.length; i ++)
			cs.setObject(i + 2, params[i]);
		
		return cs.executeQuery();
	}
	
	/**
	 * 查询单个对象 并映射到对象上
	 * @param <X> 
	 * @param clazz 映射对象
	 * @param sql 查询语句
	 * @param smap 映射配置表<字段名,类型>
	 * @param objs 参数列表
	 * @return
	 * @throws Exception
	 */
	public <X> X get(Class<X> clazz,String sql,Map<String,Class<?>> smap,Object... objs) throws SQLException {
		log.debug("Get Entity:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
		try {
			rs = query(sql, objs);
			
			if(rs != null && rs.next()) {
				X obj = returnObject(clazz,smap);
				
				return obj;
			} else {
				return null;
			}
			
		} catch (Exception e) {
			throw new SQLException(e);
		} finally {
			close();
		}
	}
	
	private ResultSet query(String sql,Object... objs) throws Exception {
		getConnection();

		ps = connection.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,
				ResultSet.CONCUR_READ_ONLY);
		ps.setQueryTimeout(queryTime);
		
		if(objs != null) {
			for(int i = 0; i < objs.length; i ++)
				ps.setObject(i + 1, objs[i]);
		}
		return ps.executeQuery();
	}
	/**
	 * 用于解析查询结果 并映射到对象
	 * @param <X>
	 * @param clazz 映射对象
	 * @param smap 映射表
	 * @return
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws SQLException
	 * @throws IllegalAccessException
	 */
	private <X> X returnObject(Class<X> clazz,Map<String,Class<?>> smap) throws InstantiationException,  SecurityException, NoSuchMethodException, SQLException, IllegalAccessException {
		X obj = clazz.newInstance();
		
		for(Entry<String,Class<?>> en : smap.entrySet()) {
			try {
				Object value = rs.getObject(en.getKey());
				
				setField(obj,en.getKey(),en.getValue(),(en.getValue().equals(String.class) ? (value != null ? value : null) : value));
			} catch (IllegalArgumentException e1) {
				log.error("不正确的对象映射. 映射类:" + clazz.getName() + 
						" 配置字段名:" + en.getKey() + 
						" 类型:" + en.getValue().getName() + 
						" 数据库字段类型:" + rs.getObject(en.getKey()).getClass().getName());
			} catch (InvocationTargetException e1) {
				e1.printStackTrace();
			}
		}
		
		return obj;
	}
	
	/**
	 * 设置对象上字段的值
	 * 现在只支持简单的对象类型 String Integer Short Double 等标准对象
	 * 可以扩展这个方法用来支持一些比较复杂的对象格式
	 * @param obj 映射的对象
	 * @param fieldname 字段名称 将调用它的set方法进行设置
	 * @param type 字段类型
	 * @param value 字段值
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	protected void setField(Object obj,String fieldname,Class<?> type,Object value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException  {
		Method method = obj.getClass().getMethod(setMethod(fieldname),type);
		method.invoke(obj, value);
	}

	/**
	 * 执行插入语句,并返回生成的主键
	 * @param sql 插入语句
	 * @param objs 参数列表
	 * @return 插入语句返回的主键值
	 * @throws SQLException
	 */
	public int insertAndReturnKey(String sql,Object... objs) throws SQLException {
		int countRow = 0;
		int key = 0;
		
		log.debug("Insert and return Id:[" + sql + "] " + (objs != null ? Arrays.deepToString(objs) : ""));
		try {
			getConnection();
			
			connection.setAutoCommit(false);

			ps = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
			ps.setQueryTimeout(queryTime);
			
			if(objs != null) {
				for(int i = 0; i < objs.length; i ++)
					ps.setObject(i+1,objs[i]);
			}
			
			countRow = ps.executeUpdate();
			if(countRow > 0) {
				ResultSet rs = ps.getGeneratedKeys();
				if(rs.next())
					key = rs.getInt(1);
			}
			connection.commit();
		} catch (SQLException e) {
			countRow = 0;
			connection.rollback();
			closeConnection();
			throw e;
		} finally {
			if (connection != null) {
				connection.setAutoCommit(true);
			}
			close();
		}
		return key;
	}
	
	/**
	 * 执行预编译SQL 
	 * @param sql SQL语句
	 * @param objs 参数列表
	 * @return 执行影响条数
	 * @throws SQLException
	 */
	public int updatePrepareSQL(String sql,Object... objs) throws SQLException {
		log.debug("Update:[" + sql + "]" + (objs != null ? objs.toString() : ""));
		
		int countRow = 0;
		try {
			getConnection();

			connection.setAutoCommit(false);

			ps = connection.prepareStatement(sql);
			ps.setQueryTimeout(queryTime);
			if(objs != null) {
				for(int i = 0; i < objs.length; i ++)
					ps.setObject(i+1,objs[i]);
			}
			countRow = ps.executeUpdate();
			connection.commit();
		} catch (SQLException e) {
			countRow = 0;
			connection.rollback();
			closeConnection();
			throw e;
		} finally {
			if (connection != null) {
				connection.setAutoCommit(true);
			}
			close();
		}
		return countRow;
	}

	public <X> List<X> find(String sql,Class<X> clazz,Map<String,Class<?>> smap,Object... objs) throws SQLException {
		try {
			log.debug("Query:" + sql + " Class:" + clazz.getName() + (objs != null ? Arrays.deepToString(objs) : ""));
			
			rs = query(sql, objs);
			
			List<X> list = new ArrayList<X>();
			
			while(rs != null && rs.next()) {
				X obj = returnObject(clazz,smap);
				
				list.add(obj);
			}
			
			return list;
		} catch (Exception e) {
			throw new SQLException(e);
		} finally {
			close();
		}
	}

	public void closeConnection() {
		try {
			if (connection != null) {
				connection.close();
			}
			connection = null;
		} catch (Exception e) {
		}
	}
	
	public Connection getConnection() throws SQLException {
		if(sourceName == null)
			throw new SQLException("没有设置数据源");
		
		int Times = 0;
		while (connection == null || connection.isClosed()) {
			try {
				closeConnection();
				connection = connectionProvider.getConnection(sourceName);
				break;
			} catch (Exception sqle) {
				log.error("error getConnection():" + sqle.getMessage(),sqle);
			}finally{
				if(Times>5){
					throw new SQLException("获取连接次数已经超过6次。不再尝试重新获取");
				}
				++Times;
			}
		}
		return connection;
	}
	
	public void close() {
		try {
			super.finalize();
			if (rs != null) {
				rs.close();
				rs = null;
			}
			if (ps != null) {
				ps.close();
				ps = null;
			}
			if (cs != null) {
				cs.close();
				cs = null;
			}
			if (connection != null) {
				connection.close();
				connection = null;
			}
		} catch (Throwable te) {
		}
	}

	public static Map<String,Class<?>> parseSmap(Class<?> clazz,String... paramNames) {
		Map<String,Class<?>> smap = new HashMap<String, Class<?>>(paramNames.length);
		
		for(String name : paramNames) {
			try {
				Field field = clazz.getDeclaredField(name);
				smap.put(name, field.getType());
			} catch (Exception e) {
				throw new RuntimeException(e);
			} 
		}
		
		return smap;
	}
	
	public static String getMethod(String name) {
		return "get" + name.replaceFirst(name.substring(0,1), name.substring(0,1).toUpperCase());
	}
	
	public static String setMethod(String name) {
		return "set" + name.replaceFirst(name.substring(0,1), name.substring(0,1).toUpperCase());
	}
}

简单介绍一下这个封装类

封装类通过ConnectionProvider获取相应的数据源类型,提供几个快速使用的JDBC方法,包括查询,更新语句,插入并返回主键,调用存储过程等几个常用方法,可以看源文件里的说明

用一个测试类来说明吧

package org.sol.util.db;

import java.sql.SQLException;
import java.sql.Types;

public class test {
	private IConnectionProvider connectionProvider = null;
	private String sourceName = "megajoysms";
	private int queryTime = 5;
	
	private DataConsole dc;
	
	public test() {
		/*
		 * 首先建立数据源对象,现在演示的使用的是JDBC提供源
		 * 三个参数 1.驱动名称 2.url(数据库名不需要填写,由sourceName参数执行) 3.登录用户名 4.密码
		 * 代码中还提供了一个tomcat的jndi数据提供源,其他类似的源可以直接继承IConnectionProvider接口实现
		 */
		try {
			connectionProvider = new JdbcProvider(
					"com.microsoft.sqlserver.jdbc.SQLServerDriver", 
					"jdbc:sqlserver://127.0.0.1:1433",
					"sa","123456");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		dc = new DataConsole(connectionProvider, sourceName, queryTime);
	}
	
	/**
	 * Find 方法用来查询对象
	 * 接收3+ 个参数
	 * 第一个参数表示执行的Select语句
	 * 第二个参数表示查询结果映射到一个对象类型上
	 * 第三个参数表示对查询结果字段的映射关系
	 * 第四个字段用于如果SQL语句是带参数的,从这里开始填入变量列表
	 * 
	 * 数据表test结构
	 * int id,
	 * varchar text,
	 * varchar value
	 * 映射对象结构
	 * class pojo {
		private Integer id;
		private String text;
		get/set 方法...
		}
	 * 第三个参数的类型是Map<String,Class<?>>表示 字段名-字段类型的映射关系
	 * 可以自己建立这个map,map的键用来匹配查询结果集的字段及映射类的字段
	 * 也可以用DataConsole.parseSmap(Class,String...)方法获取map
	 * 这个函数接收两个参数,第一个参数表示映射的类型,第二个开始的参数表示字段名称,函数会在映射类中匹配相应的字段类型
	 * PS:映射类内部的字段类型不能是基础类型比如int,short,要写成Integer,Short
	 */
	public void find() {
		try {
			System.out.println(dc.find(
					"select top 10 * from test where id>?",
					pojo.class,
					DataConsole.parseSmap(pojo.class, "id","whichservice"), 
					10));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 执行更新语句的方法
	 * 接收1+个参数
	 * 第一个参数表示SQL更新语句
	 * 第二个以后的参数表示传入的变量列表,无参数语句不填这些参数
	 * 返回结果是查询影响的记录条数
	 */
	public void update() {
		try {
			System.out.println(dc.updatePrepareSQL(
					"insert into test(id,text,value) values(?,?,?)", 
					1,"text","value"));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 执行插入动作,并返回这个操作记录的主键,如果操作无主键或主键不是自增型,返回0
	 * 参数类似于更新动作,这里演示了没有sql变量的情况
	 */
	public void insertAndReturnKey() {
		try {
			System.out.println(dc.insertAndReturnKey("insert into test(text,value) values('text','value')"));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 类似于执行select语句,此方法用于执行带有返回结果集的存储过程
	 */
	public void callAndReturnResultSet() {
		try {
			System.out.println(dc.call(pojo.class, 
					"{call proc_returnpojo(?)}", 
					DataConsole.parseSmap(pojo.class, "id","whichservice"), 
					1));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 用于执行存储过程,如果存储过程带有返回值(非output参数),返回接受到的返回值
	 *  首字段用于处理返回值 所以存储过程写法必须是 {?=call PRODUCENAME(?,?...,?)}
	 * 比如:
	 * create proc_testreturn
	 * begin
	 * return 1
	 * end
	 */
	public void callAndReturn() {
		try {
			System.out.println(dc.callWithReturn("{?=call proc_testreturn}", Types.INTEGER));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

其中用到的pojo类的定义:

package org.sol.util.db;

public class pojo {
	private Integer id;
	private String text;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
	
	@Override
	public String toString() {
		return "pojo [" + (id != null ? "id=" + id + ", " : "")
				+ (text != null ? "text=" + text : "") + "]";
	}
	
}

 

抱歉!评论已关闭.