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]