赞
踩
上一篇Blog我们详细分析了MyBatis的执行原理,我们可以感受到其实实际的方法执行是通过动态代理而非方法本身实现的,那么既然基于动态代理可以实现,那么基于注解的实现方式貌似看起来更加直观,因为注解是可以直接加在方法上的。本篇Blog来学习下如何通过注解进行MyBatis的实现。
因为我们使用了注解开发,所以PersonMapper文件就不需要了,而且我们知道其实该配置文件最终被获取的也是接口的全限定名,所以我们确保核心配置文件能找到该接口即可:
我们首先调整核心配置文件,注释掉之前的mapper文件引用,加入类的引用,需要注意两种引入方式如果引入同一个接口则不能并存:
我们调整一下注释:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <configuration> <!--导入properties文件--> <properties resource="properties/db.properties"/> <settings> <setting name="logImpl" value="log4j"/> </settings> <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="mappers/personMapper.xml"/>--> <mapper class="com.example.MyBatis.dao.mapper.PersonDao"/> </mappers> </configuration>
此时运行单元测试还是会报错,为什么呢,因为我们只引入了命名空间,并没有在任何方法上添加注释
我们把CRUD都尝试一下,因为效果和xml配置类似,只不过将xml中的sql语句直接转移到了方法上,所以效果不再展示:
package com.example.MyBatis.dao.mapper; import com.example.MyBatis.dao.model.Person; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Map; public interface PersonDao { @Select("select * from person") List<Person> getPersonList(); @Select("select * from person where password=#{password} and username = #{username}") Person getPersonByUsername(@Param("username")String username, @Param("password")String password); Person getPersonByMap(Map<String,Object> map); @Insert("insert into person (id,username,password,age,phone,email,hobby) values (#{id},#{username},#{password},#{age},#{phone},#{email},#{interests}") int addPerson(Person person); @Update(" update person set username=#{username},phone=#{phone} where id=#{id}") int updatePerson(Person person); @Delete(" delete from person where id = #{id}") int deletePerson(@Param("id")int id); List<Person> getPersonListLike(@Param("username")String username); List<Person> getPersonListPage(Map<String,Integer> map); }
测试效果就不再展示了,测试方法和之前是一样的,没有变化,这里我们只看获取时测试,通过这个来说明一个问题:
@Test
public void testGetPersonList(){
//1.获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.执行SQL
PersonDao personDao = sqlSession.getMapper(PersonDao.class);
List<Person> personList = personDao.getPersonList();
for (Person person : personList) {
System.out.println(person);
}
//关闭sqlSession
sqlSession.close();
}
返回结果如下,兴趣爱好返回为null,那是因为结果集的数据表字段和我们类的属性不匹配导致的。
所以其实注解开发方式是不支持结果集映射的。
和所有注解一样,添加注解后就一定需要对注解进行处理,在builder的时候,通过注解获取上下文的读取和使用如下:
private class AnnotationWrapper { private final Annotation annotation; private final String databaseId; private final SqlCommandType sqlCommandType; AnnotationWrapper(Annotation annotation) { this.annotation = annotation; if (annotation instanceof Select) { this.databaseId = ((Select)annotation).databaseId(); this.sqlCommandType = SqlCommandType.SELECT; } else if (annotation instanceof Update) { this.databaseId = ((Update)annotation).databaseId(); this.sqlCommandType = SqlCommandType.UPDATE; } else if (annotation instanceof Insert) { this.databaseId = ((Insert)annotation).databaseId(); this.sqlCommandType = SqlCommandType.INSERT; } else if (annotation instanceof Delete) { this.databaseId = ((Delete)annotation).databaseId(); this.sqlCommandType = SqlCommandType.DELETE; } else if (annotation instanceof SelectProvider) { this.databaseId = ((SelectProvider)annotation).databaseId(); this.sqlCommandType = SqlCommandType.SELECT; } else if (annotation instanceof UpdateProvider) { this.databaseId = ((UpdateProvider)annotation).databaseId(); this.sqlCommandType = SqlCommandType.UPDATE; } else if (annotation instanceof InsertProvider) { this.databaseId = ((InsertProvider)annotation).databaseId(); this.sqlCommandType = SqlCommandType.INSERT; } else if (annotation instanceof DeleteProvider) { this.databaseId = ((DeleteProvider)annotation).databaseId(); this.sqlCommandType = SqlCommandType.DELETE; } else { this.sqlCommandType = SqlCommandType.UNKNOWN; if (annotation instanceof Options) { this.databaseId = ((Options)annotation).databaseId(); } else if (annotation instanceof SelectKey) { this.databaseId = ((SelectKey)annotation).databaseId(); } else { this.databaseId = ""; } } } Annotation getAnnotation() { return this.annotation; } SqlCommandType getSqlCommandType() { return this.sqlCommandType; } String getDatabaseId() { return this.databaseId; } }
构建查询语句的时候就直接用注解里的参数和内容:
private SqlSource buildSqlSource(Annotation annotation, Class<?> parameterType, LanguageDriver languageDriver, Method method) {
if (annotation instanceof Select) {
return this.buildSqlSourceFromStrings(((Select)annotation).value(), parameterType, languageDriver);
} else if (annotation instanceof Update) {
return this.buildSqlSourceFromStrings(((Update)annotation).value(), parameterType, languageDriver);
} else if (annotation instanceof Insert) {
return this.buildSqlSourceFromStrings(((Insert)annotation).value(), parameterType, languageDriver);
} else if (annotation instanceof Delete) {
return this.buildSqlSourceFromStrings(((Delete)annotation).value(), parameterType, languageDriver);
} else {
return (SqlSource)(annotation instanceof SelectKey ? this.buildSqlSourceFromStrings(((SelectKey)annotation).statement(), parameterType, languageDriver) : new ProviderSqlSource(this.assistant.getConfiguration(), annotation, this.type, method));
}
}
上一篇Blog我们提到的执行原理中,执行具体的sql语句由从Mapper中获取对应方法调整为从当前方法上的注解获取配置sql内容,源码位置对应如下:
其实注解开发很简单,和xml的原理是一样的,都是在通过动态代理生成Mapper对象后进行方法调用,不同的是xml方式,需要通过绑定的方法id找到对应的内容进行执行,而注解方式则直接寻找当前方法上的注解进行执行。注解不支持结果集映射是个缺陷,而且如果代码量复杂在方法上打注解也不是一种好的方式,基于我们想要让代码和sql分离的原则,我还是更愿意使用xml方式,规整且低耦合,只有规模特别的小的项目才适合注解这种方式,注解方式就作为一个简单的了解吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。