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