项目中使用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); } }