Mybatis操作詳解
一、Mybatis的Maven工程操作
1.1、匯入依賴
<!--mysql驅動座標-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- mybatis座標 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--日誌座標-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
1.2、建立Mybatis全域性配置檔案
-
全域性配置檔案位置
-
全域性配置檔案
-
其它相關配置標籤
<!-- 是重要的設定項,name表示設定項名,value設定項取值 -->
<settings>
<setting name="" value=""></setting>
</settings>
<!-- 別名處理器:可以為java型別起別名,type是全類名,alias是新起的別名 -->
<typeAliases>
<typeAlias type="" alias=""/>
<!-- package為某個包下所有的類批量起別名,name指定包名(所有類起一個預設別名(類名小寫)) -->
<package name="">
</typeAliases>
@Alias("emp") //別名註釋,寫在類上給類起別名
1.3、建立介面(interface)
public interface UserDao{
public User queryById(int u_id);
public int addUser(int u_id,String u_username,String u_sex);
public int delUser(int u_id);
public int updateUser(int u_id,String u_sex,String u_username);
}
1.4、對映檔案—實現:增刪改查
-
必須有對應的介面的檔案,對映檔案用於實現介面並完成關係對映
1.5、執行程式,操作資料庫
1.5.1、步驟:
-
使用全域性配置檔案得到SqlSessionFactory
-
使用SqlSessionFactory,獲取SqlSession來進行增刪改查,一個SqlSession代表與資料庫的一次會話,用完需要關閉。
-
根據此方法自動為介面建立一個代理物件,去實現增刪改查功能:
-
UserDao userdao = session.getMapper(UserDao.class);
-
-
SqlSession和connection都是非執行緒安全,因此每次都要建立新的物件
public class MybatisQueryTest {
public static void main(String[] args) throws IOException {
//1-載入配置檔案
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2-獲取工廠
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inputStream);
//3-從工廠拿取session
SqlSession session = factory.openSession();
//4-從session裡面拿取mapper
UserDao userdao = session.getMapper(UserDao.class);
//5-業務處理
User user = userdao.queryById(1001);
System.out.println(user);
//6、關閉資源
session.close();
}
}
1.5.2、事務提交—增刪改
-
SqlSession session = factory.openSession();預設為手動提交,
-
必須新增 session.commit(); 否則會回滾
-
可以設定為自動提交:factory.openSession(true);
public class MybatisDelTest {
public static void main(String[] args) throws IOException {
//1-載入配置檔案
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2-獲取工廠
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inputStream);
//3-從工廠拿取session
SqlSession session = factory.openSession();
//4-從session裡面拿取mapper
UserDao userdao = session.getMapper(UserDao.class);
//5-業務處理
int num = userdao.delUser(1015);
System.out.println(num);
//7-提交事務,否則回滾
session.commit();
//6、關閉資源
session.close();
}
}
二、ORM物件關係對映
定義:Object-Relationl Mapping,它的作用是在關係型資料庫和物件之間作一個對映,這樣,我們在具體的操作資料庫的時候,就不需要再去和複雜的SQL語句打交道,只要像平時操作物件一樣操作它就可以了 。
2.1、傳入引數
2.1.1、傳入多個引數
1)預設名稱
介面資訊:
public User query(String name,String sex); //介面方法
對映檔案:
<!-- 形參會封裝為一個map,有預設的key,對映檔案可以直接取用 -->
<select id="query" resultType="pojo.User">
select * from t_user where u_name=#{param1} and u_sex=#{param2}
</select>
2)自定義名稱
介面資訊:
//若是形參多,不易辨認,可以通過@Param("name")來對映,這樣封裝map時的key可以自己設定
public User query(
對映檔案:
<select id="query" resultType="pojo.User">
select * from t_user where u_name=#{name} and u_sex=#{sex}
</select>
2.1.2、傳入pojo物件
若多個形參均為資料模型pojo的屬性,直接傳入pojo
傳入資料型別設定:parameterType="pojo全類名或者別名",也可以不寫,直接使用#{屬性}呼叫屬性值
介面資訊:
//如果多個引數正好是資料模型pojo,我們可以設定模型引數,直接傳入pojo
public boolean addUser(User user); //介面方法
對映檔案:
<!-- 若傳入的引數為pojo,#{屬性值},直接寫屬性值 -->
<select id="queryMan" resultMap="user">
select * from t_user where u_sex=#{sex}
</select>
方法呼叫:
//建立user
User user = new User();
user.setSex("男");
//業務處理
List<User> list = userdao.queryMan(user);
2.1.3、傳入Map(萬能)
如果多個引數正好是資料模型pojo,我們可以設定模型引數,直接傳入pojo
傳入資料型別設定:parameterType="map",也可以不寫,直接使用#{key}呼叫value
介面資訊:
public User querytree(Map<String,Object> map); //介面方法
對映檔案:
<!-- 若傳入的引數為pojo,#{屬性值},直接寫屬性值 -->
<select id="conditionQuery" resultType="pojo.User" parameterType="map">
select * from t_user where u_name=#{name} and u_sex=#{sex}
</select>
方法呼叫:
//業務處理
Map<String,Object> map = new HashMap();
map.put("name", "自來也");
map.put("sex", "男");
User user = userdao.querytree(map);
System.out.println(user);
2.1.4、傳入TO(Transfer Object)資料傳輸物件
Page{
int index;
int size;
}
2.1.5、注意事項
-
形參若傳入物件,物件的屬性在對映檔案中也能使用
#{Object.屬性}來獲取
-
Collection(List、set)以及陣列也會封裝進map,List和陣列還能通過索引呼叫
#{List[0]} //陣列或者list均可如此
-
#{ }和${ }之間的區別
-
#{ }:以預編譯的形式,將引數設定到sql語句中,起PreperStatement的功效,可以防止sql注入。本質就是jdbc裡面的?,一個佔位符。
-
${ }:是一個直接對映過來的資料,JDBC很多地方不支援佔位符,一般只是引數使用佔位符?,因此需要拼接語句的地方,必須使用${ }。
-
#{ }有更豐富的用法:
規定引數的一些規則:
javaType、jdbcType、mode(儲存過程)......
oracle不能識別null型別
需要指定型別
#{email,jdbcType=NULL} 預設jdbcType=OTHER
-
2、獲取返回型別
2.2.1、返回型別為List
介面資訊:
//介面設定返回資料為List<>
public List<User> queryMan(String sex);
對映檔案:
<!-- 這裡的結果型別不能寫List,寫List內部要封裝的型別 -->
<select id="queryMan" resultType="pojo.User">
select * from t_user where u_sex=#{u_sex}
</select>
2.2.2、返回一個Map
優勢:查詢多欄位,返回一個Map,並且可以指定某屬性作為key
介面資訊:
//指定某欄位作為key
對映檔案:
<select id="queryWoman" resultType="map"> //這裡指定的結果型別為為map
select * from t_user where u_sex=#{u_sex}
</select>
3、新增返回資料對映
<!-- 資料庫返回的欄位與返回結果之間的對映 -->
<resultMap type="pojo.User" id="ouruser">
<id column="u_id" property="id"/>
<result column="u_username" property="username"/>
<result column="u_password" property="password"/>
<result column="u_name" property="name"/>
<result column="u_tel" property="tel"/>
<result column="u_flag" property="flag"/>
<result column="u_image" property="image"/>
<result column="u_login_time" property="logintime"/>
<result column="u_sex" property="sex"/>
<result column="u_age" property="age"/>
</resultMap>
<!-- resultType 換做 resultMap,值寫成result 的 id -->
<select id="queryUser" parameterType="pojo.User" resultMap="ouruser">
select * from t_user where u_sex='男' and u_name=#{name}
</select>
三、關聯查詢
3.1、關聯查詢新增返回資料型別
public class CourseInfo {
private Integer id;
private String date;
//Course的引用資料型別
private Course course;
//Staff的引用資料型別
private Staff teacher;
}
public class Course {
private Integer coId;
private String coName;
private String coType;
private Integer coMaxstu;
private String coPrice;
private String coOncetime;
private String coImage;
private String coDescribe;
private String coBeizhu;
private Integer coFlag;
}
public class Staff {
private Integer staffId;
private String staffName;
private String staffGender;
private String staffTel;
private String staffJob;
private String staffPower;
private String stadium;
private String isLogin;
private String staffState;
}
3.1.1、直接對映
<resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
<id column="arrange_id" property="id"/>
<result column="date" property="date"/>
<!-- 直接: 引用資料型別屬性.屬性 -->
<id column="co_id" property="course.coId"/>
<result column="co_name" property="course.coName"/>
<result column="co_type" property="course.coType"/>
<result column="co_maxstu" property="course.coMaxstu"/>
<result column="co_price" property="course.coPrice"/>
<result column="co_oncetime" property="course.coOncetime"/>
<result column="co_image" property="course.coImage"/>
<result column="co_describe" property="course.coDescribe"/>
<result column="co_beizhu" property="course.coBeizhu"/>
<result column="co_flag" property="coFlag"/>
<!-- 直接: 引用資料型別屬性.屬性 -->
<result column="staff_id" property="teacher.staffId"/>
<result column="staff_name" property="teacher.staffName"/>
<result column="staff_gender" property="teacher.staffGender"/>
<result column="staff_tel" property="teacher.staffTel"/>
<result column="staff_job" property="teacher.staffJob"/>
<result column="staff_power" property="teacher.staffPower"/>
<result column="stadium" property="teacher.stadium"/>
<result column="is_login" property="teacher.isLogin"/>
<result column="staff_state" property="teacher.staffState"/>
<id column="ti_id" property="tiId"/>
<result column="ti_time" property="teacher.tiTime"/>
</resultMap>
3.1.2、< association >標籤對映
<resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
<id column="arrange_id" property="id"/>
<result column="date" property="date"/>
<association property="course" javaType="com.hxzy.course.pojo.Course">
<id column="co_id" property="coId"/>
<result column="co_name" property="coName"/>
<result column="co_type" property="coType"/>
<result column="co_maxstu" property="coMaxstu"/>
<result column="co_price" property="coPrice"/>
<result column="co_oncetime" property="coOncetime"/>
<result column="co_image" property="coImage"/>
<result column="co_describe" property="coDescribe"/>
<result column="co_beizhu" property="coBeizhu"/>
<result column="co_flag" property="coFlag"/>
</association>
<association property="teacher" javaType="com.hxzy.course.pojo.Staff">
<id column="staff_id" property="staffId"/>
<result column="staff_name" property="staffName"/>
<result column="staff_gender" property="staffGender"/>
<result column="staff_tel" property="staffTel"/>
<result column="staff_job" property="staffJob"/>
<result column="staff_power" property="staffPower"/>
<result column="stadium" property="stadium"/>
<result column="is_login" property="isLogin"/>
<result column="staff_state" property="staffState"/>
</association>
</resultMap>
3.1.3、< collection >關聯查詢集合封裝
<!-- collection表示返回的型別是個集合 -->
<!-- ofType指定集合內部存放的物件 -->
<collection property="course" ofType="pojo.Course">
<id column="c_id" property="id"></id>
<result column="c_name" property="name"></result>
<result column="c_address" property="address"></result>
<result column="c_flag" property="flag"></result>
</collection>
3.1.4、關聯查詢
<!-- 這裡返回型別同樣使用resultMap -->
<select id="getTuankeSchedule" resultMap="schedule">
SELECT * FROM arrange_course
JOIN course ON arrange_course.co_id = course.co_id
JOIN staff ON arrange_course.staff_id = staff.staff_id
WHERE arrange_course.co_id IN (SELECT co_id FROM course WHERE co_type = '團課')
AND arrange_course.ti_id IS NOT NULL AND arrange_course.DATE IS NOT NULL
</select>
3.2、分步查詢
如何分步:
-
物件的封裝分為多步,首先查詢封裝非引用資料型別
-
再進行通過已查詢資料,獲取引用資料型別的相關資料
<mapper namespace="com.hxzy.course.mapper.ScheduleMapper">
<resultMap id="schedule" type="com.hxzy.course.pojo.Schedule">
<id column="arrange_id" property="id"/>
<result column="date" property="date"/>
<result column="co_id" property="coId"/>
<result column="staff_id" property="staffId"/>
<!-- 在介面內新增一個queryCourseById的方法 -->
<!-- select這個引用資料是通過某個介面內的方法查詢得到的 -->
<!-- column是指定傳入查詢語句的引數 -->
<association property="course" select="com.hxzy.course.mapper.ScheduleMapper.queryCourseById" column="co_id">
<id column="co_id" property="coId"/>
<result column="co_name" property="coName"/>
<result column="co_type" property="coType"/>
<result column="co_maxstu" property="coMaxstu"/>
<result column="co_price" property="coPrice"/>
<result column="co_oncetime" property="coOncetime"/>
<result column="co_image" property="coImage"/>
<result column="co_describe" property="coDescribe"/>
<result column="co_beizhu" property="coBeizhu"/>
<result column="co_flag" property="coFlag"/>
</association>
</resultMap>
<select id="getTuankeSchedule" resultMap="schedule">
SELECT * FROM arrange_course
WHERE arrange_course.co_id IN (SELECT co_id FROM course WHERE co_type = '團課')
AND arrange_course.ti_id IS NOT NULL AND arrange_course.DATE IS NOT NULL
</select>
<select id="queryCourseById" resultType="com.hxzy.course.pojo.Course">
SELECT * FROM course
WHERE course.co_id = #{co_id}
</select>
</mapper>
3.3、延遲查詢
定義:在上述引用物件被使用時才會被查詢。
實現方式:
-
使用了分步查詢
-
對預設配置進行兩個修改
在全域性檔案中進行設定
<settings>
<!-- 表示關聯的值在使用的時候才會被查詢載入,需要開啟 -->
<setting name="lazyLaodingEnabled" value="true"></setting>
<!-- 表示我們所填屬性會被完整載入,需要關閉 -->
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
完成這兩個配置後,我們的分步查詢就變為延遲查詢。分步查詢只做第一步,後續查詢步驟被使用時才會進行
四、動態sql
作用:處理複雜查詢邏輯時,需要動態生成sql語句,這一步可以在java後臺自己進行拼接。而Mybatis已經提供了完整的SQL語句拼接功能。
ps:下面所有的實現方法,傳入的形參均為pojo;一般傳入的是pojo或者map,可以使用屬性名和key來獲取屬性值和value
4.1、< if >標籤
< if >標籤,判斷為真,將查詢語句拼入
<select id="findUserByCondition" parameterMap="user" resultMap="user">
select * from user where type='團課'
<!-- 傳入的是物件,直接輸入屬性 -->
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</select>
4.2、< where >標籤
作用:< if >標籤的最常用應用場景為條件查詢,而最開頭為where,後面是and,拼接相對不容易。< where >會自行判斷,並刪除第一個 and 或者 or
-
解決方式一
<select id="findUserByCondition" parameterMap="user" resultMap="user">
<!-- 直接寫 where 1=1 ,後面全部寫 and -->
select * from user where 1=1
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</select>
-
解決方式二
<select id="findUserByCondition" parameterMap="user" resultMap="user">
select * from user
<!-- 使用where標籤,它會自己判斷,並消除第一個 and 或 or -->
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
or username=#{username}
</if>
</where>
</select>
4.3、< trim >標籤
作用:用於解決拼接整體後的前後綴問題:
< trim >的四個屬性:
-
prefix:給拼接後的字串加一個字首
-
prefixOverrides:給拼接後的字串去掉一個字首
-
suffix:給拼接後的字串新增一個字尾
-
suffixOverrides:給拼接後的字串去掉一個字尾
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
條件查詢的解決方案三:
<!-- 表示在拼接後的字串前方新增一個where,在後方去掉一個and -->
<trim prefix="where" suffixOverrides="and">
<if test="id!=0">
id=#{id} and
</if>
<if test="username!=null">
username=#{username} and
</if>
</trim>
4.4、< choose >分支語句標籤
作用:前端頁面是一個輸入框,輸入框可以輸入姓名、ID、或者其它資訊,輸入一條資訊,但是存在多種可能,這時候的處理就需要分支語句
和< if >的區別:< choose >只能選擇其中一條路走,< if >會都進行判斷
<choose>
<when test="course!=0">
where course.co_id=#{course}
</when>
<when test="teacher!=0">
where staff.staff_id=#{teacher}
</when>
<otherwise>
where 1=1
</otherwise>
</choose>
4.5、< set >標籤
作用:修改屬性時,去除後面的逗號。這裡可以使用< trim >,或者直接用 mybatis-plus 更方便
<update id="updateUser">
update user
<set>
<if test="u_id!=null">
u_id=#{u_id},
</if>
<if test="u_sex!=null">
u_sex=#{u_sex},
</if>
</set>
where username=#{u_username};
</update>
4.6、< foreach >標籤
作用:遍歷傳入的集合
-
collection:指定要遍歷的集合
-
item:將當前遍歷出的元素賦值給指定變數,用#{變數名}能取出變數值即當前遍歷值
-
separator:每個元素直接的分隔符
-
open:遍歷後的結果拼接一個開頭字元
-
close:遍歷後的結果拼接一個結尾字元
-
index:索引
-
遍歷list的時候,index是索引,item是當前值
-
遍歷map的時候,index是key,item是map的值
-
<select id="findUserByIds" parameterType="list" resultMap="user">
select * from user where id in
<foreach collection="array" open="(" close=")" item="id"
separator=",">
#{id}
</foreach>
</select>
4.6.1、mysql環境下的 foreach 批量插入
應用場景:< foreach >標籤也能用於資料批量插入
4.6.2、oracle環境下的 foreach 批量插入