天天看點

持久層架構學習——MyBatis(二)

持久層架構學習——MyBatis(二)

      • MyBatis映射檔案(補充)
        • 動态SQL語句
          • if 标簽
          • foreach 标簽
        • SQL片段的抽取
      • MyBatis核心配置檔案(補充)
        • typeHandlers 标簽
        • plugins 标簽
      • MyBatis多表操作
        • 一對一查詢
        • 一對多查詢
        • 多對多查詢
      • MyBatis注解開發
        • 使用注解實作簡單CRUD
        • 注解開發的一對一多表操作
        • 注解開發的一對多多表操作
        • 注解開發的多對多多表操作
      • 補充/技巧

MyBatis映射檔案(補充)

在實際開發中使用Dao層的MyBatis實作是非常普遍的,MyBatis的Dao層的實作有兩個,最重要也是最常用的是接口代理方式,這種方式不需要我們去寫接口的實作類,隻需要通過MyBatis的映射配置檔案就可以幫助我們處理複雜的資料庫操作。在一些複雜業務邏輯中,複雜的SQL語句占有大多數,同樣MyBatis的映射檔案也可以幫助我們進行處理,進而不去實作接口就能達到我們想要的結果了。

動态SQL語句

動态SQL語句簡而言之就是可以動态變化的,有時我們的業務邏輯比較複雜,我們所寫的SQL語句一定是動态變化的,否則我們的SQL語句就不能滿足我們的要求了。例如:

select * from user where id=#{id} and username=#{username} and password=#{password};
           

上述的SQL語句并不是動态SQL,此時我們必須給足SQL語句所需的三個條件,缺少任何一個都會使得查詢的結果失敗,最後導緻我們想查的資料查不出來。顯然在實際的業務中,我們不會将所有條件的查詢語句都寫出來,隻讓使用者去選擇一種查詢模式,而背景伺服器去調用方法的時候執行動态SQL,将使用者需要的給與使用者,滿足使用者的查詢條件,是以這就是動态SQL的作用。動态SQL有很多種模式,在MyBatis中是利用不同的标簽來實作動态SQL的,如下所示:

if 标簽

if标簽如同我們程式設計語言中的if語句一樣,可以在标簽中設定判斷條件,如果滿足則在SQL語句中加入if标簽内部的部分語句,不滿足則進行下一步操作,示例如下:

映射檔案中的SQL如下:

<select id="findByCondition" parameterType="user" resultType="com.jclight.domain.User">
    select * from user
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
    </where>
</select>
           

測試如下:

@Test
public void findByCondition() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User condition = new User();
    condition.setId(1);
    condition.setUsername("劉一");
    //        condition.setPassword("123");
    List<User> userList = mapper.findByCondition(condition);
    System.out.println(userList);
}
           

此時,動态SQL會根據提供的條件進行判斷并擴充SQL語句,若沒有條件則查詢表中的全部資料,這裡使用的标簽就是

<if>

标簽,其中的屬性

test

的值為判斷條件,MyBatis會根據使用者送出的資料進行判斷,如果if判斷為真則擴充SQL語句,if判斷為假則不進行擴充。注意,這裡的單元測試模拟了使用者送出的資料,在實際開發中應為前端界面表單送出的資料或其他方式等。

foreach 标簽

它的作用是循環執行SQL的拼接操作,示例如下:

映射檔案中的SQL如下:

<select id="findByIds" resultType="com.jclight.domain.User" parameterType="list">
    select * from user
    <where>
        <foreach collection="list" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
           

測試如下:

@Test
public void findByIds() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    //模拟ids的資料
    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(3);
    ids.add(5);
    List<User> userList = mapper.findByIds(ids);
    System.out.println(userList);
}
           

這裡的動态SQL用到了foreach标簽,該标簽的屬性

collection

的值表示的是傳遞過來的參數的類型,屬性

open

的值表示foreach從哪個地方之後開始,屬性

close

的值表示從哪個地方之後結束,

item

表示資料的辨別,

separator

表示有多個資料時以什麼作為分隔符。foreach标簽内部寫查詢條件代表的占位符。

SQL片段的抽取

在映射檔案中可以将重複的SQL語句提取出來,使用時用

include

引用即可,最終達到SQL重用的目的。示例如下:

<sql id="selectUser">select * from user</sql>
<select id="findByCondition" parameterType="user" resultType="com.jclight.domain.User">
    <include refid="selectUser"/>
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
    </where>
</select>
           

從上述的代碼中我們可以看出,使用标簽sql即可将重複的SQL語句提取出來,這裡的屬性

id

的值代表該SQL語句的辨別。如果要用的時候,可以使用标簽include去引用這個SQL語句,這裡的屬性

refid

的值代表要使用哪個SQL語句,對應的就是sql标簽中的id值。

MyBatis核心配置檔案(補充)

typeHandlers 标簽

該标簽的意思是類型處理器,在我們使用MyBatis架構,在預處理語句中設定參數或從結果集取值時,都會用到類型處理器将擷取的值以合适的方式轉換成Java類型。部分對應關系如下(預設類型處理器):

類型處理器 Java類型 JDBC類型
BooleanTypeHandler

java.lang.Boolean

,boolean
BOOLEAN
ByteTypeHandler

java.lang.Byte

,byte
NUMERIC或BYTE
ShortTypeHandler

java.lang.Short

,short
NUMERIC或SHORT INTEGER
IntegerTypeHandler

java.lang.Integer

,int
NUMERIC或INTEGER
LongTypeHandler

java.lang.Long

,long
NUMERIC或LONG INTEGER

MyBatis為我們提供了部分預設的類型處理器,但是有時會遇到不符合的類型,此時就需要我們去自定義類型處理器。具體步驟如下:

首先,實作MyBatis提供的TypeHandler接口或者內建BaseTypeHandler類。然後,可以選擇性的将它映射到一個JDBC類型。例如:Java中的Date類型,存儲到資料庫的類型要求為1970年至今的毫秒數,從資料庫取出來時轉換為Date,存放到資料庫時轉換為varchar。這種就需要我們自定義轉化器。它的具體開發步驟如下:

①定義轉換類繼承BaseTypeHandler;②覆寫四個未實作的方法(其中的setNonNullParameter是為Java程式設定資料到資料庫的回調方法,getNullableResult是為查詢時MySql的字元串類型轉換為Java的Type類型的方法);③完成上述兩部分後要在MyBatis核心配置檔案中進行注冊;④測試是否轉換成功

示例如下:

轉換器類如下:

public class DateTypeHandler extends BaseTypeHandler<Date> {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        long time = date.getTime();
        preparedStatement.setLong(i, time);
    }
    @Override
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        //擷取結果集中需要的資料(Long),将其轉換成Date類并傳回
        long aLong = resultSet.getLong(s);
        return new Date(aLong);
    }
    @Override
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        long aLong = resultSet.getLong(i);
        return new Date(aLong);
    }
    @Override
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        long aLong = callableStatement.getLong(i);
        return new Date(aLong);
    }
}
           

核心配置檔案如下:

<?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="jdbc.properties"/>
    <!--  自定義别名  -->
    <typeAliases>
        <typeAlias type="com.jclight.domain.User" alias="user"/>
    </typeAliases>
    <!--  自定義類型處理器  -->
    <typeHandlers>
        <typeHandler handler="com.jclight.handler.DateTypeHandler"/>
    </typeHandlers>
    <!--  資料源環境  -->
    <environments default="Developement">
        <environment id="Developement">
            <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>
    <!--  加載映射檔案  -->
    <mappers>
        <mapper resource="com\jclight\mapper\UserMapper.xml"/>
    </mappers>
</configuration>
           

映射檔案如下:

<?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="com.jclight.mapper.UserMapper">
    <insert id="saveUser" parameterType="user">
        insert into user values(#{id},#{username},#{password},#{birthday});
    </insert>
    <select id="findById" resultType="com.jclight.domain.User" parameterType="int">
        select * from user where id=#{id};
    </select>
</mapper>
           

此時我們進行插入或查詢測試,最後的結果為資料庫中存放Date的毫秒值,而經過轉換器類處理後讀取出來的資料為Date類型。由此,這種方式極大的友善了我們遇到資料庫中的類型和Java代碼中的類型不同的問題,也由此可以對一些資料做一定的處理,達到安全性的标準。

plugins 标簽

MyBatis可以使用第三方插件對功能進行拓展,例如比較複雜的分頁功能的實作,在MyBatis中我們可以在plugins标簽下引入分頁助手PageHelper,它将複雜的分頁操作封裝起來,使用簡單的方式即可獲得分頁的資料。操作步驟:首先導入PageHelper的坐标,然後在MyBatis核心配置檔案中配置該插件,最後進行測試即可。示例如下:

導入坐标:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>
           

在MyBatis核心配置檔案中配置插件:

<plugins>
     <plugin interceptor="com.github.pagehelper.PageHelper">
         <property name="dialect" value="mysql"/>
     </plugin>
</plugins>
           

注意:這裡的屬性interceptor的值為分頁助手的PageHelper類,下面的name的值dialect表示該分頁是采用哪種語言來寫的,我們這裡是mysql是以value的值也為mysql。如果目前的項目的資料庫為sqlserver,那這裡的value值為sqlserver。對于使用其他插件而言,隻需要修改interceptor的值,其他的配置隻需檢視相關插件的文檔進行配置即可。

測試:

在我們之前的測試代碼中加入該語句即可實作分頁操作,這裡

startPage

的第一個參數表示第幾頁的資料,第二個參數表示每頁顯示幾條資料。在我們使用日志去處理執行時,即可看到控制台輸出的執行流程,在這裡明顯可以觀察到該插件自動幫我們加入了limit語句。

上述的操作是簡單的分頁功能實作操作,如果我們需要讓控制台輸出目前頁的頁碼、下一頁的頁碼、剩餘幾頁等資料時,我們可以更進一步去運用該插件,具體如下:

//獲得與分頁相關的參數
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("目前頁:" + pageInfo.getPageNum());
System.out.println("每頁顯示條數:" + pageInfo.getPageSize());
System.out.println("總條數:" + pageInfo.getTotal());
System.out.println("總頁數:" + pageInfo.getPages());
System.out.println("上一頁:" + pageInfo.getPrePage());
System.out.println("下一頁:" + pageInfo.getNextPage());
System.out.println("是否為第一頁:" + pageInfo.isIsFirstPage());
System.out.println("是否為最後一頁:" + pageInfo.isIsLastPage());
           

這裡不需要再對配置檔案進行處理,隻需要在之前進行測試的地方加上上面的代碼片段即可,這裡的參數userList為查詢到的結果,根據PageInfo的相關API我們可以擷取分頁的一些相關參數。使用該插件比我們自己手動去實作分頁要簡單的多。PageInfo的原理是利用查詢到的資料進行逆向推理,最終進行處理得到相關的資料。根據這些我們就可以在WEB工程中快速實作分頁功能的編寫了。

MyBatis多表操作

一對一查詢

資料庫中多個表之間的關系可以有三種關系(一對一、一對多或多對一、多對多)。之前我們對一對一關系的多表操作是利用一個VO對象,将兩個實體類封裝在一個VO對象中進行操作的,那麼在這裡使用架構進行一對一關系的多表操作具體步驟如下:首先搭建基本的運作環境,然後将需要操作的資料表封裝成實體對象,最後進行配置和測試即可。這裡面的前兩步和之前一樣,具體配置如下:

根據建立的實體類建立對應的Mapper類接口,并聲明一個查詢方法,然後根據該Mapper接口建立對應的映射檔案具體如下:

<mapper namespace="com.jclight.mapper.OrderMapper">
    <resultMap id="orderMap" type="order">
        <id column="oid" property="id"/>
        <result column="ordertime" property="ordertime"/>
        <result column="total" property="total"/>
        <result column="uid" property="user.id"/>
        <result column="username" property="user.username"/>
        <result column="password" property="user.password"/>
        <result column="birthday" property="user.birthday"/>
    </resultMap>
    <select id="multiTableFindAll" resultMap="orderMap">
        SELECT *,o.id oid FROM orders o,`user` u WHERE o.uid=u.id
    </select>
</mapper>
           

注意:這裡的

<resultMap id="orderMap" type="order">

中的屬性id可随意命名,type屬性的值為要操作的實體類對象,由于這裡在核心配置檔案中配置了自定義類型别名,是以這裡為order。下面的

<id column="oid" property="id"/>

中的屬性column表示資料表的字段名稱,property表示實體的屬性名稱。注意這裡的id為

oid

表示在執行SQL語句時一定也要含有

oid

,否則将會報錯導緻查詢不到結果。

<result column="ordertime" property="ordertime"/>

表示對結果集配置(封裝),column屬性表示資料庫表的字段的名字,property表示對應的實體的屬性名稱。這些組成了一個resultMap,通過定義好的resultMap,我們就可以将查詢的結果封裝在一個實體對象中,利用該實體對象進而得到另一個實體對象在資料庫中的值。這裡的SQL标簽中的resultType換為resultMap,它的屬性值對應的就是之前配置好的resultMap的id。在SQL語句的編寫中一定要含有上面含有的自定義的名稱

oid

,因為這個别名是自己定義的,在資料表中沒有,一旦SQL語句中沒有聲明,那麼執行的查詢結果将不存在或報錯。

除此之外,還有另一種方式處理實體對象中含有另一個實體對象的方法,具體如下:

<resultMap id="orderMap" type="order">
    <id column="oid" property="id"/>
    <result column="ordertime" property="ordertime"/>
    <result column="total" property="total"/>
    <association property="user" javaType="user">
        <id column="uid" property="id"/>
        <id column="username" property="username"/>
        <id column="password" property="password"/>
        <id column="birthday" property="birthday"/>
    </association>
</resultMap>
<select id="multiTableFindAll" resultMap="orderMap">
    SELECT *,o.id oid FROM orders o,`user` u WHERE o.uid=u.id
</select>
           

上述的

<association property="user" javaType="user">

中的屬性property的值為目前實體的屬性名稱,屬性javaType的值為目前實體的屬性的類型。

測試方法同之前一樣,使用該方式進行配置後可以快速的操作多個表,不需要去實作複雜的多表操作業務邏輯。

一對多查詢

一般而言我們的一對多關系的多表操作,通常在實體中将某個實體封裝成集合,通過VO對象來進行操作。例如:使用者和訂單的關系,這裡在使用者的實體中封裝訂單的資料,并将訂單封裝為集合對象,通過集合對象我們可以實作一個使用者含有多個訂單的需求。在不使用持久層架構前,這些都放在一個VO對象的實體中進行處理,通過VO對象來實作這種關系,使用持久層架構後我們隻需要在關系為1的實體中使用集合封裝關系為N的實體對象,然後對映射檔案進行配置即可。示例如下:

關系為1的實體類(部分代碼):

private List<Order> orders;
public List<Order> getOrders() {
    return orders;
}
public void setOrders(List<Order> orders) {
    this.orders = orders;
}
           

映射檔案:

<mapper namespace="com.jclight.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="birthday" property="birthday"/>
        <collection property="orders" ofType="order">
            <!--      封裝集合内部Order的資料      -->
            <id column="oid" property="id"/>
            <result column="ordertime" property="ordertime"/>
            <result column="total" property="total"/>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="userMap">
        SELECT *,o.id oid FROM `user` u,orders o WHERE u.id=o.uid
    </select>
</mapper>
           

注意:這裡的一些配置和一對一查詢的多表操作的配置類似,不同之處在于使用

<collection property="orders" ofType="order">

标簽封裝集合内部的實體資料,它的屬性property的值為關系為1的實體中的屬性名,ofType的值為property的值對應的集合的泛型。

配置完成後進行測試即可。

多對多查詢

根據建表原則多對多的關系需要使用一張中間表進行關聯,是以這裡所操作的其實是三張資料表,它的具體的配置和一對一關系的多表操作一樣,隻是稍有不同。示例如下:

這裡還是如一對多查詢類似,在某一個實體中封裝另一個實體對象并将其封裝成集合,映射配置如下:

<resultMap id="userRoleMap" type="user">
     <!--    封裝user的資訊    -->
     <id column="userid" property="id"/>
     <result column="username" property="username"/>
     <result column="password" property="password"/>
     <result column="birthday" property="birthday"/>
     <!--    封裝user内部的roles資訊    -->
     <collection property="roles" ofType="role">
         <id column="roleid" property="id"/>
         <result column="rolename" property="roleName"/>
         <result column="reledesc" property="roleDesc"/>
     </collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="userRoleMap">
    SELECT * FROM `user` u,sys_role sr,sys_user_role sur WHERE u.id=sur.userid AND sur.roleid=sr.id
</select>
           

這裡的不同之處在于所寫的SQL語句的不同,其他大體相同。

MyBatis注解開發

在MyBatis中也存在注解,通過注解開發方式我們可以減少編寫Mapper映射檔案進而實作簡化開發,常用的注解如下:

注解名 描述
@Insert 實作新增(插入)
@Update 實作更新(修改)
@Delete 實作删除(删除)
@Select 實作查詢(查詢)
@Result 實作結果集封裝
@Results 可以與@Result一起使用,封裝多個結果集
@One 實作一對一結果集封裝
@Many 實作一對多結果集封裝

使用注解實作簡單CRUD

當我們使用注解進行CRUD操作時,這時就不用去寫映射檔案了。我們隻需要在接口上加入對應的注解并寫好SQL語句,并在配置檔案中指定接口所在的包即可。示例如下:

Mapper接口如下:

public interface StaffMapper {
    /**
     * 新增職員
     * @param staff Staff對象
     */
    @Insert("insert into staff values(#{id},#{name},#{sex},#{age})")
    void save(Staff staff);

    /**
     * 删除職員
     * @param id 職員編号
     */
    @Delete("delete from staff where id=#{id}")
    void delete(int id);

    /**
     * 修改職員資訊
     * @param staff Staff對象
     */
    @Update("update staff set name=#{name},sex=#{sex},age=#{age} where id=#{id}")
    void update(Staff staff);

    /**
     * 查詢全部職員資訊
     * @return Staff對象封裝的集合
     */
    @Select("select * from staff")
    List<Staff> findAll();
    
    /**
     * 根據員工編号查詢員工資訊
     * @param id 員工編号
     * @return Staff對象封裝的集合
     */
    @Select("select * from staff where id=#{id}")
    Staff findById(int id);
}
           

這時的核心配置檔案如下:

<?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="jdbc.properties"/>
    <!--  配置資料源環境  -->
    <environments default="Developement">
        <environment id="Developement">
            <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>
    <!--加載映射關系-->
    <mappers>
        <!--指定接口所在的包-->
        <package name="com.jclight.mapper"/>
    </mappers>
</configuration>
           

此時就可以進行測試了,測試代碼示例如下:

public class CRUDTest {
    private StaffMapper mapper;
    @Before
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        mapper = sqlSession.getMapper(StaffMapper.class);
    }
    @Test
    public void baseOnXmlCreate() {
        Staff staff = new Staff();
        staff.setName("測試");
        staff.setSex("女");
        staff.setAge(15);
        mapper.save(staff);
    }
    @Test
    public void baseOnXmlDelete() {
        mapper.delete(12);
    }
}
           

觀察測試結果可得,這時的注解CRUD操作也是成功的。

注解開發的一對一多表操作

通過注解我們也可以對一些含有外鍵限制的表進行操作,這些表可能是(

1:1、1:N或N:1、N:N

)的關系。在基于XML的MyBatis多表操作中,我們使用的是

resultMap

resultMap+association

resultMap+collection

标簽實作的,在注解開發中可以使用

@Result、@Results、@One、@Many

這些注解組合進行實作。這些注解的作用對應的XML中的标簽如下:

注解 對應XML标簽說明
@Results 代替的是标簽

resultMap

,該注解可以使用單個@Result注解,也可以使用@Result集合。

使用格式:

@Results({@Result(),@Result()})

@Result 代替了标簽

id

result

,@Result中的屬性說明如下:

column–>資料庫中表的列名

property–>需要裝配的屬性名

one–>需要使用的@One注解,如

@Result([email protected])()

many–>需要使用的@Many注解,如

@Result([email protected])()

@One 主要用來一對一查詢,代替了

assocation

标簽,在注解中用來指定子查詢傳回單一對象。@One注解屬性說明:

select–>指定用來多表查詢的sqlmapper,使用格式:

@Result(column="",property="",[email protected](select=""))

@Many 主要用來一對多查詢,代替了

collection

标簽,在注解中用來指定子查詢傳回對象集合。使用格式如下:

@Result(property="",column="",[email protected](select=""))

示例如下:

public interface OrderMapper {
    /**
     * 查詢order和user表中的資料
     *
     * @return Order對象組成的集合
     */
    @Select("select *,o.id oid from orders o,`user` u where o.uid=u.id")
    @Results({
            @Result(column = "oid",property = "id"),
            @Result(column = "ordertime",property = "ordertime"),
            @Result(column = "total",property = "total"),
            @Result(column = "total",property = "total"),
            @Result(column = "uid",property = "user.id"),
            @Result(column = "username",property = "user.username"),
            @Result(column = "password",property = "user.password"),
    })
    List<Order> findAll();
}
           

測試如下:

public class BaseOnAnnotationCRUDTest {
    private OrderMapper orderMapper;

    @Before
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        orderMapper = sqlSession.getMapper(OrderMapper.class);
    }

    @Test
    public void retrieve() {
        List<Order> orders = orderMapper.findAll();
        for (Order order : orders) {
            System.out.println(order);
        }
    }
}
           

這種方式和之前的XML配置類似,隻不過使用注解替代了标簽的配置。此外還有另一種配置的方式,該方式比起上述更為簡便具體如下:

@Select("select * from orders")
@Results({
    @Result(column = "id", property = "id"),
    @Result(column = "ordertime", property = "ordertime"),
    @Result(column = "total", property = "total"),
    @Result(
        property = "user",
        column = "uid",
        javaType = User.class,
        one = @One(select = "com.jclight.mapper.UserMapper.findById")
    )
})
List<Order> findAll();
           

這裡我們使用注解@Result中自帶的屬性去封裝實體,并簡化SQL語句。這裡的屬性property表示要封裝的屬性名稱,column表示根據哪個字段查詢對應表中的資料,javaType表示要封裝的實體類型。這裡加入的一對一查詢的屬性one,它所對應的注解的屬性select表示查詢哪個接口的方法獲得資料,值對應的是哪個接口下的對應的方法。

注解開發的一對多多表操作

示例如下:

@Select("select * from user")
@Results({
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "username", property = "username"),
    @Result(column = "password", property = "password"),
    @Result(column = "birthday", property = "birthday"),
    @Result(
        property = "orders",
        column = "id",
        javaType = List.class,
        many = @Many(select = "com.jclight.mapper.OrderMapper.findByUid")
    )
})
List<User> findUserAndOrderAll();
           

com.jclight.mapper.OrderMapper.findByUid

如下:

@Select("select * from orders where uid=#{uid}")
List<Order> findByUid(int uid);
           

上述即可實作一對多或多對一的查詢,這裡的配置和一對一查詢類似,主要在于使用@Results({@Result()})組合來封裝資料,其中的many為一對多或多對一操作,指定的值為另一個表的查詢操作(相對應的)。

注解開發的多對多多表操作

示例如下:

@Select("select * from user")
@Results({
    @Result(id = true, column = "id", property = "id"),
    @Result(column = "username", property = "username"),
    @Result(column = "password", property = "password"),
    @Result(column = "birthday", property = "birthday"),
    @Result(
        property = "roles",
        column = "id",
        javaType = List.class,
        many = @Many(select = "com.jclight.mapper.RoleMapper.findByUid")
    )
})
List<User> findUserAndRoleAll1();
           

com.jclight.mapper.RoleMapper.findByUid

接口方法如下:

@Select("select * from sys_user_role sur,sys_role sr where sur.roleid=sr.id and sur.userid=#{id}")
List<Role> findByUid(int id);
           

此時進行測試即可,由上述代碼可以看出,多對多的查詢和一對多的查詢是類似的。這裡隻需要修改對應的SQL語句加入中間表參與查詢即可。

補充/技巧

  1. 當我們進行單元測試時,可以使用

    Junit4

    提供的注解

    @Before

    抽取相同的代碼片段,這樣在其他的測試時就不需要寫大量重複的代碼了。它裡面提供了很多注解可以幫助我們簡化代碼,這些注解的執行流程為

    @BeforeClass -> @Before -> @Test -> @After -> @AfterClass