在上面的例子中,我们使用xml加载Mapper文件,在这一篇博客中,我们使用@Mapper注解加载sql映射
1 示例
我们修改Mybatis源码学习(一)中的代码,红色为修改部分:
MyBatisMain.java
public class MybatisMain {
public static void main(String[] args) throws IOException
{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = blogMapper.query(101);
// Blog blog = sqlSession.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
System.out.println(blog.getContext());
}
}
新增接口BlogMapper.java
@Mapper
public interface BlogMapper {
@Select("select * from blog where id=#{id}")
Blog query(Integer id);
}
mybatis-config.xml
<configuration>
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper resource="mapper/BlogMapper.xml"/>-->
<mapper class="org.apache.ibatis.demo.mapper.BlogMapper"/>
</mappers>
</configuration>
2 分析
2.1 SqlSessionFactory
在 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 中,build方法中经过若干步骤,执行如下方法
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
try(InputStream inputStream = Resources.getUrlAsStream(url)){
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
当使用注解方式时,执行如下分支,configuration中添加Mapper接口对应的Class。
else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
}
2.2 通过BlogMapper接口查询
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = blogMapper.query(101);
第一行代码SqlSession的getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory的newInstance方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
这里使用了JDK的Proxy动态代理方式,Proxy.newProxyInstance接收3个参数,第一个参数是ClassLoader,第二个参数是被代理接口BlogMapper,第三个参数是实现了 InvocationHandler 接口的MapperProxy类的实例。(这是Proxy代理的规范)。
总而言之,当我们使用@Mapper注解方式时,Mybatis帮助我们使用动态代理的方式代理Mapper接口,来实现接口中所声明的查询方法。 Blog blog = blogMapper.query(101); 执行时实际上是调用的代理对象mapperProxy中的invoke方法。
本文介绍使用@Mapper注解配置MyBatis的方式,通过示例展示了如何定义接口及查询方法,并解析了其内部实现机制。
662

被折叠的 条评论
为什么被折叠?



