在项目中经常要我们查询某张表的的部分字段,比如select emp_id,emp_name,...... from emp where xxx;诸如此类,在dao层的xml我们就可以这样定义:
<resultMap id="queryNameMap" type="java.util.Map">
<result column="emp_name" property="name" jdbcType="VARCHAR">
<result column="emp_id" property="id" jdbcType="INTEGER">
.........
</resultMap>
<select id="queryName" resultMap="queryNameMap">
select emp_name,emp_idfrom emp where xxx
</select>
dao接口层:
List<Map<String,Object>> queryName() throws Exception;
此时mybatis便会自动将查出来的每一行结果字段通过key value的形式返回出来,key就是我自己定义的属性name,value就是具体查到的属性,还有就是mybatis支持两种返回结果:
- <select id="queryName" resultMap="queryNameMap"> 后面要定义一个<resultMap="xx" type="xx"> 如上,一般type定义Map,这样可以只返回你真正需要的字段
- <select id="queryName" resultType="com........Dto.Stu"> 这样会返回你需要的字段和该实体类没有被映射的字段,比如age你没去查,但由于你返回的是整个实体类,会去映射,映射不上就引用类型就是null,基本类型就是0或者false。
那我们不禁好奇,到底是怎样封装的呢?其实也很简单,就是拿到Map中每个key值的字段和类型,然后与实体类的字段与类型进行比较,然后通过反射实现set方法,实现一个对象的封装,具体看下面的demo。
package 反射;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* @author Heian
* @time 19/09/26 10:29
* @description: 反射实现Map转换Dto实体类
*/
public class MapToDto {
public static Object MapConvertDto(Map<String,Object> map, Class<?> cl) throws Exception{
Object obj = cl.newInstance ();
Field[] fields = cl.getDeclaredFields ();
for (Field field : fields) {
//筛选实体类属性为private的
int num = field.getModifiers ();//返回修饰符
String s = Modifier.toString (num);//对类成员变量进行解码,有点类似于序列化和反序列化
if ("private".equals (s)){
String fieldName = field.getName ();
if (map.containsKey (fieldName)){
Class<?> fieldClass = map.get (fieldName).getClass ();//取得map中value的类型(基本数据类型存在自动装箱,此处需要转化,这是个坑切记)
Field f = null;
//因为每个基本数据类型的包装类比如Integer都会存在全局变量TYPE,所以可针对这点来判断是否是包装类
try {
f = fieldClass.getDeclaredField ("TYPE");
} catch (NoSuchFieldException e) {
//如果不存在此变量,则说明不是基本类型,do nothing
} catch (SecurityException e) {
e.printStackTrace ();
}
//取得map中Value的接口,防止传过来的是子类,而在实体类中成员中是父类,这里常用的就是ArrayList和List,所以下面就用这个举例
//比如 实体类是List map是ArrlyList 此时invoke就会报错,因为您传入的参数类型是子类,而实体是父类,参数类型不匹配
Class<?>[] interfaces = fieldClass.getInterfaces ();
Class<?> father = null;
for (Class<?> interf : interfaces) {
if (interf.equals (List.class)){
father = interf;
break;
}
}
if (null != father){
Method method = cl.getDeclaredMethod (getFidldFuncName (fieldName), father);//拿到方法
method.invoke (obj,map.get(fieldName));
}else {
if (f == null) {
Method method = cl.getDeclaredMethod (getFidldFuncName (fieldName), fieldClass);
method.invoke (obj,map.get(fieldName));//interfClass是fieldClass的父类
}else {
List<String> first = new ArrayList<> (Arrays.asList ("java.lang.Byte","java.lang.Short",
"java.lang.Integer","java.lang.Long","java.lang.Float","java.lang.Double","java.lang.Character"));
List<Class> second = new ArrayList<> (Arrays.asList (byte.class,short.class,int.class,long.class,
float.class,double.class,char.class));
for (int i = 0; i < first.size (); i++) {
if (first.get (i).equals (fieldClass.getName ())){
Method method = cl.getDeclaredMethod (getFidldFuncName (fieldName), second.get (i));
method.invoke (obj,map.get(fieldName));
break;
}
}
}
}
}
}
}
return obj;
}
public static String getFidldFuncName(String filedStr){
return "set" + filedStr.substring (0,1).toUpperCase () + filedStr.substring (1);
}
public static void main(String[] args) throws Exception {
Map<String,Object> map = new HashMap<> ();
map.put ("id",11);
map.put ("name","jack");
map.put ("list",new ArrayList(Arrays.asList ("1","2","3")));
Stu s = (Stu) MapConvertDto (map, Stu.class);
System.out.println (s);
}
}
class Stu{
private int id;
private String name;
private List<String> list;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
@Override
public String toString() {
return "Stu{" +
"id=" + id +
", name='" + name + '\'' +
", list=" + list +
'}';
}
}
备注:
- 此处需要注意,由于jdk1.5实现可基本数据类型的自动装箱与拆箱,如果你在实体类某个字段如id定义的是int类型,而Map中的key字段id是Integer则,调用获取方 cl.getDeclaredMethod (set方法名, 参数类型)则会抛出无法找到该方法而报错,因为你实体类方法存的是int,而你传来的确实integer,所以需要在手动拆箱。
- Field[] fields1 = cl.getFields () 与 Field[] fields = cl.getDeclaredFields (); 前者是获取某个类的公有的public字段,包含父类;后者是获取当前类的所有成员变量(public、private、protected)
-
cl.getMethod(String name, Class<?>... parameterTypes)与getDeclaredMethod(String name, Class<?>... parameterTypes),前者是获取公共的public,后者是获取所有的修饰符的方法