複習了mybatis相關的一些用法,從這篇開始就是開始學習mybatis的源碼,以前也是曾經看過一些mybatis的源碼,但是隻是粗粗的看過一點兒,而且沒有什麼耐心看下去,全都是浮于表面,現在想想真的是悔不當初。
這一次不會了,在寫部落格之前,也是認真的看了一部分mybatis的源碼,現在就是以部落格的方式,記錄下我的學習過程,部落格中有錯誤的地方,希望指出,共同學習。
首先,建立mybatis_sources的maven項目,添加下面的依賴:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 寫mybatis插件使用的依賴,不用可以不添加 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
添加mybatis的配置檔案mybatis-config.xml:
<?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>
<properties resource="dbconfig.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
<setting name="jdbcTypeForNull" value="NULL" />
<!--顯示的指定每個我們需要更改的配置的值,即使他是預設的。防止版本更新帶來的問題 -->
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false" />
</settings>
<typeAliases>
<package name="com.yukio.mybatis.bean" />
</typeAliases>
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<!-- 為不同的資料庫廠商起别名 -->
<property name="MySQL" value="mysql" />
</databaseIdProvider>
<mappers>
<!-- 批量注冊: -->
<package name="com.yukio.mybatis.dao" />
</mappers>
</configuration>
添加資料庫配置檔案dbconfig.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis_sources?allowMultiQueries=true
jdbc.username=root
jdbc.password=123456
添加對應的實體bean,Student.java:
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer age;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Student(Integer id, String name, Integer age, String gender) {
super();
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
}
添加對應的接口StudentMapper.java:
import com.yukio.mybatis.bean.Student;
public interface StudentMapper {
Student getStudentById(Integer id);
}
對應的StudentMapper.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.yukio.mybatis.dao.StudentMapper">
<resultMap type="com.yukio.mybatis.bean.Student"
id="studentMap">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="gender" property="gender" />
</resultMap>
<select id="getStudentById" resultMap="studentMap">
select id,name,age,gender
from tb_student where id=#{id}
</select>
</mapper>
所有的前置工作完成之後,咱們接下來看看對應的測試代碼,StudentMapperTest.java:
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.yukio.mybatis.bean.Student;
import com.yukio.mybatis.dao.StudentMapper;
public class StudentMapperTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testGetStudentById() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentById(1);
System.out.println(student);
}
}
通過下面的這段代碼,是将mybatis的配置檔案,轉化成輸入流,裡面的具體實作,咱們就是不深入去看了,隻要記得這個配置檔案得到的輸入流,咱們在下面使用就好了。
InputStream inputStream = Resources.getResourceAsStream(resource);
往下看,咱們直接進入SqlSessionFactoryBuilder.build(InputStream inputStream)方法:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
可以看到,在這個方法中,又調用了同類中的另外一個方法SqlSessionFactoryBuilder.build(InputStream inputStream, String environment, Properties properties),代碼如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
從下面的這個代碼中,咱們能夠看到,其作用主要是從輸入流中,擷取到一個XMLConfigBuilder.java,具體的代碼實作,可以進去看看:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
接着,咱們通過XMLConfigBuilder.parse()進入到以下代碼中,可以看到一個擷取對應的mybatis配置的操作:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
通過parseConfiguration(XNode root)進入到XMLConfigBuilder.parseConfiguration(XNode root)方法中,可以看到,這個方法的主要作用就是擷取一個org.apache.ibatis.session.Configuration.java:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
mybatis的官方文檔中,就是曾經指明過mybatis中的properties參數的裝載方式,是以這裡咱們重點看一下,對應的代碼如下:
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
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.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
通過這段代碼,我們能夠得到,在 properties 元素體内指定的屬性首先被讀取,然後根據 properties 元素中的 resource 屬性讀取類路徑下屬性檔案或根據 url 屬性指定的路徑讀取屬性檔案,并覆寫已讀取的同名屬性,以及讀取作為方法參數傳遞的屬性,并覆寫已讀取的同名屬性。
好了,現在我們已經擷取到了對應的org.apache.ibatis.session.Configuration.java類,接下來,咱們需要繼續傳回到SqlSessionFactoryBuilder.build(InputStream, String, Properties).java中,代碼如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
好了,繼續往下看,通過build(parser.parse()),我們進入到了SqlSessionFactoryBuilder.build(Configuration)方法中,能夠看到我們擷取到的是一個org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.java類:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
好了,到這裡mybatis的解析過程,就算是結束了,下一篇我們開始看一下,mybatis對于參數的解析。