mybatis与反射实现对象封装

        在项目中经常要我们查询某张表的的部分字段,比如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支持两种返回结果:

  1. <select id="queryName" resultMap="queryNameMap">   后面要定义一个<resultMap="xx"    type="xx"> 如上,一般type定义Map,这样可以只返回你真正需要的字段
  2. <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 +
                '}';
    }
}

备注:

  1. 此处需要注意,由于jdk1.5实现可基本数据类型的自动装箱与拆箱,如果你在实体类某个字段如id定义的是int类型,而Map中的key字段id是Integer则,调用获取方 cl.getDeclaredMethod (set方法名, 参数类型)则会抛出无法找到该方法而报错,因为你实体类方法存的是int,而你传来的确实integer,所以需要在手动拆箱。
  2. Field[] fields1 = cl.getFields ()  与  Field[] fields = cl.getDeclaredFields (); 前者是获取某个类的公有的public字段,包含父类;后者是获取当前类的所有成员变量(public、private、protected)
  3. cl.getMethod(String name, Class<?>... parameterTypes)与getDeclaredMethod(String name, Class<?>... parameterTypes),前者是获取公共的public,后者是获取所有的修饰符的方法

 

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值