• Spring中的JdbcTemplate的使用


    在最近的一个工作中,为了简单方便我就是用了Spring自带的JdbcTemplate来访问数据库,我以为之前自己很熟练的掌握,后来才发现我太天真了,踩了很多坑。

    基本方法

    JdbcTemplate自带很多方法可以执行SQL语句,以下我主要列举,比较常用的方法

    //执行SQL,返回一个对象
    @Override
    public  T queryForObject(String sql, RowMapper rowMapper, Object... args)
    		 throws DataAccessException {
    	List results = query(sql, args, 
    			new RowMapperResultSetExtractor(rowMapper, 1));
    	return DataAccessUtils.requiredSingleResult(results);
    }
    
    //同上,不过多要传入返回值的对象的Class
    @Override
    public  T queryForObject(String sql, Class requiredType)
    		 throws DataAccessException {
    	return queryForObject(sql, getSingleColumnRowMapper(requiredType));
    }
    
    //执行SQL,返回一个Map对象
    @Override
    public Map queryForMap(String sql, Object... args)
    		 throws DataAccessException {
    	return queryForObject(sql, args, getColumnMapRowMapper());
    }
    
    //执行SQL,返回一个List对象
    @Override
    public  List query(String sql, Object[] args, RowMapper rowMapper) 
    		throws DataAccessException {
    	return query(sql, args, new RowMapperResultSetExtractor(rowMapper));
    }
    
    //执行SQL,返回一个List对象
    @Override
    @Override
    public  List queryForList(String sql, Class elementType, Object... args)
    	throws DataAccessException {
    	return query(sql, args, getSingleColumnRowMapper(elementType));
    }
    
    //执行一条SQL,主要用于更新、新增数据
    @Override
    public int update(String sql, Object... args) throws DataAccessException {
    	return update(sql, newArgPreparedStatementSetter(args));
    }
    
    //执行SQL,返回一个List对象,List里是Map对象
    @Override
    public List> queryForList(String sql, Object... args) 
    		throws DataAccessException {
    	return query(sql, args, getColumnMapRowMapper());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    注意点

    至少返回一个对象
    @Override
    public  T queryForObject(String sql, RowMapper rowMapper, Object... args)
    		 throws DataAccessException {
    	List results = query(sql, args, 
    			new RowMapperResultSetExtractor(rowMapper, 1));
    	return DataAccessUtils.requiredSingleResult(results);
    }
    
    public RowMapperResultSetExtractor(RowMapper rowMapper, int rowsExpected) {
    	Assert.notNull(rowMapper, "RowMapper is required");
    	this.rowMapper = rowMapper;
    	this.rowsExpected = rowsExpected;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    以上代码就是可以知道,必须返回一个对象,不能返回null,如果从数据库查找发现没有一条信息吻合就会报错,报以下的错误

    org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 0
    
    • 1

    所以如果不确定是否有信息,就使用queryqueryForList来避免错误。返回单对象一般会有这样的限制,如果自己不确定可以使用该方法的时候看一下源码,设置了1就代表必须要有一个值。

    返回指定对象

    JdbcTemplate里面这样的方法,就是可以传入了一个对象的Class值,就可以返回该对象的值,但是需要注意,它只支持基础类型

    //例如,它支持以下的写法
    public Integer getCourseCount(String sql){
    	return (Integer) jdbcTemplate.queryForObject(sql,java.lang.Integer.class);
    }
    
    • 1
    • 2
    • 3
    • 4

    通过源代码发现Class requiredType这个参数支持以下类型,源代码就是从primitiveWrapperTypeMap查找是否是一下类型:

    primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
    primitiveWrapperTypeMap.put(Byte.class, byte.class);
    primitiveWrapperTypeMap.put(Character.class, char.class);
    primitiveWrapperTypeMap.put(Double.class, double.class);
    primitiveWrapperTypeMap.put(Float.class, float.class);
    primitiveWrapperTypeMap.put(Integer.class, int.class);
    primitiveWrapperTypeMap.put(Long.class, long.class);
    primitiveWrapperTypeMap.put(Short.class, short.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果需要返回自定义对象就需要另外的方法:

    如果返回List,就如以下的案例

    public List getCourseList(String sql){
    	return jdbcTemplate.query(sql,new RowMapper(){
    		@Override
    		public Course mapRow(ResultSet rs, int rowNum) throws SQLException {
    			Integer id=rs.getInt("id");
    			String coursename=rs.getString("coursename");
    			//把数据封装到对象里
    			Course course=new Course();
    			course.setId(id);
    			course.setCoursename(coursename.trim());
    			return course;
    		}
    	});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    或者使用List>

    @Override
    public List listMyScore(Integer studentId) {
      String sql = "select g.score,c.className from grade g"
          + " left join teacher t on t.id=g.teacherId"
          + " left join student s on s.id=g.studentId"
          + " left join classname c on c.id=t.classNameId"
          + " where s.id="+studentId;
      List> list = jdbcTemplate.queryForList(sql);
      if(list!=null && list.size()>0){
        List ls = new ArrayList();
        for(int i=0;i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    使用了org.apache.commons.beanutils.BeanUtils把Map对象转换为自定义对象。

    后来为了方便起见我还自己写了一个RowMapper,来简化操作

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.sql.ResultSet;
    
    import org.springframework.jdbc.core.RowMapper;
    
    import com.lsb.exam.utils.StringUtils;
    
    public class MyRowMapper implements RowMapper {
    
    	Class cls;
    
    	public MyRowMapper(Class cls) {
    		this.cls = cls;
    	}
    
    	@Override
    	public T mapRow(ResultSet rs, int rowNum) {
    		try {
    			Field[] fields = cls.getDeclaredFields();
    
    			T obj = cls.newInstance();
    
    			// 获取所有的属性
    			for (Field field : fields) {
    				field.setAccessible(true);
    				if (field.getGenericType().toString().equals("class java.lang.Integer")) {
    					Method m = obj.getClass().getDeclaredMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.Integer.class);
    
    					m.invoke(obj, rs.getInt(field.getName()));
    				}
    				if (field.getGenericType().toString().equals("class java.lang.String")) {
    					Method m = obj.getClass().getDeclaredMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.String.class);
    					m.invoke(obj, rs.getString(field.getName()));
    				}
    				if (field.getGenericType().toString().equals("class java.util.Date")) {
    					Method m = obj.getClass().getDeclaredMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.util.Date.class);
    					m.invoke(obj, rs.getDate(field.getName()));
    				}
    			}
    			return obj;
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    上面有一个错误,就是如果对象继承了父类,就无法将值注入到父类的的属性中,因为cls.getDeclaredFields()无法获取父类的属性,所以我又改了一种方法

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import org.springframework.jdbc.core.RowMapper;
    
    import com.lsb.exam.utils.StringUtils;
    
    public class MyRowMapper implements RowMapper {
    
    	Class cls;
    
    	public MyRowMapper(Class cls) {
    		this.cls = cls;
    	}
    
    	@Override
    	public T mapRow(ResultSet rs, int rowNum) {
    		try {
    
    			T obj = cls.newInstance();
    			
    			//这个只能获取当前类的共有私有字段
    			//Field[] fields = cls.getDeclaredFields();
    			List list = new ArrayList<>();
    			
    			Class c = cls;
    			
    			//循环获取
    			while(c != null){
    				list.addAll(Arrays.asList(c.getDeclaredFields()));
    				c = c.getSuperclass();
    			}
    			
    			// 获取所有的属性
    			for (Field field : list) {
    				field.setAccessible(true);
    				if (field.getGenericType().toString().equals("class java.lang.Integer")) {
    					Method m = obj.getClass().getMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.Integer.class);
    
    					m.invoke(obj, rs.getInt(field.getName()));
    				}
    				if (field.getGenericType().toString().equals("class java.lang.String")) {
    					Method m = obj.getClass().getMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.lang.String.class);
    					m.invoke(obj, rs.getString(field.getName()));
    				}
    				if (field.getGenericType().toString().equals("class java.util.Date")) {
    					Method m = obj.getClass().getMethod(
    							"set" + StringUtils.firstChar2UpperCase(field.getName()), java.util.Date.class);
    					m.invoke(obj, rs.getDate(field.getName()));
    				}
    			}
    			return obj;
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    其实过程中,有一个反射的知识很重要,关于方法的介绍:

    1、获取class

    方法描述
    object.getClass()获取这个实例所属的class对象
    T.class通过类型获取所属class对象
    Class.forName()通过类路径获取class对象
    Class.getSuperclass()获取父类的class对象
    Class.getClasses()获取类内所有的公开的类,接口,枚举成员,以及它继承的成员(特指类)
    Class.getDeclaredClasses()通过类内显示声明的类,接口
    Class.getEnclosingClass()获取闭包类

    2、获取属性

    方法描述
    getDeclaredField(String name)获取指定字段(公有,私有),不包括父类字段
    getField(String name)获取指定字段(公有),包括父类字段
    getDelaredFields()获取所有类内显示声明的字段(公有,私有),不包括父类字段
    getFields()获取所有字段(公有),包括父类字段

    3、获取方法

    方法描述
    getDeclaredMethod(String name, Class … paramType)获取指定方法(公有,私有),不包括父类方法
    getMethod(String name, Class … paramType)获取指定方法(公有),包括父类方法
    getDeclaredMethods()获取所有声明方法(公有,私有),不包括父类方法
    getMethods()获取所有方法(公有),包括父类方法

    上面的信息可以访问Oracle网站的反射信息

  • 相关阅读:
    STM32物联网(封装AT指令进行TCP连接及数据的接收和发送)
    GBase8s数据库INTO table 子句
    【大数据入门核心技术-Zookeeper】(三)Zookeeper的选举机制和流程
    直击“三夏”生产:丰收喜报频传 夏播紧锣密鼓
    android studio启动报错
    FreeSWITCH对接vosk实现实时语音识别
    MVP 聚技站| 生成式 AI 系列 TW(五):今夜让我们來聊聊 LLMOp
    小谈设计模式(2)—简单工厂模式
    第三章作业【数据库原理】
    LINUX安装KDC服务
  • 原文地址:https://blog.csdn.net/flash_love/article/details/132730545