MyBatis框架:第五章:源碼解析及Mapper接口方式的mybatis的增,刪,改,查實現(xiàn)
Mapper接口編程的命名習(xí)慣
Mapper接口方式的編程,需要先有一個接口。這個接口的命名一般是xxxxMapper。
比如:
User模塊的Mapper,接口命名為UserMapper。
Book模塊的Mapper,接口命名為BookMapper。
Mapper接口開發(fā)有四個開發(fā)規(guī)范必須遵守
1、對應(yīng)的mapper配置文件的namespace屬性值必須是Mapper接口的全類名。
2、Mapper接口中的方法名必須與mapper配置文件中對應(yīng)的id值相同。
3、Mapper接口的方法的參數(shù)類型必須與mapper配置文件中配置的parameterType類型匹配上
4、Mapper接口的方法返回值類型必須與mapper配置文件中配置的resultType 類型匹配上
提前準(zhǔn)備工作,準(zhǔn)備好的項目
在mybatis-config.xml中配置你的庫名我的是mybatis
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 修改數(shù)據(jù)庫的四個連接屬性 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 配置sql語句 的那個mapper配置文件 -->
<mappers>
<mapper resource="com/dao/UserMapper.xml"/>
</mappers>
</configuration>
UserMapper代碼:
public interface UserMapper {
// 保存用戶
public int saveUser(User user);
// 更新用戶
public int updateUser(User user);
// 根據(jù)id刪除用戶
public int deleteUserById(int id);
// 根據(jù)id搜索用戶
public User findUserById(int id);
// 搜索全部用戶
public List findUsers();
}
UserMapper.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.dao.UserMapper">
<!-- public Integer saveUser(User user); -->
<insert id="saveUser" parameterType="com.pojo.User">
insert into t_user(`last_name`,`sex`) values(#{lastName},#{sex})
</insert>
<!-- public Integer updateUser(User user); -->
<update id="updateUser" parameterType="com.pojo.User">
update t_user set last_name = #{lastName} , sex = #{sex} where id = #{id}
</update>
<!-- public Integer deleteUserById(Integer id); -->
<delete id="deleteUserById">
delete from t_user where id = #{id}
</delete>
<!-- public User findUserById(Integer id); -->
<select id="findUserById" resultType="com.pojo.User">
select id,last_name lastName,sex from t_user where id = #{id}
</select>
<!-- public List<User> findUsers(); -->
<select id="findUsers" resultType="com.pojo.User">
select id,last_name lastName,sex from t_user
</select>
</mapper>
測試類:
public class UserMapperTest {
static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
String url = "mybatis-config.xml";
// 讀取配置文件
InputStream inputStream = Resources.getResourceAsStream(url);
// 創(chuàng)建SqlSessionFactory對象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testSaveUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(0, "ddddd", 1);
userMapper.saveUser(user);
session.commit();
System.out.println(user);
} finally {
session.close();
}
}
@Test
public void testUpdateUser() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(4, "eeeee", 1);
userMapper.updateUser(user);
session.commit();
} finally {
session.close();
}
}
@Test
public void testDeleteUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.deleteUserById(4);
session.commit();
} finally {
session.close();
}
}
@Test
public void testFindUserById() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findUserById(1));
} finally {
session.close();
}
}
@Test
public void testFindUsers() {
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findUsers());
} finally {
session.close();
}
}
}
源碼解析
它是怎么工作的呢?拿測試類中查詢舉例
1.讀取配置文件mybatis-config.xml,通過Resources.getResourceAsStream(“mybatis-config.xml”);返回一個流對象InputStream
2.通過SqlSessionFactoryBuilder().build(流對象InputStream放進(jìn)來)來創(chuàng)建SqlSessionFactory對象
3.使用SqlSessionFactory對象打開一個session方法,sqlSessionFactory.openSession();獲取SqlSession對象
4.使用SqlSession調(diào)用getMapper()方法,我們進(jìn)入源碼查看該方法
@Override
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
我們之前放進(jìn)來的UserMapper.class對應(yīng)Class type,它作為 configuration.getMapper(type, this)的參數(shù)再次傳遞,我們進(jìn)入它的方法體內(nèi)
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
它再次被當(dāng)做參數(shù)和sqlSession一起傳遞,進(jìn)入它的方法體內(nèi),現(xiàn)在才是重點
@SuppressWarnings(“unchecked”)
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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這個類是Mapper接口的代理工廠,這個mapper的代理就是我們之前的UserMapper 的實現(xiàn)類
看看之前的代碼UserMapper userMapper = session.getMapper(UserMapper.class);
接著查看源碼return mapperProxyFactory.newInstance(sqlSession);通過newInstance方法它要創(chuàng)建UserMapper接口的實例了,反射有講newInstance()創(chuàng)建實例。
mapperProxyFactory.newInstance(sqlSession)進(jìn)入它的方法體內(nèi)
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
newInstance(mapperProxy)進(jìn)入它的方法體內(nèi)
@SuppressWarnings(“unchecked”)
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
Proxy.newProxyInstance()方法解析:用來new一個jdk動態(tài)代理的
看看里面的參數(shù) (mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy)
mapperInterface.getClassLoader():類加載器
new Class[] { mapperInterface }:interface com.dao.UserMapper
mapperProxy:
protected T newInstance(MapperProxy mapperProxy) {
進(jìn)入MapperProxy類中
public class MapperProxy implements InvocationHandler, Serializable {
可以看到MapperProxy實現(xiàn)了InvocationHandler
回到之前測試類的代碼:UserMapper userMapper = session.getMapper(UserMapper.class);
可以看到它已經(jīng)是mapper的代理,jdk動態(tài)代理,這樣可以解釋為什么我們沒有寫接口也可以用接口里的方法,因為它通過代理給你創(chuàng)建了一個實現(xiàn)類。
繼續(xù)看System.out.println(userMapper.findUsers());進(jìn)入方法體內(nèi)
@Override
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 t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
為什么會進(jìn)入到這個方法體內(nèi)呢?userMapper.findUsers()前面證明了userMapper是jdk動態(tài)代理,jdk動態(tài)代理在執(zhí)行任何方法時都會執(zhí)行InvocationHandler執(zhí)行里面的invoke方法
執(zhí)行 final MapperMethod mapperMethod = cachedMapperMethod(method);
可以看到它已經(jīng)找到了UserMapper接口中對應(yīng)的方法了再看
可以看到它找到了select標(biāo)簽,我們接口中的方法名和我們的id值是不是對應(yīng)上了,我們還可以深入的看看,select只有二種情況,一種是執(zhí)行selectOne,一種是執(zhí)行selectList.
當(dāng)我們執(zhí)行到return mapperMethod.execute(sqlSession, args);進(jìn)入它的方法體內(nèi)
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException(“Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException(“Mapper method '” + command.getName()
+ " attempted to return null from a method with a primitive return type (” + method.getReturnType() + “).”);
}
return result;
}
通過command.getType()判斷類型SELECT執(zhí)行
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
method.returnsVoid() 可以看到通過方法的返回值區(qū)別是執(zhí)行selectOne,是執(zhí)行selectList.
// 根據(jù)id搜索用戶
public User findUserById(int id);
// 搜索全部用戶
public List<User> findUsers();
method.returnsMany() 它是返回集合,就是返回多個,可以深入的看看
public boolean returnsMany() {
return returnsMany;
}
進(jìn)入方法體內(nèi)
public static class MethodSignature {
private final boolean returnsMany;
ctrl+f查找returnsMany
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
通過this.returnsMany =
(configuration.getObjectFactory().isCollection(this.returnType) ||
this.returnType.isArray());可以看到它是不是集合還是數(shù)組
如果是list集合進(jìn)入到result = executeForMany(sqlSession, args);進(jìn)入到它的方法體內(nèi)
private Object executeForMany(SqlSession sqlSession, Object[] args) {
List result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
可以看到它找到了SelectList
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
urnsMap() 是不是Map
method.returnsCursor() 是不是游標(biāo)
最后 result = sqlSession.selectOne(command.getName(), param);可以看到它找到了selectOne