1、启动mybtais
项目结构:
1.1 maven依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
1.2 配置文件
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>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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="dyzwj"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
mapper文件
<?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.dyzwj.mybatis.mapper.UserMapper" >
<select id="selectAll" resultType="java.util.Map" parameterType="java.util.Map">
select * from user where username = #{username} and age = #{age}
</select>
<!-- <cache eviction="" flushInterval=""></cache>-->
<select id="select" resultType="java.util.Map" parameterType="java.util.Map">
select * from user order by #{username}
</select>
</mapper>
1.3 实体类和mapper接口
实体类
public class User {
private String username;
private int age;
//省略getter setter方法
}
mapper接口
public interface UserMapper {
List<Map<String,Object>> selectAll(Map<String,Object> param);
// List<Map<String,Object>> selectAll(String username,String age);
List<Map<String,Object>> select(Map<String,Object> param);
}
1.4 启动类
public class Test1 {
public static void main(String[] args) throws Exception {
String resource = "mybatis.xml";
//加载mybatis配置文件
InputStream resourceAsStream = Test1.class.getResourceAsStream("/" + resource);
InputStream resourceAsStream1 = Test1.class.getClassLoader().getResourceAsStream(resource);
System.out.println(resourceAsStream);
System.out.println(resourceAsStream.hashCode());
System.out.println(resourceAsStream1);
// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
Configuration configuration = sqlSession.getConfiguration();
/**
* 通过动态代理 去帮我们执行sql
*/
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
Map<String,Object> param = new HashMap<>();
param.put("username","zwj");
param.put("age",22);
mapper.select(param);
List<Map<String, Object>> maps = mapper.selectAll(param);
//因为一级缓存 这里不会调用两次
// List<Map<String, Object>> maps1 = mapper.selectAll(param);
//因为二级缓存 这里不会调用两次
// List<Map<String, Object>> maps2 = mapper1.selectAll(param);
// List<Map<String, Object>> maps = mapper.selectAll("zwj","1");
maps.forEach( x -> System.out.println(x));
}
}
2 mybatis启动流程
2.1 加载mybatis配置文件
SqlSessionFactoryBuilder#build()
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
//继续
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
/**
* 解析config.xml(mybatis解析xml使用的java dom)
*/
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
/**
* parse:解析mybatis.xml里面的节点
*/
//调用下面重载的方法
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//mybatis配置文件解析完成后 会将所有信息封装的全局唯一的Configuraion对象中
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
2.2 解析mybatis配置文件
XMLConfigBuilder#parse:解析mybatis.xml里面的节点
public class XMLConfigBuilder extends BaseBuilder {
/**
* 解析配置
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
/**
* 保证xml只解析一次
*/
parsed = true;
/**
* 根节点是<configuration>
*/
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// <?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"/>
// <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="org/mybatis/example/BlogMapper.xml"/>
// </mappers>
// </configuration>
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
/**
* 2.3 解析properties标签
*/
propertiesElement(root.evalNode("properties"));
/**
* 2.4 解析setting标签 读取settings属性到Properties
*/
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//2.5 加载日志实现
loadCustomLogImpl(settings);
/**
* 2.6 解析typeAlias标签 别名
*/
typeAliasesElement(root.evalNode("typeAliases"));
/**
* 2.7 插件解析
*/
pluginElement(root.evalNode("plugins"));
/**
* 2.8、解析对象工厂
*/
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//MateObject
reflectorFactoryElement(root.evalNode("reflectorFactory"));
/**
* 2.9 给Configuration设置属性
*/
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
/**
* 2.10 解析environments标签
*/
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
/**
* 2.11 解析类型处理器
*/
typeHandlerElement(root.evalNode("typeHandlers"));
/**
* 2.12 解析mapper文件 关注
*/
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
}
2.3 解析properties标签
/**
* //1.properties
* //<properties resource="org/mybatis/example/config.properties">
* // <property name="username" value="dev_user"/>
* // <property name="password" value="F2Fa3!33TYyg"/>
* //</properties>
*/
/**
* //1.在 properties 元素体内指定的属性首先被读取。
* //2.从类路径下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
* //3.作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties
* 元素体内和资源/url 属性中加载的。
*/
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
/**
* 1、得到所有的<property></>标签的属性
*/
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
/**
* 2、然后查找resource或者url,加入前面的Properties
*/
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
/**
* 3.Variables也全部加入Properties
*/
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
2.4 解析setting标签
/**
*
* //这些是极其重要的调整, 它们会修改 MyBatis 在运行时的行为方式
* //<settings>
* // <setting name="cacheEnabled" value="true"/>
* // <setting name="lazyLoadingEnabled" value="true"/>
* // <setting name="multipleResultSetsEnabled" value="true"/>
* // <setting name="useColumnLabel" value="true"/>
* // <setting name="useGeneratedKeys" value="false"/>
* // <setting name="enhancementEnabled" value="false"/>
* // <setting name="defaultExecutorType" value="SIMPLE"/>
* // <setting name="defaultStatementTimeout" value="25000"/>
* // <setting name="safeRowBoundsEnabled" value="false"/>
* // <setting name="mapUnderscoreToCamelCase" value="false"/>
* // <setting name="localCacheScope" value="SESSION"/>
* // <setting name="jdbcTypeForNull" value="OTHER"/>
* // <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
* //</settings>
*/
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
/**
* 检查下是否在Configuration类里都有相应的setter方法(没有拼写错误)
*/
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
2.5 日式实现
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
}
2.6 解析类型别名
/**
* //2.类型别名
* //<typeAliases>
* // <typeAlias alias="Author" type="domain.blog.Author"/>
* // <typeAlias alias="Blog" type="domain.blog.Blog"/>
* // <typeAlias alias="Comment" type="domain.blog.Comment"/>
* // <typeAlias alias="Post" type="domain.blog.Post"/>
* // <typeAlias alias="Section" type="domain.blog.Section"/>
* // <typeAlias alias="Tag" type="domain.blog.Tag"/>
* //</typeAliases>
* //or
* //<typeAliases>
* // <package name="domain.blog"/>
* //</typeAliases>
*/
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
/**
* 1、如果是package 调用TypeAliasRegistry.registerAliases,去包下找所有类,
* 然后注册别名(有@Alias注解则用,没有则取类的simpleName)
*/
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
/**
* 2、根据Class名字来注册类型别名
*/
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
2.7 解析插件
/**
* 插件解析
* //MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用插件来拦截方法调用
* //<plugins>
* // <plugin interceptor="org.mybatis.example.ExamplePlugin">
* // <property name="someProperty" value="100"/>
* // </plugin>
* //</plugins>
*
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
/**
* 创建拦截器对象
* 设置属性
*/
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
2.8 解析对象工厂
/**
* //4.对象工厂,可以自定义对象创建的方式
* //<objectFactory type="org.mybatis.example.ExampleObjectFactory">
* // <property name="someProperty" value="100"/>
* //</objectFactory>
* @param context
* @throws Exception
*/
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties properties = context.getChildrenAsProperties();
/**
* 创建对象工厂
*/
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
/**
* 创建对象
*/
factory.setProperties(properties);
configuration.setObjectFactory(factory);
}
}
2.9 给Configuration设置属性
private void settingsElement(Properties props) {
/**
*自动映射
*/
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
/**
*
*/
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
/**
* 缓存 二级缓存默认开启 但还需要每个mapper手动开启二级缓存
*/
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
/**
* 代理工厂
*/
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
/**
* 懒加载
*/
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
/**
* 延迟加载时,每种属性是否还要按需加载
*/
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
/**
* 允不允许多种结果集从一个单独 的语句中返回
*/
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
/**
* 使用列标签代替列名
*/
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
/**
* 允许 JDBC 支持生成的键
*/
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
/**
* 配置默认的执行器
*/
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
/**
* 超时时间
*/
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
/**
* 是否将DB字段自动映射到驼峰式Java属性(A_COLUMN-->aColumn)
*/
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
/**
* 嵌套语句上使用RowBounds
*/
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
/**
* 默认用sqlsession级别的缓存(一级缓存)
*/
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
/**
* 为null值设置jdbctype
*/
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
/**
* Object的哪些方法将触发延迟加载
*/
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
/**
* 使用安全的ResultHandler
*/
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
/**
* 动态SQL生成语言所使用的脚本语言
*/
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
/**
*
*/
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
/**
* 当结果集中含有Null值时是否执行映射对象的setter或者Map对象的put方法。此设置对于原始类型如int,boolean等无效。
*/
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
/**
*
*/
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
/**
* logger名字的前缀
*/
configuration.setLogPrefix(props.getProperty("logPrefix"));
/**
* 配置工厂
*/
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
2.10 解析environments标签
/**
* // <environments default="development">
* // <environment id="development">
* // <transactionManager type="JDBC">
* // <property name="..." value="..."/>
* // </transactionManager>
* // <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>
*/
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
/**
* 循环比较id是否就是指定的environment
*/
if (isSpecifiedEnvironment(id)) {
/**
* 事务管理器
*/
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
/**
* 数据源
*/
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
2.11 解析类型处理器
/** 类型处理器
* // <typeHandlers>
* // <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
* // </typeHandlers>
* //or
* // <typeHandlers>
* // <package name="org.mybatis.example"/>
* // </typeHandlers>
* private void typeHand
* @param parent
*/
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
/**
* 如果是package 调用TypeHandlerRegistry.register,去包下找所有类
*/
typeHandlerRegistry.register(typeHandlerPackage);
} else {
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
/**
* 手动注册
*/
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
2.12 扫描并解析mapper文件
扫描并解析mapper文件比较重要,下文单独进行说明