mybatis中有两种操作数据库的方式:
第一种比较古老,在mapper配置文件中做好配置之后,直接通过sqlSession提供的接口来关联配置文件中的sql进行操作,具体操作如下:
mapper配置:
<select id="queryAllUsers" resultType="User">
select * from hwc_users
</select>
操作方式一:
List<User> userList = sqlSession.selectList("com.huwc.mapper.UserMapper.queryAllUsers");
for (User user : userList) {
System.out.println(user);
}
这种调用方式无需声明对应的mapper接口类,而是直接通过namespace和相应配置的id进行sql的定位,然后通过sqlSession的selectList/selectOne方法进行数据库的操作。
另外一种较为流行的数据库操作方式为:根据mapper配置文件中的namespace定义相应的mapper接口类,再根据mapper配置文件中相应配置的id在接口类中定义相应的接口方法,相当于将mapper接口类与mapper配置文件进行了关联,然后通过sqlSession的getMapper方法获取对应mapper接口的代理类,继而通过代理类中的相应方法的调用来进行数据库的操作。
操作方式二:
添加对应mapper接口:
public interface UserMapper {
public List<User> queryAllUsers();
}
调用方式:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.queryAllUsers();
for (User user : users) {
System.out.println(user);
}
第二种调用的方法通过接口类的声明,是的数据库处理的调用更加的优雅,去除了程序中的硬编码,但实际上生成的mapper接口的代理类,最后依然是执行相应的方式一中的处理来实现数据库的操作,就是底层依然是调用sqlSession的selectList方法。
mapper接口代理类的生成方式为jdk的动态代理,众所周知,jdk的动态代理中相应的处理过程是封装在InvocationHandler接口的实现中,在mybatis中的InvocationHandler则是MapperProxy类,此类中的invoke方法如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
可以看到,invoke方法中是调用MapperMethod中的execute方法,传入的参数为sqlSession和对应的参数,MapperMethod的execute方法代码如下:
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;
}
此execute方法中可以清楚看到sqlSession对应方法的调用。