MyBatis操作数据库使用Mapper接口和XML SQL定义,并没有创建接口实现类,MyBatis如何为Mapper接口生成代理对象的,MyBatis使用JDK proxy或Cglib生成一个代理对象,在org.apache.ibatis.session.SqlSession方法中扫描Mapper和XML完成对数据库操作。

本例通过两种方式演示MyBatis生成代理工作原理,
一是按照MyBatis规范设计Mapper接口和XML定义,自定义代理生成类MapperProxy,使用SqlSession触发方法。
java
TestMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hk.mapper.TestMapper">
<resultMap id="UserMap" type="com.hk.entity.UserInfo">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="login_id" property="loginId"/>
<result column="pwd" property="pwd"/>
<result column="version" property="version"/>
<result column="status" property="status"/>
<result column="org_name" property="orgName"/>
</resultMap>
<select id="getUserList" parameterType="String" resultType="com.hk.entity.UserInfo">
select u.* from user u
</select>
</mapper>
TestMapper.java
package com.hk.mybatis;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.hk.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TestMapper extends BaseMapper<UserInfo> {
public List<UserInfo> getUserList();
public List<UserInfo> getUserByName(@Param("name") String name);
}
MapperProxy.java
package com.hk.mybatis;
import org.apache.ibatis.session.SqlSession;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private SqlSession sqlSession;
private final Class<T> mapperInterface;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) { // Object 提供的toString、hashCode等不需要代理执行
return method.invoke(this, args);
} else {
return sqlSession.selectList(method.getName(), args); // 代理映射最终执行是 SqlSession
}
}
}
MapperProxyFactory.java
package com.hk.mybatis;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Proxy;
import java.util.Map;
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
// 注意:接口在扫描时通过ClassScanner.scanPackage()加载为了一个Java类
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy); // 传入类加载器、接口的类对象,代理目标类,创建 JDK 动态代理对象
}
}
MapperProxyFactoryTest.java
package com.hk.mybatis;
import com.hk.Starter;
import com.hk.entity.UserInfo;
import com.hk.mapper.UserMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Starter.class)
public class MapperProxyFactoryTest {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Test
public void test_MapperProxyFactory() {
MapperProxyFactory<TestMapper> factory = new MapperProxyFactory<>(TestMapper.class);
Map<String, String> xmlMap = new HashMap<>(); // 模拟 SqlSession
TestMapper userDao = factory.newInstance(sqlSessionFactory.openSession());
List<UserInfo> list = userDao.getUserList();
System.out.println(list);
}
}
二是模拟代理类,自定义SQL操作,绕开SqlSession
java
MapperProxy.java
package com.hk.mybatis;
import org.apache.ibatis.session.SqlSession;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private SqlSession sqlSession;
private final Class<T> mapperInterface;
private Map<String,String> xmlMap;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<String,String> xmlMap) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.xmlMap = xmlMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) { // Object 提供的toString、hashCode等不需要代理执行
return method.invoke(this, args);
} else {
String sql = xmlMap.get(method.getName());
return executeSql(sqlSession.getConnection(), sql);
//return sqlSession.selectList(method.getName(), args); // 代理映射最终执行是 SqlSession
}
}
public Object executeSql(Connection conn, String sql) throws Throwable
{
List rtn = new ArrayList();
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
while(rs.next())
{
Map m = new HashMap();
for(int i=1;i<=cols;i++)
{
String colName = rsmd.getColumnName(i);
String colVal = rs.getString(colName);
m.put(colName,colVal);
}
rtn.add(m);
}
return rtn.toArray();
}
}
MapperProxyFactory.java
package com.hk.mybatis;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Proxy;
import java.util.Map;
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance(SqlSession sqlSession, Map<String, String> xmlMap) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, xmlMap);
// 注意:接口在扫描时通过ClassScanner.scanPackage()加载为了一个Java类
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy); // 传入类加载器、接口的类对象,代理目标类,创建 JDK 动态代理对象
}
}
TestMapper.java
package com.hk.mybatis;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.hk.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TestMapper extends BaseMapper<UserInfo> {
public Object getUserList();
public Object getUserByName(@Param("name") String name);
}
MapperProxyFactoryTest.java
package com.hk.mybatis;
import com.hk.Starter;
import com.hk.entity.UserInfo;
import com.hk.mapper.UserMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Starter.class)
public class MapperProxyFactoryTest {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Test
public void test_MapperProxyFactory() {
MapperProxyFactory<TestMapper> factory = new MapperProxyFactory<>(TestMapper.class);
Map<String, String> xmlMap = new HashMap<>(); // 模拟 SqlSession
xmlMap.put("getUserList", "select u.* from user u");
xmlMap.put("getUserByLoginId", "模拟执行 Mapper.xml 中 SQL 语句的操作:查询用户信息");
TestMapper userDao = factory.newInstance(sqlSessionFactory.openSession(),xmlMap);
Object obj = userDao.getUserList();
Object[] userDim = (Object[])obj;
for(int row=0;row<userDim.length;row++)
{
Map map = (Map)userDim[row];
System.out.println(map);
}
}
}