您的位置:首页 > 移动开发

MyBatis中Mapper接口映射到数据库原理分析

2017-05-17 23:13 525 查看
刚学习MyBatis,对于用@Mapper注解的接口能够完成POJO对象到数据库记录映射百般疑惑,一开始很纳闷为什么不需要定义Mapper接口的实现类就能完成这个过程。例如以下代码段中只定义了UserDAO这个接口,但是并不影响POJO对象到数据库的映射。查阅了相关的资料,稍有解惑。

@Mapper
public interface UserDAO {
String TABLE_NAME = "user";
String INSERT_FIELDS = "name, password, salt, head_url";
String SELECT_FIELDS = "id, name, password, salt, head_url";
@Insert({"insert into", TABLE_NAME, "(", INSERT_FIELDS, ") values(#{name}, #{password}, #{salt}, #{headUrl})"})
int addUser(User user);
}
public class InitDBTest {
@Autowired
UserDAO userDAO;

public void contextLoads() {
User user = new User();
user.setName("user1");
user.setPassword("");
user.setSalt("");
user.setHeadUrl("xxx.png");
userDAO.addUser(user);
}
}

1. MapperRegistry

程序启动之初MyBatis就创建了这个类的一个实例,它有一个HashMap类型的属性用于存储每个Mapper接口(key)和相应的MapperProxyFactory(value);另外有两个重要的方法getMapper()和addMapper(),分别用于获取和注册Mapper接口到这个HashMap中。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if(mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);//通过Mapper接口的type返回一个相应的mapperProxyFactory的一个实例

} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
public <T> void addMapper(Class<T> type) {
if(type.isInterface()) {
if(this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}

boolean loadCompleted = false;

try {
this.knownMappers.put(type, new MapperProxyFactory(type));//将这个Mapper接口“注册”
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if(!loadCompleted) {
this.knownMappers.remove(type);
}

}
}
}

2. MapperProxyFactory

正如它的名字mapper代理工厂“,这个类是一个生产mapper代理对象的工厂,而mapper代理对象负责代替mapper接口完成POJO到数据库的映射。在MapperProxyFactory中的两个重要方法如下,用于创建mapper代理类的一个实例。
protected T newInstance(MapperProxy<T> mapperProxy) {
//用了java的动态代理,获得真正的代理对象
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}

3. MapperProxy

通过调用特定的mapper代理对象的invoke()方法,实现到数据库的映射,实际上invoke()中又调用了MapperMethod中的execute方法,execute方法主要是用到sqlSession的insert、update、select、delete等,这部分就是我们都很熟悉的了。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}

4. MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if(SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if(SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
} else if(SqlCommandType.DELETE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
} else if(SqlCommandType.SELECT == this.command.getType()) {
if(this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if(this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if(this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if(this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
} else {
if(SqlCommandType.FLUSH != this.command.getType()) {
throw new BindingException("Unknown execution method for: " + this.command.getName());
}

回到最上面的demo,userDAO属性被注解为@Autowired,由spring完成bean的自动装配,其中spring会去获取一个MyBatis的mapperProxy代理对象作为这个bean,它是一个真正的对象而不是一个Interface类型,由这个代理人完成到数据库的映射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: