论坛首页 Java企业应用论坛

SpringJdbcTemplate操作Clob和Blob的通用类封装

浏览 7126 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-07-07  

        项目中使用SpringJdbcTemplate来操作数据库,简单方便实用(根据项目需求选择技术),目前用到Oracle10g数据库,由于其操作大文本使用Clob类型,故而研究了下jdbctemplate对clob和Blob的操作。  

       jdbctemplate对clob和blob的操作使用起来也很简单,网友提供很多实例和代码。例如:

http://hi.baidu.com/sileader/item/0b3335f512378fb731c19999

主要是利用jdbctemplate提供的两个类来操作:LobCreator和LobHandler,具体使用方法可参考该链接。

但是如果某个Bean属性字段太多的话,代码写起来将会很麻烦。是否可以提供一种通用的方法,让每个业务方法操作数据库Clob和Blob时候也可以像操作其他基本数据类型一样,一句代码就完成。例如插入和修改Bean:

public <T> void saveOrUpdate(String sql, T bean) {
	namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(bean));
}

 经过一些尝试和摸索,我使用反射和注解,写了一个通用类,可以操作查询、修改、添加,满足了项目中的需求。

1、定义了一个注解类,用于bean中属性上,主要是提供属性的数据库字段名以及其数据库类型。

/**
 * @description 
 * @author aokunsang
 * @date 2013-7-4
 */
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) 
@Documented
@Inherited
public @interface PmcColumn {
	/**
	 * 数据库中字段类型
	 * @return
	 */
	int type();
	/**
	 * 数据库中字段名
	 * @return
	 */
	String columnName();
	/**
	 * 查询时候是否忽略
	 * @return
	 */
	boolean ignore() default false;
}

 2、定义一个反射的工具类,反射操作属性字段的setter和getter方法注入和获取属性值、

/**
 * @description 对bean的反射操作
 * @author aokunsang
 * @date 2013-7-3
 */
public class BeanUtils {

	  /** 
     * @param obj 操作的对象 
     * @param att 操作的属性 
     * */
	public static Object getter(Object obj, String att) { 
        try { 
            Method method = obj.getClass().getMethod("get" + StringUtils.capitalize(att)); 
            return method.invoke(obj); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        }
		return null; 
    }
    /**
     * 注入数据
     * @param obj   类的实例
     * @param att   属性名
     * @param value 注入数据内容
     * @param type  返回的数据类型
     * 
     */
	public static void setter(Object obj, String att, Object value, Class<?> type) { 
        try { 
            Method method = obj.getClass().getMethod("set" + StringUtils.capitalize(att), type); 
            method.invoke(obj, value); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        }
    }
	
	/**
	 * 获取某个属性字段的ignore信息
	 * @param field
	 * @return
	 */
	public static boolean getPmcColumnIgnore(Field field){
		Annotation annotation = field.getAnnotation(PmcColumn.class);
		boolean ignore = false;
		if(annotation!=null){
			ignore = ((PmcColumn)annotation).ignore();
		}
		return ignore;
	}
	
	/**
	 * 获取某个属性的Type信息
	 * @param field
	 * @return
	 */
	public static int getPmcColumnType(Field field){
		Annotation annotation = field.getAnnotation(PmcColumn.class);
		int type = Types.VARCHAR;
		if(annotation!=null){
			type = ((PmcColumn)annotation).type();
		}
		return type;
	}
	
	/**
	 * 获取某个属性的数据库中字段名
	 * @param field
	 * @return
	 */
	public static String getPmcColumnName(Field field){
		Annotation annotation = field.getAnnotation(PmcColumn.class);
		String columnName = "";
		if(annotation!=null){
			columnName = ((PmcColumn)annotation).columnName();
		}
		return columnName;
	}
}

 3、定义一个对sql语句的转换类,sql语句进行转变,变成需要的字符串。

如:insert into t1 values(:a,:b,:c)  ---->   insert into t1(A,B,C) values(?,?,?);

或者update t1 set A=:a,B=:b where C=:c   ----> update t1 set A=?,B=? where C=?

/**
 * @description 工具类
 * @author aokunsang
 * @date 2013-1-9
 */
public class Util {

	/**
	 * 分割插入、修改的sql语句
	 * @param sql
	 * @param columnMaps   <属性名,数据库中字段名>
	 * @return
	 */
	public static Map<String,List<String>> spQuerysql(String sql,Map<String,String> columnMaps){
		if(StringUtils.isEmpty(sql)) return null;
		String _sql = sql + " ";
		String key = _sql.replaceAll(":(.+?),\\s*", "?,").replaceAll(":(.+?)[)]\\s*", "?)").replaceAll(":(.+?)\\s+", "? ");
		Map<String,List<String>> result = new HashMap<String, List<String>>();
		List<String> fieldList = new LinkedList<String>();
		Pattern pattern = Pattern.compile(":(.+?)[)|,|\\s*]");
		Matcher matcher = pattern.matcher(_sql);
		StringBuffer insertString = new StringBuffer();
		while(matcher.find()){
			String value = matcher.group(1);
			fieldList.add(value);
			insertString.append(columnMaps.get(value)+ ",");
		}
		if(fieldList.isEmpty() || key.equals(_sql)) return null;
		StringBuffer key_sb = new StringBuffer(key);
		if(!key.trim().matches("insert\\s*into\\s*[\\w|_]+?\\(.*?\\)\\s*values.*") && !key.trim().contains("update")){  //判断insert语句是否有数据库字段,比如:insert into t1(ID,NAME) 
			key_sb.insert(key_sb.indexOf("values")-1, "("+insertString.substring(0,insertString.length()-1)+")");
		}
		result.put(key_sb.toString(), fieldList);
		return result;
	}
}

 4、定义一个对clob和blob的通用类[[关键类],也可以说是已经存在的Dao类的扩展类。

/**
 * @description 增强版数据库操作类[添加对clob和blob的部分操作]
 * @author aokunsang
 * @date 2013-7-4
 */
public class StGenericDao extends GenericDao {
	
	/**
	 * 查询单条记录
	 * @param <T>
	 * @param sql
	 * @param clazz
	 * @param args
	 * @return
	 */
	public <T> T _find(String sql,final Class<T> clazz,Object... args){
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.queryForObject(sql,new RowMapper<T>(){
				@Override
				public T mapRow(ResultSet rs, int rownum) throws SQLException {
				    T bean = null;
					try {
						bean = clazz.newInstance();
						Field[] fields = clazz.getDeclaredFields();   //获取所有属性
						for(Field field : fields){
							if(field.getAnnotation(PmcColumn.class)==null) continue;
							if(BeanUtils.getPmcColumnIgnore(field)) continue;
							switch(BeanUtils.getPmcColumnType(field)) {  //如果Bean中增加了类型,需要在这里以及下面方法中添加case
								case Types.CLOB:{
									BeanUtils.setter(bean, field.getName(), lobHandler.getClobAsString(rs, BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.BLOB:{
									BeanUtils.setter(bean, field.getName(), lobHandler.getBlobAsBytes(rs, BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.TIMESTAMP:{
									BeanUtils.setter(bean, field.getName(),rs.getTimestamp(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.DATE:{
									BeanUtils.setter(bean, field.getName(),rs.getDate(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.INTEGER:{
									BeanUtils.setter(bean, field.getName(),rs.getInt(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.VARCHAR:{
									BeanUtils.setter(bean, field.getName(),rs.getString(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.NUMERIC:{
							 		BeanUtils.setter(bean, field.getName(),rs.getLong(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
								case Types.FLOAT:{
									BeanUtils.setter(bean, field.getName(),rs.getFloat(BeanUtils.getPmcColumnName(field)), field.getType());
									break;
								}
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
					return bean;
				}
			},args);
		} catch (EmptyResultDataAccessException e) {
			return null;
		} catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误saveOrUpdate()------", e);
		}
	}
	/**
	 * 添加或修改某条记录
	 * @param <T>
	 * @param sql
	 * @param bean
	 */
	public <T> void _saveOrUpdate(String sql,final T bean){
		final Map<String,Integer> columnTypes = new HashMap<String,Integer>();
		Map<String,String> columnMaps = new HashMap<String,String>();
		Field[] fields = bean.getClass().getDeclaredFields();   //获取所有属性
		for(Field field : fields){
			columnTypes.put(field.getName(), BeanUtils.getPmcColumnType(field));
			columnMaps.put(field.getName(), BeanUtils.getPmcColumnName(field));
		}
		final Map<String,List<String>> resultMap = Util.spQuerysql(sql,columnMaps);
		if(resultMap==null) throw new PmcRuntimeException(String.format("你提供的SQL语句有问题,请详细检查再次尝试运行。SQL:[s%]",sql));
		Object[] validSql = resultMap.keySet().toArray();   //改变后的Sql语句
		printSqlInfo(sql+"<<----->>"+validSql[0].toString(),"");   //打印改变前后的sql语句
		final List<String> fieldList = resultMap.get(validSql[0]);   //对应插入的bean字段
		jdbcTemplate.execute(validSql[0].toString(),new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) {
			@Override
			protected void setValues(PreparedStatement ps, LobCreator lobCreater)
					throws SQLException, DataAccessException {
				for(int i=0,length=fieldList.size();i<length;i++){
					String filedValue = fieldList.get(i);
					switch (columnTypes.get(filedValue)) {
						case Types.CLOB:{
							lobCreater.setClobAsString(ps, i+1, BeanUtils.getter(bean, filedValue)==null ? null : BeanUtils.getter(bean, filedValue).toString());
							break;
						}
						case Types.BLOB:{
							lobCreater.setBlobAsBytes(ps, i+1, BeanUtils.getter(bean, filedValue)==null ? null : (byte[])BeanUtils.getter(bean, filedValue));
							break;
						}
						case Types.TIMESTAMP:{
							ps.setTimestamp(i+1, BeanUtils.getter(bean, filedValue)==null ? null : (Timestamp)BeanUtils.getter(bean, filedValue));
							break;
						}
						case Types.DATE:{
							ps.setDate(i+1, BeanUtils.getter(bean, filedValue)==null ? null : (Date)BeanUtils.getter(bean, filedValue));
							break;
						}
						case Types.INTEGER:{
							ps.setInt(i+1, BeanUtils.getter(bean, filedValue)==null ? null : (Integer)BeanUtils.getter(bean, filedValue));
							break;
						}
						case Types.VARCHAR:{
							ps.setString(i+1, BeanUtils.getter(bean, filedValue)==null ? null : BeanUtils.getter(bean, filedValue).toString());
							break;
						}
						case Types.NUMERIC:{
							ps.setLong(i+1, BeanUtils.getter(bean, filedValue)==null ? null : (Long)BeanUtils.getter(bean, filedValue));
							break;
						}
						case Types.FLOAT:{
							ps.setFloat(i+1, BeanUtils.getter(bean, filedValue)==null ? null : (Float)BeanUtils.getter(bean, filedValue));
							break;
						}
					}
				}
			}
		});
	}
}

 5、对JDBC的配置文件修改,添加lobHandler类,并且注入到StGenericDao中。

  <!-- JDBC上传附件时候使用 -->
	 <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
     
     <!-- 默认数据源[showsql-是否显示sql语句] 如果需要操作clob和blob,则声明StGenericDao-->
     <bean id="genericDao" class="com.pmc.dwa.common.dao.StGenericDao">
     	<property name="simpleJdbcTemplate" ref="dataSource"></property>
     	<property name="showsql" value="true"></property>
     	<property name="lobHandler" ref="lobHandler"></property>
     </bean>

 如何使用以上方法,需要注意以下2个方面:

其一:如果某个Bean类中包括Clob和Blob字段,那么该Bean的字段类型需要加入PmcColumn注解。如:

/**
 * @description 
 * @author aokunsang
 * @date 2013-7-4
 */
public class TDemo implements Serializable{
	
	@PmcColumn(columnName="ID",type=Types.NUMERIC)
	private Long id;
	@PmcColumn(columnName="NAME",type=Types.VARCHAR)
	private String name;
	@PmcColumn(columnName="DEMO_PIC",type=Types.BLOB)
	private byte[] pic;       //BLOB类型,属性字段类型为byte[]
	@PmcColumn(columnName="CONTENT",type=Types.CLOB)
	private String content;   //CLOB类型,属性字段类型为String


	setter and  getter...
}

 其二:Service业务方法中注入StGenericdao类,使用其方法即可;当然如果你操作的Bean类没有Clob和Blob类型字段就无需写PmcColumn注解,注入IGenericDao接口也可以。

/**
 * @description 
 * @author aokunsang
 * @date 2013-7-4
 */
@Service
public class DemoServiceImpl implements IDemoService{
	
	@Resource(name="genericDao")
	private StGenericDao genericDao;

	public void saveDemo(TDemo demo){
		genericDao._saveOrUpdate("insert into tdemo values(:id,:name,:pic,:content)",demo);
	}
}
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics