MyBatis 學習 (一) 入門
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 statement物件
設定sql語句
設定sql語句中的引數(使用preparedStatement)
通過statement執行sql並獲取結果
對sql執行結果進行解析處理
釋放資源(resultSet、preparedstatement、connection)
jdbc問題總結如下:
資料庫連線建立、釋放頻繁造成系統資源浪費,從而影響系統性能。如果使用資料庫連線池可解決此問題。
Sql語句在程式碼中硬編碼,造成程式碼不易維護,實際應用中sql變化的可能較大,sql變動需要改變java程式碼。
使用preparedStatement向佔有位符號傳引數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改程式碼,系統不易維護。
對結果集解析存在硬編碼(查詢列名),sql變化導致解析程式碼變化,系統不易維護,如果能將資料庫記錄封裝成pojo/bean物件解析比較方便。
MyBatis架構
- mybatis配置
SqlMapConfig.xml,此檔案作為mybatis的全域性配置檔案,配置了mybatis的執行環境等資訊。
mapper.xml檔案即sql對映檔案,檔案中配置了操作資料庫的sql語句。此檔案需要在SqlMapConfig.xml中載入。
通過mybatis環境等配置資訊構造SqlSessionFactory即會話工廠
由會話工廠建立sqlSession即會話,操作資料庫需要通過sqlSession進行。
mybatis底層自定義了Executor執行器介面操作資料庫,Executor介面有兩個實現,一個是基本執行器、一個是快取執行器。
Mapped Statement也是mybatis一個底層封裝物件,它包裝了mybatis配置資訊及sql對映資訊等。mapper.xml檔案中一個sql對應一個MappedStatement物件,sql的id即是Mapped statement的id。
MappedStatement對sql執行輸入引數進行定義,包括HashMap、基本型別、pojo,Executor通過Mapped Statement在執行sql前將輸入的java物件對映至sql中,輸入引數對映就是jdbc程式設計中對preparedStatement設定引數。
Mappedstatement對sql執行輸出結果進行定義,包括HashMap、基本型別、pojo,Executor通過Mapped Statement在執行sql後將輸出結果對映至java物件中,輸出結果對映過程相當於jdbc程式設計中對結果的解析處理過程。
入門案例(增刪改查)
根據id查詢使用者
表結構如下
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '使用者名稱稱',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性別',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
SqlMapConfig.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>
<!--
environments 環境配置(資料庫連線資訊)
和spring整合後 environments配置將廢除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 資料庫連線池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 告訴總配置檔案 表配置檔案在哪兒 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
bean
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer id;
private String username;// 使用者姓名
private String sex;// 性別
private Date birthday;// 生日
private String address;// 地址
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
user.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">
<!-- namespace:名稱空間,用於隔離sql,還有一個很重要的作用,後面會講 -->
<!-- 寫SQL語句 -->
<mapper namespace="test">
<!-- 通過id查詢一個使用者
id:用於識別方法呼叫時,呼叫哪個sql語句
parameterType:引數型別
resultType:返回結果型別 (包名+類名) 使用此屬性要注意bean中屬性與資料庫中的要一致!
#{} :佔位符,相當於jdbc的 ?
-->
<select id="findUserById" parameterType="Integer" resultType="cn.itcast.bean.User">
select * from user where id = #{id}
<!-- #{}
parameterType是值型別裡面隨便添點東西,不寫會報錯 -->
</select>
</mapper>
測試
package cn.itcast.MyBatis_day02;
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 cn.itcast.bean.User;
import junit.framework.TestCase;
public class AppTest extends TestCase
{
@Test
public void testName () throws IOException
{
//載入核心配置檔案
String resource = "SqlMapConfig.xml" ;
InputStream in = Resources.getResourceAsStream(resource) ;
//建立SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder ().build(in) ;
//建立SqlSession
SqlSession sqlSession = ssf.openSession() ;
//執行sql語句
User user = sqlSession.selectOne("test.findUserById" , 1) ;
System.out.println(user);
}
}
根據使用者名稱模糊查詢
<!--
#{} 佔位符 這種寫法可以防止sql注入 (推薦第三種)
${} 字串拼接 使用${} 裡面只能填value
-->
<select id="findUserByName" parameterType="String" resultType="cn.itcast.bean.User">
<!-- select * from user where username like #{name} -->
<!-- select * from user where username like '%${value}%' -->
select * from user where username like "%"#{name}"%"
</select>
//根據使用者名稱查詢使用者資訊
@Test
public void testName () throws Exception
{
String resource = "SqlMapConfig.xml" ;
InputStream in = Resources.getResourceAsStream(resource) ;
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in) ;
SqlSession ss = ssf.openSession() ;
// User user = ss.selectOne("test.findUserByName", "%三豐%");
User user = ss.selectOne("test.findUserByName", "三豐");
System.out.println(user);
}
#{}和${}
#{}表示一個佔位符號,通過#{}可以實現preparedStatement向佔位符中設定值,自動進行java型別和jdbc型別轉換。#{}可以有效防止sql注入。 #{}可以接收簡單型別值或pojo屬性值。 如果parameterType傳輸單個簡單型別值,#{}括號中可以是value或其它名稱。
${}表示拼接sql串,通過${}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc型別轉換, ${}可以接收簡單型別值或pojo屬性值,如果parameterType傳輸單個簡單型別值,${}括號中只能是value。
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可以查詢一條或多條記錄。
**插入一條新資料**
-------
//新增
@Test
public void testInsert () throws Exception
{
String resource = “SqlMapConfig.xml” ;
InputStream in = Resources.getResourceAsStream(resource) ;
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in) ;
SqlSession ss = ssf.openSession() ;
User user = new User () ;
user.setUsername("哈哈");
user.setBirthday( new Date ());
user.setSex("男");
user.setAddress("d");
int i = ss.insert("test.insertUser", user) ;
//提交事務
ss.commit();
}
<!-- #{username} 此時#{} 作用像是取值,從user物件取出username屬性的值 -->
<insert id="insertUser" parameterType="cn.itcast.bean.User">
insert into User ( username , birthday , sex , address )values ( #{username} , #{birthday} , #{sex} , #{address} )
</insert>
在插入新資料後,返回新資料的主鍵id
------------------
//新增
@Test
public void testInsert () throws Exception
{
String resource = “SqlMapConfig.xml” ;
InputStream in = Resources.getResourceAsStream(resource) ;
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in) ;
SqlSession ss = ssf.openSession() ;
User user = new User () ;
user.setUsername("哈哈");
user.setBirthday( new Date ());
user.setSex("男");
user.setAddress("d");
int i = ss.insert("test.insertUser", user) ;
//提交事務
ss.commit();
System.out.println(user.getId());
}
<!-- #{username} 此時#{} 作用像是取值,從user物件取出username屬性的值 -->
<insert id="insertUser" parameterType="cn.itcast.bean.User">
<!-- 獲取剛剛插入新資料的主鍵id (這是一條SQL語句) -->
<!-- selectKey 標籤實現主鍵返回 -->
<!-- keyColumn:主鍵對應的表中的哪一列 -->
<!-- keyProperty:主鍵對應的pojo中的哪一個屬性 -->
<!-- order:設定在執行insert語句前執行查詢id的sql,還是在執行insert語句之後執行查詢id的sql -->
<!-- resultType:設定返回的id的型別 -->
<selectKey keyColumn="id" keyProperty="id" order="AFTER" resultType="Integer">
select LAST_INSERT_ID()
</selectKey>
insert into User ( username , birthday , sex , address )values ( #{username} , #{birthday} , #{sex} , #{address} )
</insert>
使用uuid實現主鍵
----------
思路:在insert之前執行
select uuid()得到uuid值 返回值填充到bean的id中,然後#{}取出即可
- <insert id="insert" parameterType="com.mawulou.model.Functions" >
-- <selectKey keyProperty="pkGlobalId" resultType="String" order="BEFORE">
- select uuid()
- </selectKey>
-- insert into tbl_function (id, name, parent_id,
- sort, url)
- values (#{id}, #{name}, #{parentId},
- #{sort}, #{url})
- </insert>
## 更新資料 ##
<update id="updateUser" parameterType="cn.itcast.bean.User">
update user set username = #{username} where id = #{id}
</update>
//更新
@Test
public void testUpdate () throws Exception
{
String resource = "SqlMapConfig.xml" ;
InputStream in = Resources.getResourceAsStream(resource) ;
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in) ;
SqlSession ss = ssf.openSession() ;
User user = new User () ;
user.setUsername("呵呵");
user.setId(27);
ss.update("updateUser", user) ;
//提交事務
ss.commit();
ss.close();
}
刪除資料
<delete id="deleteUser" parameterType="Integer">
delete from user where id = #{id}
</delete>
//刪除
@Test
public void testDelete () throws Exception
{
String resource = "SqlMapConfig.xml" ;
InputStream in = Resources.getResourceAsStream(resource) ;
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in) ;
SqlSession ss = ssf.openSession() ;
ss.delete("deleteUser", 29) ;
//提交事務
ss.commit();
ss.close();
}
1. 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定義輸出結果的型別。
1. 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需要具有很強的經驗和能力才行。
總之,按照使用者的需求在有限的資源環境下只要能做出維護性、擴充套件性良好的軟體架構都是好架構,所以框架只有適合才是最好。
“`