MyBatis進階 一 執行原理
初次學習MyBatis,自己花了不少時間,理解一件事物是需要時間的。經過多次反覆的理解,你的認知能力就可以得到提升。以下是學習MyBatis的一些理解認識,技術理解上若有不當之處,敬請朋友們提出寶貴意見,以此共勉!
感觸:要想真正理解框架,應該深入到底層實現程式碼中去。只有這樣,才能夠真正理解其框架內涵,或許還可以寫出個性化的框架喲!
基本的演變流程為:JDBC--->dbutils--->MyBatis--->Hibernate
MyBatis作為資料庫持久層框架,在使用前首先匯入相關jar包(mybatis-3.2.7.jar),還要對其進行配置,配置檔案為mybatis.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="jdbc.properties" />
<!-- 設定資訊元素
會修改MyBatis在執行時的行為方式 -->
<settings >
<!-- 全域性對映器啟用快取 -->
<setting name="cacheEnabled" value="true" />
<!-- 查詢時,關閉關聯物件及時載入以提高效能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 設定關聯物件載入的形態,此處為按需載入欄位(載入欄位由SQL指定),不會載入關聯表的所有欄位,以提高效能 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 對於未知的SQL查詢,允許返回不同的結果集以達到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 允許使用列標籤代替列名 -->
<setting name="useColumnLabel" value="true" />
<!-- 允許使用自定義的主鍵值(比如由程式生成的UUID 32位編碼作為鍵值),資料表的PK生成策略將被覆蓋 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 給予被巢狀的resultMap以欄位-屬性的對映支援 -->
<setting name="autoMappingBehavior" value="FULL" />
<!-- 對於批量更新操作快取SQL以提高效能 -->
<setting name="defaultExecutorType" value="BATCH" />
<!-- 資料庫超過25000秒仍未響應則超時 -->
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<!--
第一種寫法
類型別名是為Java型別命名一個短的名字。它只和XML配置有關,只用來減少類完全限定名的多餘部分
<typeAliases>
<typeAlias alias="Person" type="com.msun.daomain.Person"/>
</typeAliases>
-->
<!-- 第二種寫法 配合註解使用,在該包com.msun.daomain下使用註解@Alias("person")
指定一個包中所有類的別名-->
<typeAliases>
<package name="com.msun.daomain"/>
</typeAliases>
<!--
development:開發模式
work:工作模式
-->
<environments default="development" >
<environment id="development" >
<transactionManager type="JDBC" />
<!-- JDBC連線物件的資料來源連線池的實現,用來避免建立新的連線例項時必要的初始
連線和認證時間。這是一種當前Web應用程式用來快速響應請求很流行的方法。 -->
<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>
</configuration>
以上是對MyBatis的屬性進行配置及設定JDBC連線物件的資料來源連線池的實現。
@Override
public void insertUser(JSONObject user) {
SqlSession session = null;
try {
//根據 JDBC 規範建立與資料庫的連線;
session = sqlSessionFactory.openSession();
//通過反射打通 Java 物件與資料庫引數互動之間相互轉化關係
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.insertUser(parasJson(user));
session.commit();
} finally {
session.close();
}
}
以上程式碼完成資料寫入資料庫的操作。
其向外提供生成代理物件的函式getMapper()方法如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//如果不存在這個mapper,則直接丟擲異常
if (!knownMappers.contains(type))
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//返回代理類
return MapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MyBatis中涉及到ORM的思想,ORM框架最重要功能是將面向物件方法中的物件和關係型資料庫中的表關聯了起來,在關聯過程中就必然涉及到物件中的資料型別(Java資料型別)和資料庫中的表字段型別的轉換,Mybatis中的org.apache.ibatis.type包主要就是實現這個功能。只要提供了持久化類與表的對映關係,ORM框架在執行時就能參照對映檔案的資訊,把物件持久化到資料庫中。當前ORM框架主要有三種:Hibernate,iBATIS,EclipseLink。
對映關係體現在mapper檔案中,有兩種方式進行關聯,分別是:基於註解的方式和編寫對映檔案(xml)的形式。基於註解的形式如下:
public interface UserMapper {
@Select(value="select * from user where id = #{id}")
public User findUserById(int id);
@Select(value="select * from user where username = #{username} and password = #{password}")
public User login(User user);
@Insert(value="insert into user (id,username,phone,email,password) values(#{id},#{username},#{phone},#{email},#{password})")
public void insertUser(User user);
@Select(value="select * from user where nickname = #{nickname} and email = #{email}")
public User findUserByNameAndEmail(User user);
}
以插入操作為例,插入的資料為#{id}...其屬性為domain域中類的相關屬性。框架會在屬性中進行相應屬性的查詢。若查詢不到則會報錯。
public class SqlSessionUtils {
/**
* SqlSessionFactory物件可以看成DataSource(資料庫連線池)
* 在應用執行期間,應該只建立一次,建議使用單例模式
*/
private static SqlSessionFactory factory=null;
public static SqlSessionFactory getSessionFactory(){
if(factory==null){
synchronized (SqlSessionUtils.class) {
if(factory==null){
try {
//1.建立配置檔案的輸入流
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2.建立SqlSessionFactory
factory=new SqlSessionFactoryBuilder().build(inputStream);
//新增對映器類(註解方式),避免了對xml檔案的依賴
factory.getConfiguration().addMapper(MyLotteryMapper.class);
factory.getConfiguration().addMapper(LotteryMapper.class);
factory.getConfiguration().addMapper(UserMapper.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
return factory;
}
}
在使用ibatis執行資料庫訪問時,會呼叫形如
getSqlMapClientTemplate().queryForObject("getCityByCityId", cityId);
這樣的程式碼。這樣的形式要求呼叫方選擇需要使用的函式(queryForObject、queryForList、update),還需要告訴這個函式具體執行哪一個statement(上文中是“getCityByCityId”),在這個過程中如果有一個地方選擇錯誤或者拼寫錯誤,不僅沒有辦法達到自己的期望值,可能還會出現異常,並且這種錯誤只有在執行時才能夠發現。
mybatis對此進行了改進,只要先宣告一個介面,就可以利用IDE的自動完成功能幫助選擇對應的函式,簡化開發的同時增加了程式碼的安全性。(如以下介面UserMapper所示 )
public interface UserMapper {
@Select(value="select * from user where id = #{id}")
public User findUserById(int id);
@Select(value="select * from user where username = #{username} and password = #{password}")
public User login(User user);
@Insert(value="insert into user (id,username,phone,email,password) values(#{id},#{username},#{phone},#{email},#{password})")
public void insertUser(User user);
@Select(value="select * from user where nickname = #{nickname} and email = #{email}")
public User findUserByNameAndEmail(User user);
}
以下為MapperRegistry中addMapper方法的底層程式碼:
public void addMapper(Class<?> type) {
//因為Java的動態代理只能實現介面,因而在註冊mapper時也只能註冊介面
if (type.isInterface()) {
//如果已經註冊過了,則丟擲異常,而不是覆蓋
if (knownMappers.contains(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//先將這個mapper新增到mybatis中,如果載入過程中出現異常需要再將這個mapper從mybatis中刪除
knownMappers.add(type);
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
//下面兩行程式碼其實做了很多的事,由於涉及的東西較多,在此不做過多的描述,留待以後專門進行介紹
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
另外,MyBatis持久層框架還涉及到動態繫結、反射、單例、工廠設計模式。個人感覺,應深入到底層程式碼去閱讀,只有這樣才可以對框架有進一步的瞭解。
參考網址
http://www.th7.cn/Program/java/201407/240933.shtml http://www.th7.cn/Program/java/201407/240933.shtml
http://blog.csdn.net/hupanfeng/article/details/9238127 http://blog.csdn.net/hupanfeng/article/details/9238127
mybatis原始碼分析之binding包
http://www.cnblogs.com/sunzhenchao/archive/2013/05/13/3075854.html http://www.cnblogs.com/sunzhenchao/archive/2013/05/13/3075854.html
Mybatis原始碼分析之別名
http://www.cnblogs.com/sunzhenchao/archive/2013/04/09/3010527.html
動態代理反射機制
java動態代理(JDK和cglib)
http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
http://www.cnblogs.com/sunzhenchao/tag/Mybatis/
Mybatis原始碼分析之型別轉換
http://www.cnblogs.com/sunzhenchao/archive/2013/04/09/3009431.html
再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed