5.14 mybatis之objectFactory详解


ObjectFactory是Mybatis中的对象工厂,mybatis每次创建mapper映射结果对象的实例时,都会使用对象工厂ObjectFactory实例来完成。ObjectFactory接口只有一个默认的实现,即DefaultObjectFactory,默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认的构造方法,要么在参数映射存在的时候通过参数构造方法来实例化,这也是为什么很多人只知道mapper映射结果对象对应的类必须默认的无参构造函数,因为对象工厂默认使用无参构造函数实例化结果对象,但其实对象工厂也支持用有参构造函数实例化对象。总结起来就一句话:ObjectFactory是用来创建结果对象的。

1. 案例一

抛开mybatis,单独看下ObjectFactory是如何使创建对象的。
比如下面例子,通过DefaultObjectFactory对象创建了一个list对象,并向list对象中添加2个参数。
所以抛开mybatis,即使有其他场景需要通过工厂方式创建对象的,也可以直接借用ObjectFactory来创建对象。

public void testObjectFactory1(){
    ObjectFactory objectFactory = new DefaultObjectFactory();
    List<String> list = objectFactory.create(List.class);
    list.add("AA");
    list.add("BB");
    System.out.println(list); 	//输出[AA, BB]
}

2. 案例二

上面一个案例是用ObjectFactory创建List(默认为ArrayList)的对象,默认为无参构造函数,那么如果要创建的对象是有参数构造函数的怎么办?
下面以创建Man对象为例,该类有一个有参构造函数对应的类如下所示

package com.lzj.bean;

public class Man {
    private Integer id;
    private String name;
    private Integer age;

    public Man(Integer id, String name, Integer age){
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Man{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

下面创建objectFactory工厂对象,利用工厂对象创建Man对象时,一定要传入构造函数对象的参数以及类型,这样才能方便通过反射实例化对象,示例如下所示

public void testObjectFactory2(){
    ObjectFactory objectFactory = new DefaultObjectFactory();
    List<Object> argsName = new ArrayList<>();
    argsName.add(1);
    argsName.add("lzj");
    argsName.add(20);
    List<Class<?>> argsClass = new ArrayList<>();
    argsClass.add(Integer.class);
    argsClass.add(String.class);
    argsClass.add(Integer.class);
    Man man = objectFactory.create(Man.class, argsClass, argsName);
    System.out.println(man);  //Man{id=1, name='lzj', age=20}
}

3. 案例三

前面2个案例都是脱离mybatis单纯的看ObjectFactory的用法,那么下面案例看下ObjectFacory是如何在mybatis中发挥威力的。首先看下mybatis中最简单的一个应用,就是查询car表中数据,以结果对象Map所示进行返回

car对应结构如下所示
在这里插入图片描述
对应的dao方法如下所示

package com.lzj.dao;

import java.util.Map;

public interface CarDao {
    public Map select3();
}

对应的mapper中SQL如下所示

<select id="select3" parameterType="String" resultType="Map">
    select * from car where name=#{name}
</select>

执行下面测试案例

public void sqlSessionTest2() throws SQLException {
    MybatisUtil2 mybatisUtil2 = new MybatisUtil2();
    mybatisUtil2.init();
    SqlSessionFactory factory = mybatisUtil2.getFactory();
    SqlSession sqlSession = factory.openSession(true);  //true表示自动提交
    Map car = sqlSession.selectOne("com.lzj.dao.CarDao.select3", "xiaoli");
    System.out.println(car);
    sqlSession.close();
}

输出结果如下所示,说明正常输出了结果对象Map中的数据为{name=xiaoli, brand=BYD}

Opening JDBC Connection
Created connection 1882349076.
==>  Preparing: select * from car where name=? 
==> Parameters: xiaoli(String)
<==    Columns: name, brand
<==        Row: xiaoli, BYD
<==      Total: 1
{name=xiaoli, brand=BYD}
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@70325e14]

假设mybatis中有一需求,查询下面car表返回Map结构,然后需要在返回的Map结果对象中需要插入一个标签,也即向Map结果对象中额外插入一条键值对(“title”,“CAR”)。那么就需要在上面的步骤中额外添加如下操作
首先创建自己的ObjectFactory对象,去满足在返回的结果对象Map中添加数据,如下所示创建MyObjectFactory,去覆写create方法,实现向结果对象中添加数据

package com.lzj.objectFactory;

import org.apache.ibatis.reflection.factory.DefaultObjectFactory;

import java.util.Map;

public class MyObjectFactory extends DefaultObjectFactory {
    @Override
    public <T> T create(Class<T> type) {
    	//如果结果对象为Map类型的,就像对象中添加title键值对
        if (Map.class.isAssignableFrom(type)){
            Map<String, Object> map = (Map<String, Object>) super.create(type);
            map.put("title", "CAR");
            return (T) map;
        }
        return super.create(type);
    }
}

创建好了MyObjectFactory,还需要在mybatis中配置MyObjectFactory,告诉mybatis后续通过MyObjectFactory来创建结果对象

<objectFactory type="com.lzj.objectFactory.MyObjectFactory"></objectFactory>

下面再运行上面的测试案例,结果输出如下所示,发向返回的Map中已经插入了键值对title=CAR

Opening JDBC Connection
Created connection 590646109.
==>  Preparing: select * from car where name=? 
==> Parameters: xiaoli(String)
<==    Columns: name, brand
<==        Row: xiaoli, BYD
<==      Total: 1
{name=xiaoli, title=CAR, brand=BYD}
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@23348b5d]

4. 案例四

其实ObjectFactory对象工厂还支持从外界向对象工厂中传递参数。那是如何应用的,还是以上面的案例为例,通过向对象工厂传递参数,然后把参数键值对插入到结果对象Map中
比如下面的例子,向MyObjectFactory对象工厂中传入了属性名为title,value值为CAR的属性

<objectFactory type="com.lzj.objectFactory.MyObjectFactory">
    <property name="title" value="CAR"/>
</objectFactory>

修改自定义的MyObjectFactory,把向对象工厂传递的属性插入的返回结果对象Map中,如下所示

package com.lzj.objectFactory;

import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import java.util.Map;
import java.util.Properties;

public class MyObjectFactory extends DefaultObjectFactory {

    private Properties properties;

    @Override
    public <T> T create(Class<T> type) {
        if (Map.class.isAssignableFrom(type)){
            Map<String, Object> map = (Map<String, Object>) super.create(type);
            map.put("title", properties.getProperty("title"));
            return (T) map;
        }
        return super.create(type);
    }

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}

再次执行上面测试案例,输出结果如下所示,返现传递给对象工厂的属性按目标插入到结果对象Map中。

Opening JDBC Connection
Created connection 590646109.
==>  Preparing: select * from car where name=? 
==> Parameters: xiaoli(String)
<==    Columns: name, brand
<==        Row: xiaoli, BYD
<==      Total: 1
{name=xiaoli, title=CAR, brand=BYD}
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@23348b5d]
  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值