一、mybatis的框架結構
過程說明:
1、 mybatis配置
SqlMapConfig.xml,此文件作爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在SqlMapConfig.xml中加載。
2、 通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
3、 由會話工廠創建sqlSession即會話,操作數據庫需要通過sqlSession進行。
4、 mybatis底層自定義了Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。
5、 Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
6、 Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
7、 Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
注意事項:1.代碼說明
public class User_select {
public static void main(String[] args) throws IOException {
//加載配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//根據mytais的配置創建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//根據SqlSessionFactory創建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通過sqlSession查詢用戶信息(發起數據庫操作)
//第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
//第二個參數parameter,指定 輸入參數
//selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
//根據映射文件中的resultType指定輸出類型
User user = sqlSession.selectOne("test.findUserById", 10);
//遍歷查詢結果
System.out.println(user);
//查詢用戶列表
//selectList表示查詢一個結果集(可以是一條或多條)
List<User> list = sqlSession.selectList("test.findUserList", "張");
System.out.println(list.size());
//關閉sqlSession
sqlSession.close();
}
}
2、sqlSession的使用注意事項
SqlSession使用方法
SqlSessionFactoryBuilder:用於創建SqlSessionFactory,將SqlSessionFactoryBuilder當成工具類使用。
SqlSessionFactory:會話工廠,用於創建SqlSession,SqlSessionFactory一旦創建成功,不用每次創建工廠,建議單例模式使用工廠。如果和spring整合後,由spring來管理SqlSessionFactory,在spring容器中SqlSessionFactory是一個單例對象。
SqlSession:(重點)是一個面向用戶的接口,通過SqlSessionFactory獲取SqlSession,每次數據操作都需要創建新的SqlSession,SqlSession 不是線程安全,最佳應用場合是方法體內,在方法中定義一個SqlSession局部變量。
二、mybatis作爲持久層框架在開發過程中常用的兩種配置方式
1、原始dao層的使用方式
這種情況:需要開發dao接口和dao的實現類。
public interface UserDao {
//根據用戶id查詢用戶信息
public User findUserById(int id) throws Exception;
//添加用戶
public void insertUser(User user) throws Exception;
//查詢用戶列表
public List<User> findUserList()throws Exception;
}
public class UserDaoImpl implements UserDao {
// 注入SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
// 根據SqlSessionFactory創建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通過sqlSession查詢用戶信息(發起數據庫操作)
// 第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
// 第二個參數parameter,指定 輸入參數
// selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
// 根據映射文件中的resultType指定輸出類型
User user = sqlSession.selectOne("test.findUserById", id);
// 遍歷查詢結果
// System.out.println(user);
return user;
}
@Override
public void insertUser(User user) throws Exception {
// 根據SqlSessionFactory創建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
sqlSession.close();
}
@Override
public List<User> findUserList() throws Exception {
// 根據SqlSessionFactory創建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通過sqlSession查詢用戶信息(發起數據庫操作)
// 第一個參數statement:指定mapper映射文件中statement的id,指定 時需要前邊加上statement所屬的命名空間
// 第二個參數parameter,指定 輸入參數
// selectOne返回的是單條記錄,如果select返回多條記錄(list集合),使用selectOne會報錯
// 根據映射文件中的resultType指定輸出類型
// 查詢用戶列表
// selectList表示查詢一個結果集(可以是一條或多條)
List<User> list = sqlSession.selectList("test.findUserList", "張");
System.out.println(list.size());
return list;
}
}
具體代碼參考:https://github.com/libolibolibo/mybatis1217_1.git
2、使用mybatis的動態代理的方法
2.1、使用mybatis動態代理的好處
在開發過程中,只需要開發dao接口、接口對應的mapper.xml文件就可以了;
2.2、使用mybatis動態代理開發的原理
通過mybatis動態代理規則的配置,根據映射文件會生成dao接口實現類的代理對象,這個就相當於mybatis自己通過映射文件的配置,自己生成類dao接口的實現類,進而動態的調用statement語句;
2.3、使用mybatis的動態代理的規則配置
2.31、 在mapper.xml中將namespace設置爲mapper.java接口的全限定名
2.32、 將mapper.java接口的方法名和mapper.xml中statement的id保持一致。
2.33、 將mapper.java接口的方法輸入參數類型和mapper.xml中statement的parameterType保持一致
2.34、 將mapper.java接口的方法輸出 結果類型和mapper.xml中statement的resultType保持一致
2.4注意:mybatis在調用查詢的時候是調用selectOne還是selectList取決於dao接口的返回值,但是resultType保持不變;Mybatis生成代理對象時,根據statement的標籤決定調用 SqlSession的方法(select、insert、update..)
根據上邊接口方法返回值 類型來決定 是調用 selectOne還是selectList,如果返回的是單個對象,動態代理調用selectOne(),如果返回的是集合對象,動態代理調用selectList()。
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM USER WHERE id = #{id}
</select>
<select id="findUserList" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User" >
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
public interface UserMapper {
//根據用戶id查詢用戶信息
public User findUserById(int id) throws Exception;
//查詢用戶列表
public List<User> findUserList(String username)throws Exception;
}
具體的代碼示例:https://github.com/libolibolibo/mybatis1217_2.git
三、mybatis框架的全局的配置文件(一般名稱爲SqlMapConfig.xml)說明,這個在開發過程中使用比較少,一般在與spring整合後就不使用該文件了,但是要了解
1、全局配置文件的作用
SqlMapConfig.xml作爲mybatis的全局配置文件,配置內容包括:數據庫環境、mapper定義、全局參數設置......
- properties(屬性)
properties屬性文件一般使用在,將數據庫連接參數單獨在一個properties文件中配置,好處是:方便系統升級維護。
- settings(全局配置參數)
mybaits框架運行設置一些全局配置參數,比如:開啓二級緩存 ,開啓延遲載。。。
在ibatis中有一些設置性能參數(最大線程數、最大請求數。。),在mybatis中沒有這些性能參數。
注意:設置全局參數會影響mybatis框架運行,謹慎設置。
- typeAliases(類型別名)
在parameterType和resultType設置時,爲了方便編碼,定義別名代替pojo的全路徑。
- typeHandlers(類型處理器)
類型處理器用於java類型和jdbc類型映射:
Mybatis提供 的類型處理器滿足日常需要。
- objectFactory(對象工廠)
- plugins(插件)
- environments(環境集合屬性對象)
- environment(環境子屬性對象)
- transactionManager(事務管理)
- dataSource(數據源)
- mappers(映射器)
<?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="db.properties" />
<!-- 全局配置參數 -->
<!-- settings></settings> -->
<!-- 定義別名 -->
<typeAliases>
<!-- 單個別名定義
type:pojo的路徑
alias:別名的名稱
-->
<!-- <typeAlias type="cn.itcast.mybatis.po.User" alias="user"/> -->
<!-- 批量別名定義
name:指定包名,將包下邊的所有pojo定義別名 ,別名爲類名(首字母大寫或小寫都行)
-->
<package name="cn.itcast.mybatis.po"/>
</typeAliases>
<!-- 和spring整合後 environments配置將廢除 -->
<environments default="development">
<environment id="development">
<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>
<!-- 配置mapper映射文件 -->
<mappers>
<!-- resource方式
在UserMapper.xml,定義namespace爲mapper接口的地址,映射文件通過namespace找到對應的mapper接口文件
-->
<!-- <mapper resource="sqlmap/UserMapper.xml" /> -->
<!-- class方式
class:指定 mapper接口的地址
遵循規則:將mapper.xml和mapper.java文件放在一個目錄 且文件名相同
-->
<!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper"/> -->
<!--批量mapper掃描
遵循規則:將mapper.xml和mapper.java文件放在一個目錄 且文件名相同
-->
<package name="cn.itcast.mybatis.mapper"/>
</mappers>
</configuration>
四、mybatis的映射文件配置說明(一般命名爲xxxMapper.xml)
(一)、主鍵返回問題
需求 :對於新增的記錄,需要將主鍵返回到pojo中,就可以從pojo中獲取新添加的記錄id。
自增主鍵生成 Uuid主鍵生成時機區別:
- 自增主鍵在insert語句執行後生成 的。
- Uuid主鍵在insert語句執行前生成 的。
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!--
keyProperty:將主鍵設置到pojo中哪個屬性中
order:selectKey中sql執行的時機
resultType:selectKey中sql執行的結果類型
LAST_INSERT_ID:是insert後獲取自增主鍵值
-->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address,detail,score)
values(#{username},#{birthday},#{sex},#{address},#{detail},#{score})
</insert>
2、 Uuid主鍵獲取
Uuid主鍵在insert語句執行前生成 的
如果使用uuid獲取主鍵,定義selectkey
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!--
keyProperty:將主鍵設置到pojo中哪個屬性中
order:selectKey中sql執行的時機
resultType:selectKey中sql執行的結果類型
LAST_INSERT_ID:是insert後獲取自增主鍵值
-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address,detail,score)
values(#{id},#{username},#{birthday},#{sex},#{address},#{detail},#{score})
</insert>
如果不用selectKey,也可以在調用SqlSession.insert()前,在輸入參數設置id值 (生成uuid,設置到user的id屬性中。)。
3、Oracle主鍵返回
Oracle沒有自增主鍵,使用oracle的序列(可以生成流水號,類似 自增主鍵)生成主鍵。
通過序列獲取流水號方法:
Select 序列名.next.val from dual
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!--
keyProperty:將主鍵設置到pojo中哪個屬性中
order:selectKey中sql執行的時機
resultType:selectKey中sql執行的結果類型
LAST_INSERT_ID:是insert後獲取自增主鍵值
-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
Select 序列名.next.val from dual
</selectKey>
insert into user(id,username,birthday,sex,address,detail,score)
values(#{id},#{username},#{birthday},#{sex},#{address},#{detail},#{score})
</insert>
(二)、parameterType(輸入類型)
parameterType:用於設置輸入參數的類型。
1、#{}與${}
#{}:表示佔位符,如果獲取簡單類型,#{}中可以使用value或其它名稱 。有效防止sql注入。使用#{}設置參數無需考慮參數的類型。
如果使用#{}比較日期字段,select * from tablename where birthday >=#{birthday}
${}:表示sql拼接,如果獲取簡單類型,${}中只能使用value 。無法防止sql注入。使用${}設置參數必須考慮參數的類型,比如:使用oracle查詢條件是日期類型,如果使用${},必須人爲將${}兩邊加單引號通過to_date轉日期。(只是表示簡單的字符串拼接,而沒有mybatis的映射關係)
Select * from table where birthday >=to_date(‘${birthday}’,’yyyy-MM-dd’)
在沒有特殊要求的情況下,建議使用#{}佔位符
有些情況必須使用${},
比如:需要動態拼接表名,Select * from ${tablename}
動態拼接排序字段:select * from tablename order by ${username} desc
2、 傳遞pojo對象
2.1、parameterType指定 輸入參數爲pojo自定義對象時,在sql中使用${}和#{}獲取pojo的屬性。
2.2、 包裝對象使用
開發中使用pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括用戶查詢條件還包括其它的查詢條件(是另一個pojo),使用包裝對象傳遞輸入參數。
定義包裝對象將查詢條件(pojo)以類組合的方式包裝起來。
parameterType使用包裝對象:
包裝類
public class QueryVo {
//用戶查詢條件
//爲了查詢條件擴展方便,基於po的基礎上自定義的pojo,繼承於Po
private UserCustom userCustom;
private User user;
private int[] ids;
public UserCustom getUserCustom() {
return userCustom;
}
public void setUserCustom(UserCustom userCustom) {
this.userCustom = userCustom;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int[] getIds() {
return ids;
}
public void setIds(int[] ids) {
this.ids = ids;
}
//基它的查詢條件
}
public class UserCustom extends User {
//學生類型,擴展字段
private String groupid;
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
}
映射文件.xml
<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
<select id="findUserList" parameterType="queryVo" resultType="user">
select id,username username_ from user
<!-- where自動將第一個and去掉 -->
<where>
<!-- 這裏調用 queryVo的getUser方法獲 取user的值 -->
<if test="user!=null">
<!-- 這裏調用 queryVo的user的getUsername方法獲取username的值 -->
<if test="user.username!=null and user.username!=''">
and user.username = #{user.username}
</if>
<if test="user.sex!=null and user.sex!=''">
and user.sex = #{user.sex}
</if>
</if>
<if test="ids!=null">
<foreach collection="ids" item="id" open="AND ("
separator="OR" close=")">
id =#{id}
</foreach>
<!-- AND id IN (10,89,16) -->
<!-- <foreach collection="ids" item="id" open="AND id IN ("
separator="," close=")">
#{id}
</foreach> -->
</if>
</where>
</select>
2.3、 傳遞hashmap
parameterType指定 hashmap傳遞輸入參數,#{}和${}中引用map的key。
Sql映射文件定義如下:
<!-- 傳遞hashmap綜合查詢用戶信息 -->
<select id="selectUserByHashmap" parameterType="hashmap" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
(三)、 resultType輸出映射到java對象上
1、返回pojo
resultType:將sql查詢結果集映射爲java對象。要求sql查詢的字段名和resultType指定pojo的屬性名一致,才能映射成功。
如果全部字段和pojo的屬性名不一致,映射生成 的java對象爲空,只要有一個字段和pojo屬性名一致,映射生成 的java對象不爲空。
結論:sql查詢字段名和pojo的屬性名一致才能映射成功。
不管select返回的是單個 對象還是集合對象,resultType要指定單條記錄映射的java對象。
2、返回簡單類型
如果 sql查詢的結果集只有一行且一列,resultType可以返回簡單類型。
3、 返回hashmap
輸出pojo對象可以改用hashmap輸出類型,將輸出的字段名稱作爲map的key,value爲字段值。
l Mapper.xml
<select id="findUserListReturnMap" parameterType="queryVo" resultType="hashmap">
select id,username username_ from user where username = #{user.username} and sex=#{user.sex}
</select>
l Mapper.java
Public Map findUserListReturnMap(QueryVo queryVo);
建議不使用map作爲返回值 ,因爲需要對key在代碼中硬編碼。
(四)、mybatis的動態sql
Mybatis提供 了很多標籤,用於拼接sql語句。
- if標籤
- where標籤
- set標籤
- foreach標籤
<?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">
<mapper namespace="cn.itcast.mybatis.mapper.UserMapperCustom">
<!-- sql片段 -->
<!--
用戶查詢條件
id:在namespace唯 一標識
建議以單表抽取查詢條件
-->
<sql id="query_user_where">
<!-- 這裏調用 queryVo的getUser方法獲 取user的值 -->
<if test="user!=null">
<!-- 這裏調用 queryVo的user的getUsername方法獲取username的值 -->
<if test="user.username!=null and user.username!=''">
and user.username = #{user.username}
</if>
<if test="user.sex!=null and user.sex!=''">
and user.sex = #{user.sex}
</if>
</if>
<if test="ids!=null">
<!-- 根據傳入id數組構造查詢條件 -->
<!-- AND (id =10 OR id =89 OR id=16) -->
<!-- 遍歷ids數組
collection:集合,ids數組
item:遍歷的每個對象
open:開始遍歷時拼接的sql
separator:遍歷的間隔符號
close:結束 遍歷時拼接的sql
-->
<foreach collection="ids" item="id" open="AND ("
separator="OR" close=")">
id =#{id}
</foreach>
<!-- AND id IN (10,89,16) -->
<!-- <foreach collection="ids" item="id" open="AND id IN ("
separator="," close=")">
#{id}
</foreach> -->
</if>
</sql>
<!-- 定義resultMap,將用戶查詢的字段和user這個pojo的屬性名作一個對應關係 -->
<!--
type:最終映射的java對象。
id:resultMap的唯一標識
-->
<resultMap type="user" id="userListResultMap">
<!-- id標籤:查詢結果集的唯 一標識 列(主鍵或唯 一標識 )
column:sql查詢字段名(列名)
property:pojo的屬性名
result標籤:普通列
-->
<id column="id_" property="id"/>
<result column="username_" property="username"/>
<result column="birthday_" property="birthday"/>
</resultMap>
<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
<select id="findUserList" parameterType="queryVo" resultType="user">
select id,username username_ from user
<!-- where自動將第一個and去掉 -->
<where>
<!--
refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
-->
<include refid="query_user_where"/>
</where>
</select>
<!-- 查詢用戶列表 根據用戶名稱和用戶性別查詢用戶列表 -->
<select id="findUserListResultMap" parameterType="queryVo" resultMap="userListResultMap">
select id id_,username username_,birthday birthday_ from user
<!-- where自動將第一個and去掉 -->
<where>
<!--
refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
-->
<include refid="query_user_where"/>
</where>
</select>
<!-- 查詢用戶列表總數 用於分頁查詢 -->
<select id="findUserCount" parameterType="queryVo" resultType="int">
select count(*) from user
<!-- where自動將第一個and去掉 -->
<where>
<!--
refid:指定 sql片段的id,如果要引用其它命名空間的sql片段,需要前邊加namespace
-->
<include refid="query_user_where"/>
</where>
</select>
</mapper>
具體代碼示例,看上面兩個項目的代碼吧!