經過項目逐步深刻了解Mybatis<二>

轉載請務必註明出處,原創不易!java

相關文章:經過項目逐步深刻了解Mybatis<一>

本項目所有代碼地址:Github-Mybatis

Mybatis 解決 jdbc 編程的問題

一、 數據庫連接建立、釋放頻繁形成系統資源浪費從而影響系統性能,若是使用數據庫連接池可解決此問題。mysql

解決:在SqlMapConfig.xml中配置數據連接池,使用鏈接池管理數據庫連接。git

二、 Sql語句寫在代碼中形成代碼不易維護,實際應用sql變化的可能較大,sql變更須要改變java代碼。程序員

解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。github

三、 向sql語句傳參數麻煩,由於sql語句的where條件不必定,可能多也可能少,佔位符須要和參數一一對應。spring

解決:Mybatis自動將java對象映射至sql語句,經過statement中的parameterType定義輸入參數的類型。sql

四、 對結果集解析麻煩,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須要具備很強的經驗和能力才行。

總之,按照用戶的需求在有限的資源環境下只要能作出維護性、擴展性良好的軟件架構都是好架構,因此框架只有適合纔是最好。

Mybatis 開發 dao

兩種方法

  • 原始 dao 開發方法(程序須要編寫 dao 接口和 dao 實現類)(掌握)

  • Mybatis 的 mapper 接口(至關於 dao 接口)代理開發方法(掌握)

需求

將下邊的功能實現Dao:

  • 根據用戶id查詢一個用戶信息

  • 根據用戶名稱模糊查詢用戶信息列表

  • 添加用戶信息

Mybatis 配置文件 SqlMapConfig.xml

Sqlsession 的使用範圍

SqlSession 中封裝了對數據庫的操做,如:查詢、插入、更新、刪除等。

經過 SqlSessionFactory 建立 SqlSession,而 SqlSessionFactory 是經過 SqlSessionFactoryBuilder 進行建立。

一、SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 用於建立 SqlSessionFacoty,SqlSessionFacoty 一旦建立完成就不須要SqlSessionFactoryBuilder 了,由於 SqlSession 是經過 SqlSessionFactory 生產,因此能夠將SqlSessionFactoryBuilder 當成一個工具類使用,最佳使用範圍是方法範圍即方法體內局部變量。

二、SqlSessionFactory

SqlSessionFactory 是一個接口,接口中定義了 openSession 的不一樣重載方法,SqlSessionFactory 的最佳使用範圍是整個應用運行期間,一旦建立後能夠重複使用,一般以單例模式管理 SqlSessionFactory。

三、SqlSession

SqlSession 是一個面向用戶的接口, sqlSession 中定義了數據庫操做,默認使用 DefaultSqlSession 實現類。

執行過程以下:

1)、 加載數據源等配置信息

Environment environment = configuration.getEnvironment();

2)、 建立數據庫連接

3)、 建立事務對象

4)、 建立Executor,SqlSession 全部操做都是經過 Executor 完成,mybatis 源碼以下:

if (ExecutorType.BATCH == executorType) {
      executor = newBatchExecutor(this, transaction);
    } elseif (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
if (cacheEnabled) {
      executor = new CachingExecutor(executor, autoCommit);
    }

5)、 SqlSession的實現類即 DefaultSqlSession,此對象中對操做數據庫實質上用的是 Executor

結論:

每一個線程都應該有它本身的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。所以最佳的範圍是請求或方法範圍(定義局部變量使用)。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。

     打開一個SqlSession;使用完畢就要關閉它。一般把這個關閉操做放到 finally 塊中以確保每次都能執行關閉。以下:
SqlSession session = sqlSessionFactory.openSession();
    try {
          // do work
    } finally {
          session.close();
}

原始 Dao 開發方法

思路:

須要程序員編寫 Dao 接口和 Dao 實現類;

須要在 Dao 實現類中注入 SqlsessionFactory ,在方法體內經過 SqlsessionFactory 建立 Sqlsession。

Dao接口

public interface UserDao    //dao接口,用戶管理
{
    //根據id查詢用戶信息
    public User findUserById(int id) throws Exception;

    //添加用戶信息
    public void addUser(User user) throws Exception;

    //刪除用戶信息
    public void deleteUser(int id) throws Exception;
}

Dao 實現類

public class UserDaoImpl  implements UserDao  //dao接口實現類
{
    //須要在 Dao 實現類中注入 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();
        User user = sqlSession.selectOne("test.findUserById", id);
        sqlSession.close();
        return user;
    }
    @Override
    public void insertUser(User user) throws Exception
    {
        //在方法體內經過 SqlsessionFactory 建立 Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //執行插入的操做
        sqlSession.insert("test.insetrUser", user);
        //提交事務
        sqlSession.commit();
        //釋放資源
        sqlSession.close();
    }
    @Override
    public void deleteUser(int id) throws Exception
    {
        //在方法體內經過 SqlsessionFactory 建立 Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.delete("test.deleteUserById", id);
        //提交事務
        sqlSession.commit();
        sqlSession.close();
    }
}

測試

public class UserDaoImplTest
{
    private SqlSessionFactory sqlSessionFactory;
    //此方法是在 testFindUserById 方法以前執行的
    @Before
    public void setup() throws Exception
    {
        //建立sqlSessionFactory
        //Mybatis 配置文件
        String resource = "SqlMapConfig.xml";
        //獲得配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //建立會話工廠,傳入Mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception
    {
        //建立UserDao的對象
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        //調用UserDao方法
        User user = userDao.findUserById(1);
        System.out.println(user);
    }
}

經過id查詢用戶信息測試結果以下:(其餘的能夠本身在寫測試代碼,原理相似)

問題

原始Dao開發中存在如下問題:

  • Dao方法體存在重複代碼:經過 SqlSessionFactory 建立 SqlSession,調用 SqlSession 的數據庫操做方法

  • 調用 sqlSession 的數據庫操做方法須要指定 statement 的i d,這裏存在硬編碼,不得於開發維護。

  • 調用 sqlSession 的數據庫操做方法時傳入的變量,因爲 sqlsession 方法使用泛型,即便變量類型傳入錯誤,在編譯階段也不報錯,不利於程序員開發。

Mybatis 的 mapper 接口

思路

程序員須要編寫 mapper.xml 映射文件

只須要程序員編寫Mapper接口(至關於Dao接口),需遵循一些開發規範,mybatis 能夠自動生成 mapper 接口類代理對象。

開發規範:

  • 在 mapper.xml 中 namespace 等於 mapper 接口地址

    <mapper namespace="cn.zhisheng.mybatis.mapper.UserMapper"></mapper>
  • 在 xxxmapper.java 接口中的方法名要與 xxxMapper.xml 中 statement 的 id 一致。

  • 在 xxxmapper.java 接口中的輸入參數類型要與 xxxMapper.xml 中 statement 的 parameterType 指定的參數類型一致。

  • 在 xxxmapper.java 接口中的返回值類型要與 xxxMapper.xml 中 statement 的 resultType 指定的類型一致。

UserMapper.java

//根據id查詢用戶信息
    public User findUserById(int id) throws Exception;

UserMapper.xml

<select id="findUserById" parameterType="int" resultType="cn.zhisheng.mybatis.po.User">
        select * from user where id = #{1}
</select>

總結:

以上的開發規範主要是對下邊的代碼進行統一的生成:

User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.insert("test.insetrUser", user);
sqlSession.delete("test.deleteUserById", id);
List<User> list = sqlSession.selectList("test.findUserByName", username);

測試

測試以前記得在 SqlMapConfig.xml 文件中添加加載映射文件 UserMapper.xml:

<mapper resource="mapper/UserMapper.xml"/>

測試代碼:

public class UserMapperTest
{
    private SqlSessionFactory sqlSessionFactory;
    //此方法是在 testFindUserById 方法以前執行的
    @Before
    public void setup() throws Exception
    {
        //建立sqlSessionFactory
        //Mybatis 配置文件
        String resource = "SqlMapConfig.xml";
        //獲得配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //建立會話工廠,傳入Mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //建立usermapper對象,mybatis自動生成代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //調用UserMapper的方法
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
}

經過id查詢用戶信息測試結果以下:(其餘的請本身根據上下文寫測試代碼,或者去看我 Github-Mybatis學習筆記 上看我這個項目的所有代碼)

經過姓名查詢用戶信息:

代理對象內部調用 selectOne 或者 selectList

  • 若是 mapper 方法返回單個 pojo 對象(非集合對象),代理對象內部經過 selectOne 查詢數據庫

  • 若是 mapper 方法返回集合對象,代理對象內部經過 selectList 查詢數據庫

mapper接口方法參數只能有一個是否影響系統開發

mapper 接口方法參數只能有一個,系統是否不利於維護?

系統框架中,dao層的代碼是被業務層公用的。

即便 mapper 接口只有一個參數,可使用包裝類型的 pojo 知足不一樣的業務方法的需求。

注意:持久層方法的參數能夠包裝類型、map.... ,service方法中不建議使用包裝類型。(不利於業務層的可擴展性)

SqlMapConfig.xml 文件

Mybatis 的全局配置變量,配置內容和順序以下:

properties(屬性)

settings(全局配置參數)

typeAliases(類型別名)

typeHandlers(類型處理器)

objectFactory(對象工廠)

plugins(插件)

environments(環境集合屬性對象)

​ environment(環境子屬性對象)

​ transactionManager(事務管理)

​ dataSource(數據源)

mappers(映射器)

properties 屬性

需求:將數據庫鏈接參數單獨配置在 db.properties 中,只須要在 SqlMapConfig.xml 中加載該配置文件 db.properties 的屬性值。在 SqlMapConfig.xml 中就不須要直接對數據庫的鏈接參數進行硬編碼了。方便之後對參數進行統一的管理,其餘的xml文件能夠引用該 db.properties 。

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

那麼 SqlMapConfig.xml 中的配置變成以下:

<!--加載配置文件-->
    <properties resource="db.properties"></properties>
    <!-- 和spring整合後 environments配置將廢除-->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事務管理,事務由 Mybatis 控制-->
            <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>

配置完成後咱們測試一下是否可以和剛纔同樣的可以成功呢?那麼我就先在db.properties中把數據庫密碼故意改錯,看是不是正確的?不出意外的話是會報錯的。

注意: MyBatis 將按照下面的順序來加載屬性:

  • 在 properties 元素體內定義的屬性首先被讀取。

  • 而後會讀取 properties 元素中 resource 或 url 加載的屬性,它會覆蓋已讀取的同名屬性。

  • 最後讀取 parameterType 傳遞的屬性,它會覆蓋已讀取的同名屬性。

所以,經過parameterType傳遞的屬性具備最高優先級,resource或 url 加載的屬性次之,最低優先級的是 properties 元素體內定義的屬性。

建議:

  • 不要在 properties 元素體內添加任何屬性值,只將屬性值定義在 db.properties 文件之中。

  • 在 db.properties 文件之中定義的屬性名要有必定的特殊性。如 xxx.xxx.xxx

settings(全局配置參數)

Mybatis 框架在運行時能夠調整一些運行參數

好比:開啓二級緩存、開啓延遲加載。。。

typeAliases(類型別名)

需求:

在mapper.xml中,定義不少的statement,statement須要parameterType指定輸入參數的類型、須要resultType指定輸出結果的映射類型。

若是在指定類型時輸入類型全路徑,不方便進行開發,能夠針對parameterType或resultType指定的類型定義一些別名,在mapper.xml中經過別名定義,方便開發。

Mybatis支持的別名:

別名 映射的類型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal

自定義別名:

在 SqlMapConfig.xml 中配置:(設置別名)

<typeAliases>
    <!-- 單個別名定義 -->
    <typeAlias alias="user" type="cn.zhisheng.mybatis.po.User"/>
    <!-- 批量別名定義,掃描整個包下的類,別名爲類名(首字母大寫或小寫均可以) -->
    <package name="cn.zhisheng.mybatis.po"/>
    <package name="其它包"/>
</typeAliases>

在 UserMapper.xml 中引用別名:( resultType 爲 user )

<select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
</select>

測試結果:

typeHandlers(類型處理器)

mybatis中經過typeHandlers完成jdbc類型和java類型的轉換。

一般狀況下,mybatis提供的類型處理器知足平常須要,不須要自定義.

mybatis支持類型處理器:

類型處理器 Java類型 JDBC類型
BooleanTypeHandler Boolean,boolean 任何兼容的布爾值
ByteTypeHandler Byte,byte 任何兼容的數字或字節類型
ShortTypeHandler Short,short 任何兼容的數字或短整型
IntegerTypeHandler Integer,int 任何兼容的數字和整型
LongTypeHandler Long,long 任何兼容的數字或長整型
FloatTypeHandler Float,float 任何兼容的數字或單精度浮點型
DoubleTypeHandler Double,double 任何兼容的數字或雙精度浮點型
BigDecimalTypeHandler BigDecimal 任何兼容的數字或十進制小數類型
StringTypeHandler String CHAR和VARCHAR類型
ClobTypeHandler String CLOB和LONGVARCHAR類型
NStringTypeHandler String NVARCHAR和NCHAR類型
NClobTypeHandler String NCLOB類型
ByteArrayTypeHandler byte[] 任何兼容的字節流類型
BlobTypeHandler byte[] BLOB和LONGVARBINARY類型
DateTypeHandler Date(java.util) TIMESTAMP類型
DateOnlyTypeHandler Date(java.util) DATE類型
TimeOnlyTypeHandler Date(java.util) TIME類型
SqlTimestampTypeHandler Timestamp(java.sql) TIMESTAMP類型
SqlDateTypeHandler Date(java.sql) DATE類型
SqlTimeTypeHandler Time(java.sql) TIME類型
ObjectTypeHandler 任意 其餘或未指定類型
EnumTypeHandler Enumeration類型 VARCHAR-任何兼容的字符串類型,做爲代碼存儲(而不是索引)。

mappers(映射器)

  • <mapper resource=" " />

使用相對於類路徑的資源,如:<mapper resource="sqlmap/User.xml" />

  • <mapper url=" " />

使用徹底限定路徑
如:<mapper url="file://D:workspacemybatisconfigsqlmapUser.xml" />

  • <mapper class=" " />

使用 mapper 接口類路徑

如:<mapper class="cn.zhisheng.mybatis.mapper.UserMapper"/>

注意:此種方法要求 mapper 接口名稱和 mapper 映射文件名稱相同,且放在同一個目錄中。

  • <mapper name=" " />

註冊指定包下的全部mapper接口
如:<package name="cn.zhisheng.mybatis.mapper"/>
注意:此種方法要求 mapper 接口名稱和 mapper 映射文件名稱相同,且放在同一個目錄中。

Mapper.xml 映射文件

Mapper.xml映射文件中定義了操做數據庫的sql,每一個sql是一個statement,映射文件是mybatis的核心。

輸入映射

經過 parameterType 指定輸入參數的類型,類型能夠是簡單類型、hashmap、pojo的包裝類型。

傳遞 pojo 包裝對象 (重點)

開發中經過pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不只包括用戶查詢條件還包括其它的查詢條件(好比將用戶購買商品信息也做爲查詢條件),這時可使用包裝對象傳遞輸入參數。

  • 定義包裝對象

定義包裝對象將查詢條件(pojo)以類組合的方式包裝起來。

UserQueryVo.java

public class UserQueryVo    //用戶包裝類型
{
    //在這裏包裝所須要的查詢條件
    //用戶查詢條件
    private UserCustom userCustom;
    public UserCustom getUserCustom()
    {
        return userCustom;
    }
    public void setUserCustom(UserCustom userCustom)
    {
        this.userCustom = userCustom;
    }
    //還能夠包裝其餘的查詢條件,好比訂單、商品
}

UserCustomer.java

public class UserCustom extends User    //用戶的擴展類
{
    //能夠擴展用戶的信息
}
  • UserMapper.xml 文件

    <!--用戶信息綜合查詢
        #{userCustom.sex} :取出pojo包裝對象中的性別值
        #{userCustom.username} :取出pojo包裝對象中的用戶名稱
        -->
        <select id="findUserList" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="cn.zhisheng.mybatis.po.UserCustom">
            select * from user where user.sex = #{userCustom.sex} and user.username like  '%${userCustom.username}%'
        </select>
  • UserMapper.java

    //用戶信息綜合查詢
    public List<UserCustom> findUserList(UserQueryVo userQueryVo) throws Exception;
  • 測試代碼

    //測試用戶信息綜合查詢
        @Test
        public void testFindUserList() throws Exception
        {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //建立usermapper對象,mybatis自動生成代理對象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //建立包裝對象,設置查詢條件
            UserQueryVo userQueryVo = new UserQueryVo();
            UserCustom userCustom = new UserCustom();
            userCustom.setSex("男");
            userCustom.setUsername("張小明");
            userQueryVo.setUserCustom(userCustom);
            //調用UserMapper的方法
            List<UserCustom> list = userMapper.findUserList(userQueryVo);   
            System.out.println(list);
        }
  • 測試結果

輸出映射

  • resultType

  • 使用 resultType 進行輸出映射,只有查詢出來的列名和 pojo 中的屬性名一致,該列才能夠映射成功。

  • 若是查詢出來的列名和 pojo 中的屬性名所有不一致,沒有建立 pojo 對象。

  • 只要查詢出來的列名和 pojo 中的屬性有一個一致,就會建立 pojo 對象。

輸出簡單類型

需求:用戶信息綜合查詢列表總數,經過查詢總數和上邊用戶綜合查詢列表才能夠實現分頁

實現:

<!--用戶信息綜合查詢總數
    parameterType:指定輸入的類型和findUserList同樣
    resultType:輸出結果類型爲 int
    -->
    <select id="findUserCount" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="int">
      select count(*) from user where user.sex = #{userCustom.sex} and user.username like  '%${userCustom.username}%'
    </select>
//用戶信息綜合查詢總數
    public int findUserCount(UserQueryVo userQueryVo) throws Exception;
//測試用戶信息綜合查詢總數
    @Test
    public void testFindUserCount() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //建立usermapper對象,mybatis自動生成代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //建立包裝對象,設置查詢條件
        UserQueryVo userQueryVo = new UserQueryVo();
        UserCustom userCustom = new UserCustom();
        userCustom.setSex("男");
        userCustom.setUsername("張小明");
        userQueryVo.setUserCustom(userCustom);
        //調用UserMapper的方法
        System.out.println(userMapper.findUserCount(userQueryVo));
    }

注意:查詢出來的結果集只有一行且一列,可使用簡單類型進行輸出映射。

輸出pojo對象和pojo列表

不論是輸出的pojo單個對象仍是一個列表(list中包括pojo),在mapper.xml中resultType指定的類型是同樣的。

在mapper.java指定的方法返回值類型不同:

一、輸出單個pojo對象,方法返回值是單個對象類型

//根據id查詢用戶信息
    public User findUserById(int id) throws Exception;

二、輸出pojo對象list,方法返回值是List<Pojo>

//根據用戶名查詢用戶信息
    public List<User> findUserByUsername(String userName) throws  Exception;

resultType總結:

輸出pojo對象和輸出pojo列表在sql中定義的resultType是同樣的。

返回單個pojo對象要保證sql查詢出來的結果集爲單條,內部使用session.selectOne方法調用,mapper接口使用pojo對象做爲方法返回值。

返回pojo列表表示查詢出來的結果集可能爲多條,內部使用session.selectList方法,mapper接口使用List<pojo>對象做爲方法返回值。

  • resultMap

resultType 能夠指定 pojo 將查詢結果映射爲 pojo,但須要 pojo 的屬性名和 sql 查詢的列名一致方可映射成功。

若是sql查詢字段名和pojo的屬性名不一致,能夠經過resultMap將字段名和屬性名做一個對應關係 ,resultMap實質上還須要將查詢結果映射到pojo對象中。

resultMap能夠實現將查詢結果映射爲複雜類型的pojo,好比在查詢結果映射對象中包括pojo和list實現一對一查詢和一對多查詢。

使用方法:

一、定義 resultMap

二、使用 resultMap 做爲 statement 的輸出映射類型

將下面的 sql 使用 User 完成映射

select id id_, username username_ from user where id = #{value}

User 類中屬性名和上邊查詢的列名不一致。

因此須要:

一、定義 resultMap

<!--定義 resultMap
    將select id id_, username username_ from user where id = #{value} 和User類中的屬性作一個映射關係
    type: resultMap最終映射的java對象類型
    id:對resultMap的惟一標識
    -->
    <resultMap id="userResultMap" type="user">
        <!--id表示查詢結果中的惟一標識
        column:查詢出來的列名
        property:type指定pojo的屬性名
        最終resultMap對column和property作一個映射關係(對應關係)
        -->
        <id column="id_" property="id"/>

        <!--result: 對普通結果映射定義
        column:查詢出來的列名
        property:type指定pojo的屬性名
        最終resultMap對column和property作一個映射關係(對應關係)
        -->
        <result column="username_" property="username"/>
    </resultMap>

二、使用 resultMap 做爲 statement 的輸出映射類型

<!--使用 resultMap 做爲輸出映射類型
        resultMap="userResultMap":其中的userResultMap就是咱們剛纔定義的 resultMap 的id值,若是這個resultMap在其餘的mapper文件中,前邊須加上namespace -->
    <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
        select id id_, username username_ from user where id = #{value}
    </select>

三、UserMapper.java

//根據id查詢用戶信息,使用 resultMap 輸出
public User findUserByIdResultMap(int id) throws Exception;

四、測試

//測試根據id查詢用戶信息,使用 resultMap 輸出
    @Test
    public void testFindUserByIdResultMap() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //建立usermapper對象,mybatis自動生成代理對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //調用UserMapper的方法
        User user = userMapper.findUserByIdResultMap(1);
        System.out.println(user);
    }

五、測試結果

動態 SQL

經過mybatis提供的各類標籤方法實現動態拼接sql。

需求:

用戶信息綜合查詢列表和用戶信息查詢列表總數這兩個 statement的定義使用動態sql。

對查詢條件進行判斷,若是輸入的參數不爲空才進行查詢條件拼接。

UserMapper.xml (findUserList的配置以下,那麼findUserCount的也是同樣的,這裏就不所有寫出來了)

<select id="findUserList" parameterType="cn.zhisheng.mybatis.po.UserQueryVo" resultType="cn.zhisheng.mybatis.po.UserCustom">
        select * from user
        <!--where能夠自動的去掉條件中的第一個and-->
        <where>
            <if test="userCustom != null">
                <if test="userCustom.sex != null and userCustom.sex != ''">
                    and user.sex = #{userCustom.sex}
                </if>
                <if test="userCustom.username != null">
                    and user.username like  '%${userCustom.username}%'
                </if>
            </if>
        </where>
    </select>

測試代碼:由於設置了動態的sql,若是不設置某個值,那麼條件就不會拼接在sql上

因此咱們就註釋掉設置username的語句

//userCustom.setUsername("張小明");

測試結果:

Sql 片斷

經過上面的其實看到在 where sql語句中有不少重複代碼,咱們能夠將其抽取出來,組成一個sql片斷,其餘的statement就能夠引用這個sql片斷,利於系統的開發。

這裏咱們就拿上邊sql 中的where定義一個sq片斷以下:

<!--sql片斷
    id:惟一標識
    經驗:是基於單表來定義sql片斷,這樣的話sql片斷的可重用性才高
    通常不包含where
    -->
    <sql id="query_user_where">
        <if test="userCustom != null">
            <if test="userCustom.sex != null and userCustom.sex != ''">
                and user.sex = #{userCustom.sex}
            </if>
            <if test="userCustom.username != null">
                and user.username like  '%${userCustom.username}%'
            </if>
        </if>
    </sql>

那麼咱們該怎樣引用這個sql片斷呢?以下:

select * from user
        <where>
        <!--refid: 指定sql片斷的id,若是是寫在其餘的mapper文件中,則須要在前面加上namespace-->
            <include refid="query_user_where"/>
        </where>

測試的話仍是那樣了,就不繼續說了,前面已經說了不少了。

foreach

向sql傳遞數組或List,mybatis使用foreach解析

需求:

在用戶查詢列表和查詢總數的statement中增長多個id輸入查詢。

sql語句以下:

SELECT * FROM USER WHERE id=1 OR id=10 ORid=16
或者
SELECT * FROM USER WHERE id IN(1,10,16)

在輸入參數類型中添加 List<Integer> ids 傳入多個 id

public class UserQueryVo    //用戶包裝類型
{
    //傳入多個id
    private List<Integer> ids;
}

修改 UserMapper.xml文件

WHERE id=1 OR id=10 OR id=16

在查詢條件中,查詢條件定義成一個sql片斷,須要修改sql片斷。

<if test="ids!=null">
            <!-- 使用 foreach遍歷傳入ids
            collection:指定輸入 對象中集合屬性
            item:每一個遍歷生成對象中
            open:開始遍歷時拼接的串
            close:結束遍歷時拼接的串
            separator:遍歷的兩個對象中須要拼接的串
             -->
             <!-- 使用實現下邊的sql拼接:
              AND (id=1 OR id=10 OR id=16) 
              -->
            <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
                <!-- 每一個遍歷須要拼接的串 -->
                id=#{user_id}
            </foreach>
            
            <!-- 實現  「 and id IN(1,10,16)」拼接 -->
            <!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
                每一個遍歷須要拼接的串
                #{user_id}
            </foreach> -->    
            </if>

測試代碼:

//傳入多個id
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(10);
ids.add(16);
//將ids傳入statement中
userQueryVo.setIds(ids);

期待後續的文章吧!

相關文章
相關標籤/搜索