一、Mybatis 介紹
MyBatis 本是 apache 的一個開源項目 iBatis, 2010年這個項目由 apache software foundation 遷移到了 google code,并且改名為 MyBatis 。2013年11月遷移到 Github。
MyBatis是一個優秀的持久層框架,它對 jdbc 操作數據庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等 jdbc 繁雜的過程代碼。
Mybatis通過xml或注解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,并通過 java 對象和 statement 中的 sql 進行映射生成最終執行的sql語句,最后由mybatis框架執行sql并將結果映射成java對象并返回。
二、使用 JDBC 編程問題總結
- JDBC 編程步驟
- 注冊數據庫驅動
- 創建并獲取數據庫連接 connection
- 創建 jdbc statement 對象
- 書寫 sql 語句
- 設置 sql 語句中的參數(使用 preparedStatement)
- 通過 statement 執行 sql 并獲取結果
- 對 sql 執行結果進行解析處理
- 釋放資源 (resultSet、preparedStatement、connection)
- JDBC 程序示例
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 注冊數據庫驅動
Class.forName("com.mysql.jdbc.Driver");
// 通過驅動管理類獲取數據庫連接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
// 定義sql語句 ?表示占位符
String sql = "select * from user where username = ?";
// 獲取預處理statement
preparedStatement = connection.prepareStatement(sql);
// 設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值
preparedStatement.setString(1, "王五");
// 向數據庫發出sql執行查詢,查詢出結果集
resultSet = preparedStatement.executeQuery();
// 遍歷查詢結果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 釋放資源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上邊使用jdbc的原始方法(未經封裝)實現了查詢數據庫表記錄的操作。
- JDBC 問題總結如下:
- 數據庫連接創建、釋放頻繁造成系統資源浪費,從而影響系統性能。可使用數據庫連接池解決此問題。
- Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用中sql變化的可能較大,sql變動需要改變java代碼。
- 使用preparedStatement向占位符傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
- 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將數據庫記錄封裝成pojo對象解析比較方便。
三、Mybatis 架構
- mybaits 配置 ------> SqlMapConfig.xml,此文件作為 mybaits 的全局配置文件,配置了 mybatis 的運行環境等信息。mapper.xml 文件即 sql 映射文件,文件中配置了操作數據庫的 sql 語句。此文件需要在 SqlMapConfig.xml 中加載。
- 通過mybatis環境等配置信息構造SqlSessionFactory(會話工廠)。
- 由會話工廠創建sqlSession(會話),操作數據庫需要通過sqlSession進行。
- mybaits底層自定義了 Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。
- Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
- Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
- Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql后將輸出結果映射至java對象中,輸出結果映射過程相當于jdbc編程中對結果的解析處理過程。
- jar包介紹 (mybatis核心包、mybatis依賴包、數據庫驅動包)
- 配置文件參考
log4j.properties
,mybatis默認使用log4j作為輸出日志信息。
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
SqlMapConfig.xml
,mybatis核心配置文件,配置文件內容為數據源、事務管理。(引用db.properties)
<?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>
<!-- 使用resource屬性加載外部配置文件 -->
<properties resource="db.properties">
<!-- 在properties內部用property定義屬性 -->
<!-- 如果外部配置文件有該屬性,則內部定義屬性被外部屬性覆蓋 -->
<property name="jdbc.username" value="root123" />
<property name="jdbc.password" value="root123" />
</properties>
<typeAliases>
<!-- 單個別名定義 -->
<typeAlias alias="user" type="cn.itcast.mybatis.pojo.User" />
<!-- 批量別名定義,掃描整個包下的類,別名為類名(大小寫不敏感) -->
<package name="cn.itcast.mybatis.pojo" />
<package name="其它包" />
</typeAliases>
<!-- 和spring整合后 environments配置將廢除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池 -->
<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>
<!-- 加載映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml" />
<mapper resource="mapper/UserMapper.xml" />
</mappers>
</configuration>
- MyBatis 將按照下面的順序來加載屬性:
1)、在 properties 元素體內定義的屬性首先被讀取。
2)、然后會讀取properties 元素中resource或 url 加載的屬性,它會覆蓋已讀取的同名屬性。 - 在mapper.xml配置文件中,就可以使用設置的別名了。(別名大小寫不敏感)
sql映射文件
,需要 在SqlMapConfig中加載
<?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">
<!-- namespace:
1.命名空間,用于隔離sql,
2.使用動態代理開發DAO,namespace必須和Mapper接口類路徑一致。 -->
<mapper namespace="test">
<!-- id:statement的id 或者叫做sql的id-->
<!-- parameterType:聲明輸入參數的類型 -->
<!-- resultType:聲明輸出結果的類型,應該填寫pojo的全路徑 -->
<!-- #{}:輸入參數的占位符,相當于jdbc的? -->
<select id="queryUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">
SELECT * FROM `user` WHERE id = #{id}
</select>
<!-- 如果返回多個結果,mybatis會自動把返回的結果放在list容器中 -->
<!-- resultType的配置和返回一個結果的配置一樣 -->
<select id="queryUserByUsername1" parameterType="string" resultType="cn.itcast.mybatis.pojo.User">
SELECT * FROM `user` WHERE username LIKE #{username}
</select>
<!-- 如果傳入的參數是簡單數據類型,${}里面必須寫value -->
<select id="queryUserByUsername2" parameterType="string" resultType="cn.itcast.mybatis.pojo.User">
SELECT * FROM `user` WHERE username LIKE '%${value}%'
</select>
<!-- 保存用戶 -->
<insert id="saveUser" parameterType="cn.itcast.mybatis.pojo.User">
INSERT INTO `user` (username,birthday,sex,address) VALUES
(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 保存用戶 自增主鍵返回-->
<insert id="saveUser1" parameterType="cn.itcast.mybatis.pojo.User">
<!-- selectKey 標簽實現主鍵返回 -->
<!-- keyColumn:主鍵對應的表中的哪一列 -->
<!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
<!-- order:設置在執行insert語句前執行查詢id的sql,孩紙在執行insert語句之后執行查詢id的sql -->
<!-- resultType:設置返回的id的類型 -->
<selectKey keyColumn="id" keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `user` (username,birthday,sex,address) VALUES
(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 保存用戶 使用 uuid實現主鍵-->
<insert id="saveUser2" parameterType="cn.itcast.mybatis.pojo.User">
<!-- selectKey 標簽實現主鍵返回 -->
<!-- keyColumn:主鍵對應的表中的哪一列 -->
<!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
<!-- order:設置在執行insert語句前執行查詢id的sql,孩紙在執行insert語句之后執行查詢id的sql -->
<!-- resultType:設置返回的id的類型 -->
<selectKey keyColumn="id" keyProperty="id" order="BEFORE" resultType="string">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `user` (username,birthday,sex,address) VALUES
(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 更新用戶 -->
<update id="updateUserById" parameterType="cn.itcast.mybatis.pojo.User">
UPDATE `user` SET username = #{username} WHERE id = #{id}
</update>
<!-- 刪除用戶 -->
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
四、需要注意的點
-
#{} 與 ${}
parameterType 與 resultType
parameterType:指定輸入參數類型,mybatis通過ognl從輸入對象中獲取參數值拼接在sql中。
resultType:指定輸出結果類型,mybatis將sql查詢結果的一行記錄數據映射為resultType指定類型的對象。如果有多條數據,則分別進行映射,并把對象放到容器List中selectOne 和 selectList
selectOne:查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList:可以查詢一條或多條記錄。加載sql映射文件的方式
<mapper resource=" " />
使用相對于類路徑的資源(常用方式)
如:<mapper resource="sqlmap/User.xml" /><mapper class=" " />
使用mapper接口類路徑
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。<package name=""/>
注冊指定包下的所有mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
五、Mybatis解決jdbc編程的問題
- 數據庫連接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫連接池可解決此問題。
解決:在SqlMapConfig.xml中配置數據連接池,使用連接池管理數據庫鏈接。 - Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。 - 向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。
解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。 - 對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將數據庫記錄封裝成pojo對象解析比較方便。
解決:Mybatis自動將sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。
六、mybatis與hibernate不同
Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。mybatis可以通過XML或注解方式靈活配置要運行的sql語句,并將java對象和sql語句映射生成最終執行的sql,最后將sql執行的結果再映射生成java對象。
Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關系數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,因為這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套sql映射文件,工作量大。
Hibernate對象/關系映射能力強,數據庫無關性好,對于關系模型要求高的軟件(例如需求固定的定制化軟件)如果用hibernate開發可以節省很多代碼,提高效率。但是Hibernate的學習門檻高,要精通門檻更高,而且怎么設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。總之,按照用戶的需求在有限的資源環境下只要能做出維護性、擴展性良好的軟件架構都是好架構,所以框架只有適合才是最好。