持久層架構學習——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 | ,boolean | BOOLEAN |
ByteTypeHandler | ,byte | NUMERIC或BYTE |
ShortTypeHandler | ,short | NUMERIC或SHORT INTEGER |
IntegerTypeHandler | ,int | NUMERIC或INTEGER |
LongTypeHandler | ,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 | 代替的是标簽 ,該注解可以使用單個@Result注解,也可以使用@Result集合。 使用格式: |
@Result | 代替了标簽 和 ,@Result中的屬性說明如下: column–>資料庫中表的列名 property–>需要裝配的屬性名 one–>需要使用的@One注解,如 many–>需要使用的@Many注解,如 |
@One | 主要用來一對一查詢,代替了 标簽,在注解中用來指定子查詢傳回單一對象。@One注解屬性說明: select–>指定用來多表查詢的sqlmapper,使用格式: |
@Many | 主要用來一對多查詢,代替了 标簽,在注解中用來指定子查詢傳回對象集合。使用格式如下: |
示例如下:
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語句加入中間表參與查詢即可。
補充/技巧
- 當我們進行單元測試時,可以使用
提供的注解Junit4
抽取相同的代碼片段,這樣在其他的測試時就不需要寫大量重複的代碼了。它裡面提供了很多注解可以幫助我們簡化代碼,這些注解的執行流程為@Before
。@BeforeClass -> @Before -> @Test -> @After -> @AfterClass