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类型,由这个代理人完成到数据库的映射。
@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类型,由这个代理人完成到数据库的映射。
相关文章推荐
- Mybatis--使用mybatis generator插件映射数据库,自动生成pojo对象,dao接口,mapper.xml文件的方法
- [Mybatis] Mapper接口原理分析
- Mybatis接口编程原理分析(一)
- mybatis mapper接口不用去实现就能处理业务原理
- SprignMVC+myBatis整合+mybatis源码分析+动态代理实现流程+如何根据mapper接口生成其实现类
- SSM框架搭建(三) 数据库创建和MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件
- 使用Mybatis生成工具自动生成实体类和对应的mapper映射文件以及接口文件
- mybatis -spring 集成映射原理 --分析
- 使用Mybatis-Generator逆向生成po,映射文件和mapper接口
- MyBatis中通过Mapper接口加载映射文件
- 简单三步快速学会使用Mybatis-Generator自动生成entity实体、dao接口以及mapper映射文件(postgre使用实例)
- MyBatis接口的简单实现原理分析
- Mybatis接口编程原理分析(一)
- Mybatis接口编程原理分析(三)
- 整合maven+mybatis+generator生成java自定义model实体类,dao接口和mapper映射文件
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- Mybatis接口编程原理分析(一)
- Java的MyBatis框架中Mapper映射配置的使用及原理解析
- Mybatis配置和接口映射原理
- Mybatis配置和接口映射原理