Jade Dungeon

DBUtils

基本方法

  1. DBUtils是JDBC的简单封装,可以和JDBC混合使用。
  2. DBUtils对结果集自动封装为JavaBean是有着苛刻要求的:必须满足JavaBean的规范,其次Bean的getter与setter方法的名字与结果集的列名一一对应,而不要求JavaBean的私有成员与表结果集列名一一对应。
  3. DBUtils可以将结果集封装为各种类型,主要有:Bean/List<Bean>Map/List<Map>/Map<Map>数组/List<数组>列/List<列>,这些类型。
  4. DBUtils执行插入操作的时候,无法返回自增主键,这是一个很严重的问题,当然不能怪DBUtils,可以通过变通的方法来实现,比如在MySQL中,执行完了一个插入SQL后,接着执行SELECT LAST_INSERT_ID()语句,就可以获取到自增主键。
  5. DBUtils的性能和JDBC性能是一样,测试过程中没发现性能损失,拥有了很高性能的同时,而不失JDBC的灵活性。

对于Map<Map>的类型使用KeyedHandler作为结果集处理器,内层的Map是列名-值对, 外层的Map是主键-内层Map的引用,但此处的主键不一定就是数据库的主键,可以随意指定, 比如:

ResultSetHandler h = new KeyedHandler("id");
Map found = (Map) queryRunner.query("select id, name, age from person", h);
Map jane = (Map) found.get(new Long(1)); // jane's id is 1
String janesName = (String) jane.get("name");
Integer janesAge = (Integer) jane.get("age");

DbUtils类

提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:

  • public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
  • public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLException。
  • public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
  • public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。

QueryRunner类

该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。 QueryRunner类提供了两个构造方法:

默认的构造方法: QueryRunner() 需要一个javax.sql.DataSource来作参数的构造方法。 QueryRunner(DataSource ds)

QueryRunner类

  • public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
  • public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource 方法中重新获得 Connection。
  • public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException: 执行一个不需要置换参数的查询操作。
  • public int update(Connection conn, String sql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
  • public int update(Connection conn, String sql) throws SQLException:用来执行一个不需要置换参数的更新操作。

ResultSetHandler接口

该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。

ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)

ResultSetHandler 接口的实现类

  • ArrayHandler( ):把结果集中的第一行数据转成对象数组。
  • ArrayListHandler( ):把结果集中的每一行数据都转成一个数组,再存放到List中。
  • BeanHandler(Class type):将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler(Class type):将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ColumnListHandler(int columnIndex / String columnName):将结果集中某一列的数据存放到List中。
  • KeyedHandler( int columnIndex / String columnName ):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,并将其columnName的值作为指定的key。
  • MapHandler( ):将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler( ):将结果集中的每一行数据都封装到一个Map里,然后再存放到List
  • ScalarHandler( ):将结果集中的某一列 装入到一个对象中。

简单例子

例子1:

package com.crm.db.base;

import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com.crm.domain.User;
import com.crm.util.Constants;

public class DBBase {

	private static DBBase dbBase;

	private static QueryRunner run ;

	private DataSource dataSource;
	public DataSource getDataSource() { return dataSource; }

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	private DBBase() {}

	private void init() {
		dbBase = this;
		run=new QueryRunner(dataSource);
	}

	public static DBBase getInstance() {
		return dbBase;
	}

	/**
	 * eg:
	 * select count(1) from user
	 *
	 * @param sql
	 * @param params
	 * @return
	 */
	public int count(String sql, Object[] params) {

		Object o = getAnAttr(sql,params);
		if(o instanceof Integer) {
			return (Integer) o;
		}
		if(o instanceof Long) {
			Long l = (Long)o;
			return l.intValue();
		}

		String s = (String)o;
		try{
			return Integer.parseInt(s);
		}catch (NumberFormatException e) {
			return 0;
		}
	}

	 /**
	  * 获得第一个查询第一行第一列
	  * @param sql
	  * @param params
	  * @return
	  */
	public Object getAnAttr(String sql, Object[] params) {

		showSql(sql);

		Object s=null;
		try {
			s = run.query(sql, new ScalarHandler(1),params);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return s;
	}

	/**
	 * 查询返回单个对象
	 * @param sql
	 * @param clazz
	 * @return
	 */
	public <T> T queryForObject(String sql,Object param[],Class<T> clazz) {		
		T obj = null;
		try {			
			showSql(sql);			
			obj = (T)run.query(sql,new BeanHandler(clazz), param);			
		} catch (SQLException e) {			
			e.printStackTrace();
		}
		return obj;
	}

	/**
	 * 查询返回list对象
	 * @param sql
	 * @param clazz
	 * @return
	 */
	public <T> List<T> queryForOList(String sql,Object[] param,Class<T> clazz) {
		List<T> obj = null;
		try {
			showSql(sql);
			obj = (List<T>)run.query(sql,new BeanListHandler(clazz),param);			
		} catch (SQLException e) {			
			e.printStackTrace();
		}
		return obj;
	}


	/**
	 * 保存返回主键
	 * @param sql
	 * @param param
	 * @return
	 */
	public int storeInfoAndGetGeneratedKey(String sql,Object[] param) {
		int pk=0;
		try {
			showSql(sql);
			run.update(sql,param);
			pk = ((Long)run.query("SELECT LAST_INSERT_ID()", new ScalarHandler(1))).intValue();
		}catch(SQLException e) {
			e.printStackTrace();
		}
		return pk;
	}

	/**
	 * 更新
	 * @param sql
	 * @return
	 */
	public int update(String sql,Object[] param) {
		int i=0;
		try {
			showSql(sql);
			i = run.update(sql,param);
		}catch(SQLException e) {
			e.printStackTrace();
		}
		return i;
	}
	
	private void showSql(String sql) {
		if(Constants.SHOW_SQL) {
			System.out.println(sql);
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String uri = "jdbc:mysql://localhost:3306/miccrm";	
		DataSource ds = setupDataSource(uri);
		DBBase db = new DBBase();
		db.setDataSource(ds);
		db.init();

		String sql = "select count(1) from user";	
		int i = db.count(sql,null);

		sql="select name from user";
		List<User> us = DBBase.getInstance().queryForOList(sql, null, User.class);		
		for(User u:us) {
			System.out.println(u.getName());
		}

		sql = "insert into user(name) values(?)";
		int pk = DBBase.getInstance().storeInfoAndGetGeneratedKey(sql, new Object[]{"a"});
		System.out.println(pk);

		sql ="select name from user where id =?";
		String a =(String) DBBase.getInstance().getAnAttr(sql, new Object[]{1});

		System.out.println(a);
	}


	private static DataSource setupDataSource(String connectURI) {
		BasicDataSource ds = new BasicDataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUsername("root");
		ds.setPassword("123456");
		ds.setUrl(connectURI);
		return ds;
	}

}

DBUtils与Spring结合

DBUtils和Spring可以一起使用。

用DBUtils实现DAO,让Spring管理Datasource和transaction, 把DBBase声明为Spring可管理的bean,把datasource注入到DBBase中。 可能要用构造函数注入,因为需要在构造函数中创建QueryRunner。

DBBase中所有包含数据库操作的函数都让它抛出Runtime Exception, 或者直接借用Spring的DataAccessException。

DataSource应该使用Spring的一个DataSource代理TransactionAwareDataSourceProxy:

<bean id="dataSourceTarget" scope="singleton"
	class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName">
		<value>oracle.jdbc.driver.OracleDriver</value>
	</property>
	<property name="url">
		<value>jdbc:oracle:thin:@localhost:1521:dbname</value>
	</property>
	<property name="username"><value>user_bask_app</value></property>
	<property name="password"><value>user_bask_app</value></property>
</bean>

<bean id="dataSource" scope="singleton"
 class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
	<constructor-arg>
		<ref bean="dataSourceTarget"/>
	</constructor-arg>
</bean>

<bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
	<constructor-arg>
		<ref bean="dataSource"/>
	</constructor-arg>
</bean>