1:mybatis首先构建SqlSessionFactory,这个工厂主要就是初始化与数据库操作有关的上下文信息。会收集xml中的配置,如environment,datasource,transactionManager,setting配置等等,而默认构建出来的是DefaultSqlSessionFactory。
在mybatis最重要的配置节点就是和sql执行相关的mappers配置节点:
mybatis的mappers节点下可配置mapper和package:
(1)如果配置package首先会扫描接口,将接口中的每一个方法扫描到,并获取注解,“接口方法的全名称”和对应的“mappedStatement”对象(mappedStatement实际就是sql配置节点各个属性封装成的java对象)放入strictMap中。
(2)如果配置了mapper则,resources属性,url属性,class属性只能选择一个,因为源码只允许写一个,否则会抛出异常。
mybatis实际生产sql的时候是有两种方式,一个是动态sql一个是静态sql:
(1)动态sql中mybatis解析器会有一个标识,如果开头是“${”,结尾是“}”,则认为是动态sql。
(2)如果是静态sql那么加入,mappedStatement中的sqlResource就是将“#{}”替换成了“?”占位符的sql样子。
2:当SqlSessionFactory创建完成以后就会通过openSession方法获得一个session,默认情况下也是获取了一个DefaultSqlSession,er该sqlsession并没有数据库打开链接之类的行为,而是存储了一些信息,比如“sql执行器”,“配置信息configuration”,以及事务是否自动提交。
tx =transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit);
3:核心方法通过getMapper获取实现了接口的代理对象,通过jdk动态代理方式。
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); } } protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
4:MapperProxy是动态代理的核心方法它继承自InvocationHandler,而获取对应的Mapper类以后实际执行的方法就是MapperProxy的invok方法
@Override 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); } @UsesJava7 private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); }
5 mybatis中除了代理对象通过invoke方法执行以外实际方法名对应的sqlResource以外,还有比较重要的就是参数解析,抛开实体对象参数和Map参数,因为这两种参数实际的值都有对应的key。
我们下面来说下普通多个参数的问题,如果配置sql如下select * from table where id=#{id} and name=#{name}。
如果在java1.7中,你的方法是这样的:select(String name,int age),那么在mybatis源码中参数只能按照顺序获取并获取到arg0,arg1这种样子,因为1.7中的反射并没有提供实际的参数名称,也就是在sql的#占位符中只能按照参数的顺序替换值,在1.8中反射可以拿到参数名,如果在1.7中使用@params("id")注解也是可以解决这个问题。
而在myBatis中提供一种额外的方式来获取参数内容,因为在基础反射条件下参数名称多变的缘故,会按照参数顺序在paramsMap中提供了param1,param2这样的参数信息并始终存在。
{ "arg0":"id1", "arg1":"name1", "param1":"id1", "param2":"name1", }
6 在myBatis中where条件后尽量不要使用${}这种动态的sql语句,而${}这种动态语句的实际作用是作用在sql 语句的其它位置,比如select结果集字段有哪些。
因为${}相当于动态替换会带来sql注入风险;如果使用#{}则可以变为参数化查询,底层源码实际调用了PreparedStatement去进行参数化处理,而sqlResource中的参数占位符
变成了“?”,如:select * from table where id=? and name=?