自我控制是最強者的本能。——蕭伯納
目錄
Mybatis簡介
什麼是Mybatis
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java對象)映射成數據庫中的記錄。
也就是說MyBatis通過一些配置後,只需要關注SQL語句即可,而不用花費大量的時間精力去編寫一些複雜的重複的代碼,如:註冊驅動,獲取連接,獲取傳輸器,處理結果集等等。這一切得複雜的代碼都被MyBatis給封裝集成了,大大的減少了開發過程中的代碼量與出錯的概率。
總結:Mybatis對JDBC訪問數據庫的過程進行了封裝,簡化了JDBC代碼,解決JDBC將結果集封裝爲Java對象的麻煩。
爲什麼使用Mybatis
Mybatis對JDBC訪問數據庫的過程進行了封裝,那麼在不使用Mybatis的情況下,JDBC是如何的呢?
使用傳統方法訪問數據庫
(1)使用JDBC訪問數據庫有大量重複代碼(比如註冊驅動、獲取連接、獲取傳輸器、釋放資源等);
(2)JDBC自身沒有連接池,會頻繁的創建連接和關閉連接,效率低;
(3)SQL是寫死在程序中,一旦修改SQL,需要對類重新編譯;
(4)對查詢SQL執行後返回的ResultSet對象,需要手動處理,有時會特別麻煩;
使用Mybatis
(1)Mybatis對JDBC對了封裝,可以簡化JDBC代碼;
(2)Mybatis自身支持連接池(也可以配置其他的連接池),因此可以提高程序的效率;
(3)Mybatis是將SQL配置在mapper文件中,修改SQL只是修改配置文件,類不需要重新編譯。
(4)對查詢SQL執行後返回的ResultSet對象,Mybatis會幫我們處理,轉換成Java對象。
由上面可以看出,JDBC中的問題(複雜的代碼,連接的創建,結果集的處理)幾乎都被Mybatis解決了。
Mybatis的架構
(1)mybatis-config.xml是Mybatis的核心配置文件,通過其中的配置可以生成SqlSessionFactory,也就是SqlSession工廠
(2)基於SqlSessionFactory可以生成SqlSession對象
(3)SqlSession是一個既可以發送SQL去執行,並返回結果,類似於JDBC中的Connection對象,也是Mybatis中至關重要的一個對象。
(4)Executor是SqlSession底層的對象,用於執行SQL語句
(5)MapperStatement對象也是SqlSession底層的對象,用於接收輸入映射(SQL語句中的參數),以及做輸出映射(即將SQL查詢的結果映射成相應的結果)
瞭解了這些,那就往下看看Mybatis如何使用吧。
Mybatis入門
準備數據
在此呢,以代碼爲例進行展示
創建數據庫、創建表、並插入數據
1、創建數據庫
create database if not exists userdb charset utf8;--userdb數據庫不存在的話創建,指定編碼utf8
use userdb; -- 選擇userdb數據庫
2、如果存在user表的話刪除
drop table if exists user;
3、創建user表
create table user(
id int primary key auto_increment,
name varchar(50),
job varchar(50),
age int
);
4、插入數據
insert into user values(null, '張飛', '匹夫', 22);
insert into user mp values(null, '劉備', '老大', 29);
insert into user values(null, '關羽', '馬伕', 24);
insert into user values(null, '趙子龍', '愣頭青', 25);
insert into user values(null, '華雄', '小嘍囉', 18);
數據都是亂寫的,莫要認真哈
創建工程
1、創建Maven的Java工程
2、導入Junit、mysql、mybatis等開發包
<dependencies>
<!-- junit單元測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!-- mysql驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- 整合log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
3、創建測試類並提供一個方法
編寫實體類User
此處需要注意一點:實體類的屬性名應該與數據庫表的字段名一致,不然會出現錯誤。
當然表的字段名不推薦使用駝峯,兩個字母之間使用下劃線連接,而實體類的屬性是駝峯規則,此時,就需要有額外的配置使得屬性名與字段名相匹配。(這個方法此處就不多說了,百度,不止一種方法)
package com.demo.pojo;
import java.io.Serializable;
/*
* 實體類用於封裝數據庫中的每一條信息
* 爲了防止信息丟失,一般都會實現序列化
*/
public class User implements Serializable{
private static final long serialVersionUID = -2261716295130942575L;
//聲明實體類的屬性
private Integer id;
private String name;
private String job;
private Integer age;
//生成對應的get set方法
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 String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
//重寫toString方法
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", job=" + job + ", age=" + age + "]";
}
}
添加UserMapper.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一般指定爲當前文件的所在包路徑+文件名(將來是接口名)
在程序中通過[ namespace + id ]定位到執行哪一條SQL語句
-->
<mapper namespace="UserMapper">
<!-- 通過select、insert、update、delete標籤聲明要執行的SQL -->
<!-- 練習1: 查詢user表中的所有員工信息
resultType指定查詢的結果將會封裝到什麼類型中 -->
<select id="findAll" resultType="com.demo.pojo.User">
select * from user
</select>
<!--
resultType:返回值類型,簡單類型(例如:Integer,String,Emp等)
如果返回集合(List<Emp>),只需配置集合中的元素類型即可!
-->
</mapper>
mapper標籤:跟標籤,namespace就相當於是一個路徑,一般指向該文件的位置(包路徑+文件名)
select標籤:指定要執行的SQL語句,還有delete、insert、update,標籤上可以聲明屬性
id:要求不能重複,要通過xxxmapper.xml文件名稱+id值來尋找執行的SQL語句
resultType屬性:返回值結果類型,當然如果說期望的返回值爲集合類型,那麼此處只需要寫集合中包含的元素的類型即可,像這個例子中,期望類型爲list<User>,不用寫list,寫User的全限定路徑即可
resultMap屬性:複雜對象結構(如多表關聯),當然這個屬性還可以用來解決表中下劃線字段名與實體類中駝峯屬性不匹配的問題
添加mybatis-config.xml文件
1、在src/main/resources目錄下創建mybatis-config.xml文件(該文件爲mybatis的核心配置文件)
2、配置核心配置文件
<?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">
<!-- MyBatis的全局配置文件 -->
<configuration >
</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">
<!-- MyBatis的全局配置文件 -->
<configuration>
<!-- 1.配置開發環境(需設置一個默認的環境) -->
<environments default="develop">
<!-- 這裏可以配置多個環境,比如develop,test等 -->
<environment id="develop">
<!-- 1.1.配置事務管理方式:JDBC/MANAGED JDBC:將事務交給JDBC管理(推薦) MANAGED:自己管理事務 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 1.2.配置數據源,即連接池方式:JNDI/POOLED/UNPOOLED JNDI:已過時 POOLED:使用連接池(推薦) UNPOOLED:不使用連接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/userdb?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 2.加載Mapper配置文件(因mapper文件中配置了要執行的SQL語句) -->
<mappers>
<!-- 注意路徑 -->
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>
environments標籤:該標籤內部可以配置多個environment,每個environment可以有自己的配置,各個environment之間的配置互不影響,但是在運行程序時,在多個environment之間只能有一個在運行。
environment標籤:進行各項配置,比如說事務、數據源等。
transactionManager 標籤:事務管理配置,mybatis中有兩種JDBC和MANAGED
JDBC:有提交和回滾設置,它依賴於從數據源得到的連接來管理事務範圍。推薦使用。
MANAGED:這個配置幾乎沒做什麼。它從來不提交或回滾一個連接。需要自己手動添加並管理。不推薦使用。
DataSource:配置數據源,也就是連接池。數據源類型有三種:JNDI(已過時)/POOLED(使用連接池,mybatis會創建連接,使用完後連接返回池中)/UNPOOLED(不使用連接池)
Mappers標籤:導入mapper文件的位置,可以配置多個。
測試
package com.demo.mybatis;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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.demo.pojo.User;
public class TestMybatis {
/** 練習1(快速入門): 查詢user表中的所有員工, 返回一個List<User>集合
* @throws IOException */
@Test
public void findAll() throws IOException {
//1.讀取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通過配置信息獲取一個SqlSessionFactory工廠對象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build( in );
//3.通過工廠獲取一個SqlSession對象
SqlSession session = fac.openSession();
//4.通過namespace+id找到要執行的sql語句並執行sql語句
List<User> list = session.selectList("UserMapper.findAll");
//5.輸出結果
for(User e : list) {
System.out.println( e );
}
}
}
運行結果如下:
增刪改查
增刪改查大同小異,此處只寫一個
新增一個員工信息:貂蟬,舞娘,28
先寫UserMapper.xml,如下:
<update id="insert" >
insert into user value(null, '貂蟬', '舞娘', 28)
</update>
測試方法:
@Test
public void testInsert() throws IOException{
//1.讀取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通過配置信息獲取一個SqlSessionFactory工廠對象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build( in );
//3.通過工廠獲取一個SqlSession對象
SqlSession session = fac.openSession();
//執行sql語句, 返回執行結果
int rows = session.update("UserMapper.insert");
//提交事務
session.commit();
System.out.println("影響的行數: "+rows);
}
刪和改與上面一樣。
動態SQL
mybatis中有一些元素對SQL語句進行了優化,這裏簡單的說三個:if、where、foreach
if元素
if元素用於對某一字段進行判斷,根據判斷的結果決定是否執行包含在其中的SQL片段。
舉個栗子:
假如說要查詢user表中年齡在最小與最大之間的user信息
年齡最小用minAge表示,最大年齡用maxAge表示
<select id="findAllByAge" resultType="com.demo.pojo.User">
select * from user
where 1=1
<if test="minAge != null">
and age>#{minAge }
</if>
<if test="maxAge != null">
and minAge <![CDATA[ < ]]> #{maxAge}
</if>
</select>
上述代碼解釋:
1)、where後面爲什麼會跟個1=1?
如果minAge和maxAge兩個參數都沒有給賦值的話,代碼會變爲下面:
<select id="findAllByAge" resultType="com.demo.pojo.User">
select * from user
where 1=1
</select>
看出來了吧,此處的1=1只是爲了保證SQL語句不會出錯,如果沒有1=1的話,SQL就會變爲select * from user where,這樣where後面沒有條件。
2)if元素:如果傳入了參數,那麼執行if內部的age>#{minAge},或者是age<#{maxAge},又或者兩者都執行,此時1=1也並不礙事
3)、有細心的小夥伴是不是已經有發現不一樣了呢,對,就是 <![CDATA[ < ]]> 東西,嘿嘿,什麼意思呢?
如果你嘗試直接寫<小於號的話,你會發現xml文件裏面報錯了。因爲小於號(<)在xml文件中是特殊字符,被xml文件當成了標籤的開始符號。
解決方法的就是將特殊符號包含在CDATA區()中,這是因爲放在CDATA區中的內容,只會被xml解析器當作普通文本來處理。而不是被當成標籤的一部分處理。小於號也就變成了 <![CDATA[ < ]]>
如果說讓你一直寫where 1=1這種東西,煩不煩?或者萬一忘了呢?此時你就會用到下面這個元素了where。
where
where元素則用於對包含在其中的SQL語句進行檢索,需要時會剔除多餘的連接詞(比如and或者or),並且在需要時可以添加where關鍵詞
<select id="find" resultType="com.demo.pojo.User">
select * from user
<where>
<if test="id!=null">
id>#{id}
</if>
or 1=2
</where>
</select>
看這個:我要的效果是如果id有值那麼就進行查詢,如果id沒有傳值,那麼就不進行查詢
當id沒有傳值時:
<select id="find" resultType="com.demo.pojo.User">
select * from user
<where>
or 1=2
</where>
</select>
轉換爲SQL語句就是:select * from user where 1=2
因爲where元素有自動去除多餘的and或者or連接詞的功能
foreach
如果傳過來的參數是集合或者數組的話,那麼取參數就要進行遍歷了。
<!-- 練習13: 根據員工的id批量刪除員工信息
delete from emp
where id in (1,3,5,7)
-->
<update id="deleteByIds">
delete from emp where id in
<foreach collection="array" open="("
item="id" separator="," close=")">
#{id}
</foreach>
</update>
@Test
public void testDeleteByIds() {
Integer[] ids = { 1, 3, 5, 7 };
int rows = session.update(
"EmpMapper.deleteByIds", ids);
session.commit();
System.out.println("影響的行數: "+rows);
}
collection屬性:看你傳的參數是什麼類型,數組就是array,集合就是list
open、close:開始位置,結束位置
separator:分隔符,可以是逗號(,)、or等
item:爲封裝遍歷得到的參數起的名字
#{}和${}佔位符
由上面的SQL語句,我們看到很多使用#{}佔位符,那麼爲什麼呢?
在JDBC中,有兩種傳輸器Statement和PreparedStatement,前者是將SQL語句寫死,會造成SQL的注入攻擊,不安全。後者是先寫一個骨架,將SQL中寫死的值換成了佔位符,進行了預編譯,不會造成SQL注入。
佔位符呢又分爲兩種#{}和${}。
#{}:當SQL語句中包含的參數值是傳遞過來的,難麼我們會使用#{}佔位符進行佔位,在SQL執行時,在用傳遞過來的值替換佔位符。其實#{}就是JDBC中的問號(?)佔位符,因而爲了安全,會對傳遞的值進行轉譯處理。
舉個栗子:
select * from emp where name=#{name} 相當於是 select * from emp where name=?
${}:如果我們在傳遞的時候不是一個參數值,而是一個SQL片段呢?此時我們就需要使用${}佔位符了,起一個拼接的作用。
select ${columns} from emp
需要注意的是,在傳遞 ${}對應的值時,需要將值存入map集合中!!
總結:在大多數情況下還是使用#{}佔位符,而${}多用於爲不帶引號的字符串進行佔位!
Mapper接口開發
爲什麼要用mapper接口開發
在上面的過程中,sqlsession想要執行某個SQL方法的話,需要傳入一個字符串參數,該值對應的內容爲: (Mapper文件中的) namespace + id, 通過這種方式, 找到Mapper文件中映射的SQL語句並執行!!但是此處比較容易發生錯誤而且還沒有錯誤提示,我們應該去尋找更好的方法,也就是mapper接口開發。
使用mapper接口開發需要注意幾點:
1、創建一個接口,接口的全路徑名和mapper文件的namespace值要相同
2、mapper文件中每條要執行的SQL語句,在接口中要添加一個對應的方法,並且接口中的方法名和SQL標籤上的id值相同
3、Mapper接口中方法接收的參數類型,和mapper.xml中定義的sql的接收的參數類型要相同
4、接口中方法的返回值類型和SQL標籤上的resultType即返回值類型相同(如果方法返回值是集合,resultType只需要指定集合中的泛型)
如何使用mapper接口開發
創建一個接口
接口中提供一個方法
package com.demo.mapper;
import java.util.List;
import com.demo.pojo.User;
public interface Usermapper {
List<User> findAll();
}
修改UserMapper.xml文件
<mapper namespace="com.demo.mapper.Usermapper">
此時,namespace的路徑應該爲UserMapper接口的全限定路徑
注意:方法的名字要和映射的sql標籤的id值保持一致
方法的返回值類型和resultType的類型要一致
創建測試方法
@Test
public void testFindAll() throws Exception{
......
//3.獲取Mapper接口對象
UserMapper map = session.getMapper(UserMapper.class);
//4.調用接口對象的方法進行查詢
List<User> list = map.findAll();
//5.輸出結果
for (User e : list) {
System.out.println(e);
}
上面的獲取sqlsession的方法都一樣
優化
可以加入日誌配置
在項目中加入log4j的配置文件,用於打印日誌信息,便於開發調試。
mybatis默認使用log4j作爲輸出日誌信息。
只要將該文件放在指定的位置,log4j工具會自動到指定位置加載上述文件,讀取文件中的配置信息並使用!
在src(或相似的目錄)下創建log4j.properties如下:
# 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
JDBC.properties文件
將連接數據庫的配置信息單獨放在一個properties文件中(方便管理和維護), 然後在MyBatis的mapper文件中引入properties文件的配置信息
在src目錄下創建一個名稱爲jdbc.properties的文件
jdbc.properties文件內容如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/userdb?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
在mybatis-config.xml文件中引入jdbc.properties文件
<properties resource="jdbc.properties"/> 標籤用於引入jdbc.properties文件,默認到classpath即類目錄下尋找指定的文件;
properties標籤上value屬性中配置的 ${jdbc.xxx},比如${jdbc.driver}:就是jdbc.properties文件中的 jdbc.driver的值
拓展
mybatis-config文件沒有提示
如果說沒有外網的情況下,按如下配置:
(1)找到mybatis-3-config.dtd的文件的位置,例如:
(2)複製下面的url地址:
http://mybatis.org/dtd/mybatis-3-config.dtd
(3)菜單欄中: window --> Preferences --> 在搜索框中搜索 [ xml ] XML --> XML Catalog --> User Specified Entries --> Add…
Mapper文件沒有提示
(1)找到mybatis-3-mapper.dtd的文件的位置,例如:
(2)複製上面的url地址,即:
http://mybatis.org/dtd/mybatis-3-mapper.dtd
(3)
菜單欄中: window --> Preferences --> 在搜索框中搜索 [ xml ] XML --> XML Catalog --> User Specified Entries --> Add…