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());
}

注意点

至少返回一个对象

@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;
}

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

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

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

返回指定对象

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

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

通过源代码发现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);

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

如果返回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;
		}
	});
}

或者使用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

使用了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;
		}
	}
}

上面有一个错误,就是如果对象继承了父类,就无法将值注入到父类的的属性中,因为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、获取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网站的反射信息。

你可能感兴趣的:(spring)