Java的MyBatis框架中實現(xiàn)多表連接查詢和查詢結(jié)果分頁
實現(xiàn)多表聯(lián)合查詢
還是在david.mybatis.model包下面新建一個Website類,用來持久化數(shù)據(jù)之用,重寫下相應(yīng)toString()方法,方便測試程序之用。
package david.mybatis.model;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Website {
private int id;
private String name;
private int visitorId;
private int status;
private Date createTime;
private Visitor visitor;
public Website() {
// TODO Auto-generated constructor stub
createTime = new Date();
visitor = new Visitor();
}
public Website(String name, int visitorId) {
this.name = name;
this.visitorId = visitorId;
visitor = new Visitor();
status = 1;
createTime = new Date();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Visitor getVisitor() {
return visitor;
}
public void setVisitor(Visitor visitor) {
this.visitor = visitor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public int getVisitorId() {
int id = 0;
if (visitor == null)
id = visitorId;
else
id = visitor.getId();
return id;
}
public void setVisitorId(int visitorId) {
this.visitorId = visitorId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(String.format("Website=> {Id:%d, Name:%s, CreateTime:%s}\r\n", id, name,
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTime)));
if (visitor != null)
sb.append(String.format("Visitor=> %s", visitor.toString()));
return sb.toString();
}
}
在david.mybatis.demo下面分別新建相應(yīng)的操作接口:
package david.mybatis.demo;
import java.util.List;
import david.mybatis.model.Website;
public interface IWebsiteOperation {
public int add(Website website);
public int delete(int id);
public int update(Website website);
public Website query(int id);
public List<Website> getList();
}
在mapper文件夾下新建WebsiteMapper.xml映射文件,分別參照上一張所說的把增刪改查的單表操作配置分別放進去,這樣你可以建造一點測試數(shù)據(jù)。如下
<?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="david.mybatis.demo.IWebsiteOperation">
<sql id="getListSql">
select id,
name, VisitorId, status, createTime from Website
where status>0
</sql>
<insert id="add" parameterType="Website" useGeneratedKeys="true"
keyProperty="Id">
insert into Website (Name, VisitorId, Status, CreateTime)
values (#{name}, #{visitorId}, #{status}, #{createTime})
</insert>
<delete id="delete" parameterType="int">
delete from website where
status>0 and id = #{id}
</delete>
<update id="update" parameterType="Website">
update website set
name=#{name} where status>0 and id=#{id}
</update>
<select id="query" parameterType="int" resultMap="websiteRs">
select
Website.id siteId, Website.name siteName, Visitor.Id visitorId,
Visitor.name visitorName,
Website.status siteStatus, Website.createtime
siteCreateTime from Website
inner join Visitor on Website.visitorid =
Visitor.id where Website.status>0 and
Website.id=#{id}
</select>
<resultMap type="Website" id="websiteRs">
<id column="siteId" property="id" />
<result column="siteName" property="name" />
<result column="siteStatus" property="status" />
<result column="siteCreateTime" property="createTime" />
<association property="visitor" javaType="Visitor" resultMap="visitorRs" />
</resultMap>
<resultMap type="Visitor" id="visitorRs">
<id column="visitorId" property="id" />
<result column="visitorName" property="name" />
</resultMap>
<select id="getList" resultMap="websiteByVisitorIdRs">
<include refid="getListSql" />
</select>
</mapper>
這里今天主要說的就是那個查,現(xiàn)在我們想要查詢網(wǎng)站的同時分別把相應(yīng)的訪問者信息一起拿出來,怎么做呢,大家可以參照配置中的query,寫下聯(lián)表查詢的SQL,
這里主要要注意的是,Website實體與Visit的實體里面Id與Name這2個屬性都是一樣的,所以為了避免映射出現(xiàn)出錯現(xiàn)象,把相應(yīng)的查詢結(jié)果列起上不一樣的別名,這樣綁定的時候就可以避免。
假如我像下面一樣配置會得到什么呢?
<select id="query" parameterType="int" resultMap="websiteRs">
select
Website.id, Website.name siteName, Visitor.Id,
Visitor.name visitorName,
Website.status siteStatus, Website.createtime
siteCreateTime from Website
inner join Visitor on Website.visitorid =
Visitor.id where Website.status>0 and
Website.id=#{id}
</select>
<resultMap type="Website" id="websiteRs">
<id column="id" property="id" />
<result column="siteName" property="name" />
<result column="siteStatus" property="status" />
<result column="siteCreateTime" property="createTime" />
<association property="visitor" javaType="Visitor"
resultMap="visitorRs" />
</resultMap>
<resultMap type="Visitor" id="visitorRs">
<id column="id" property="id" />
<result column="visitorName" property="name" />
</resultMap>

有木有發(fā)覺,Visitor的Id也變成2了,這個其實它默認(rèn)映射了Website的ID,因為SQL語句查詢出來的結(jié)果2個ID都是變成2了,有人會問為什么不是4呢,因為他默認(rèn)匹配第一個如果你把Website.Id與Visit.Id的位置,相互換下就會發(fā)現(xiàn)結(jié)果又神奇的變了

所以需要起個別名避免這種情況,這樣你就會發(fā)現(xiàn)真相其實只有一個就是下面的:

大家可以看到其實多表處理resultMap的方式和單表是一致的,也無非是吧列明與Javabean屬性名成對應(yīng)上去,可以看到在Website的<resultMap>節(jié)點里面前臺另外一個resultMap,他就是代表Visit實體所需要映射的實體,可以使用以下方式進行關(guān)聯(lián)
<association property="visitor" javaType="Visitor" resultMap="visitorRs" />
其中的visitor就是Website實體中的visit字段名,必須保證名稱一致,否則就會拋出There is no getter for property named 'XXX' in 'class david.mybatis.model.Website'的異常,這在上幾章已經(jīng)講述了,當(dāng)然如果你覺得不用嵌套resultMap也行,嵌套也是出于其他地方可以還要用到這個配置那就提煉出來的過程,也是抽象出來的一種思想。具體使用<resultMap>中的ID與Result可以從官網(wǎng)查找相應(yīng)區(qū)別說明:http://mybatis.github.io/mybatis-3/sqlmap-xml.html#Result_Maps
這樣,一個簡單的多表聯(lián)合查詢就出來啦~,如果還有更加復(fù)雜的查詢業(yè)務(wù)費是在這個基礎(chǔ)上些許的變通修改。
分頁效果邏輯
下面要講的是關(guān)于一個業(yè)務(wù)問題中我們常碰到的分頁問題。在開發(fā)web項目的時候我們經(jīng)常會使用到列表顯示,一般我們都會用一些常用的列表控件例如,datatables(個人感覺十分不錯),easy ui下面的那些封裝好的表格控件。
思路:在這些控件里要達(dá)到分頁的效果,一般都會傳2個參數(shù),第一個是表示當(dāng)前頁的索引(一般從0開始),第二個表示當(dāng)前頁展示多少條業(yè)務(wù)記錄,然后將相應(yīng)的參數(shù)傳遞給List<T> getList(PagenateArgs args)方法,最終實現(xiàn)數(shù)據(jù)庫中的分頁時候我們可以使用limit關(guān)鍵詞(針對mysql)進行分頁,如果是oracle或者sql server他們都有自帶的rownum函數(shù)可以使用。
針對上述思路,首先我們需要還是一如既往的在demo.mybatis.model下面新建一個名為PagenateArgs的分頁參數(shù)實體類與一個名為SortDirectionEnum的枚舉類,里面包含當(dāng)前頁面索引pageIndex, 當(dāng)前頁展示業(yè)務(wù)記錄數(shù)pageSize, pageStart屬性表示從第幾條開始,(pageStart=pageIndex*pageSize)因為limit關(guān)鍵詞用法是表示【limit 起始條數(shù)(不包含),取幾條】,orderFieldStr排序字段,orderDirectionStr 排序方向,所以具體創(chuàng)建如下:
package david.mybatis.model;
/*
* 分頁參數(shù)實體類
*/
public class PagenateArgs {
private int pageIndex;
private int pageSize;
private int pageStart;
private String orderFieldStr;
private String orderDirectionStr;
public PagenateArgs() {
// TODO Auto-generated constructor stub
}
public PagenateArgs(int pageIndex, int pageSize, String orderFieldStr, String orderDirectionStr) {
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.orderFieldStr = orderFieldStr;
this.orderDirectionStr = orderDirectionStr;
pageStart = pageIndex * pageSize;
}
public int getPageIndex() {
return pageIndex;
}
public int getPageStart() {
return pageStart;
}
public int getPageSize() {
return pageSize;
}
public String orderFieldStr() {
return orderFieldStr;
}
public String getOrderDirectionStr() {
return orderDirectionStr;
}
}
package david.mybatis.model;
/*
* 排序枚舉
*/
public enum SortDirectionEnum {
/*
* 升序
*/
ASC,
/*
* 降序
*/
DESC
}
完成上面的步驟以后我們在IVisitorOperation接口類中繼續(xù)添加一個方法public List<Visitor> getListByPagenate(PagenateArgs args),前幾章中我們其實已經(jīng)有g(shù)etList方法了,這次的分頁其實也就是在這個的基礎(chǔ)上稍加改動即可,IVisitorOperation接口類改動后如下所示:
package david.mybatis.demo;
import java.util.List;
import david.mybatis.model.PagenateArgs;
import david.mybatis.model.Visitor;
import david.mybatis.model.VisitorWithRn;
public interface IVisitorOperation {
/*
* 基礎(chǔ)查詢
*/
public Visitor basicQuery(int id);
/*
* 添加訪問者
*/
public int add(Visitor visitor);
/*
* 刪除訪問者
*/
public int delete(int id);
/*
* 更新訪問者
*/
public int update(Visitor visitor);
/*
* 查詢訪問者
*/
public Visitor query(int id);
/*
* 查詢List
*/
public List<Visitor> getList();
/*
* 分頁查詢List
*/
public List<Visitor> getListByPagenate(PagenateArgs args);
}
接下來我們就要開始動手改動我們的VisitorMapper.xml配置文件了,新增一個<select>節(jié)點id與參數(shù)類型參照前幾章的方式配置好,如下此處新增的id就為getListByPagenate,配置好以后如下
<?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="david.mybatis.demo.IVisitorOperation">
<!-- useGeneratedKeys="true"代表是否使用自增長序列, keyProperty="Id"指定自增長列是哪一列, parameterType="Visitor"指定IVisitorOperation接口類中定義中所傳的相應(yīng)類型 -->
<insert id="add" parameterType="Visitor" useGeneratedKeys="true"
keyProperty="Id">
insert into Visitor (Name, Email, Status, CreateTime)
values (#{name}, #{email}, #{status}, #{createTime})
</insert>
<delete id="delete" parameterType="int">
delete from Visitor where
status>0 and id = #{id}
</delete>
<update id="update" parameterType="Visitor">
update Visitor set Name =
#{name}, Email=#{email}, Status=#{status} where id=#{id} and Status>0;
</update>
<select id="query" parameterType="int" resultType="Visitor">
select Id,
Name, Email, Status, CreateTime from visitor where id=#{id} and
Status>0 order by Id
</select>
<select id="basicQuery" parameterType="int" resultType="Visitor">
select *
from visitor where id=#{id} and
Status>0 order by Id
</select>
<select id="getList" resultMap="visitorRs">
<include refid="getListSql" />
</select>
<sql id="getListSql">
select * from Visitor where
status>0
</sql>
<!-- 以下為新增部分用來分頁,orderBySql這個提取出來是為了后面有示例復(fù)用 -->
<resultMap type="Visitor" id="visitorRs">
<id column="Id" property="id" />
<result column="Name" property="name" />
<result column="Email" property="email" />
<result column="Status" property="status" />
<result column="CreateTime" property="createTime" />
</resultMap>
<select id="getListByPagenate" parameterType="PagenateArgs"
resultType="Visitor">
select * from (
<include refid="getListSql" /> <include refid="orderBySql"/>
) t <!-- #{}表示參數(shù)化輸出,${}表示直接輸出不進行任何轉(zhuǎn)義操作,自己進行轉(zhuǎn)移 -->
<if test="pageStart>-1 and pageSize>-1">
limit #{pageStart}, #{pageSize}
</if>
</select>
<sql id="orderBySql">
order by ${orderFieldStr} ${orderDirectionStr}
</sql>
</mapper>
在上面你會發(fā)現(xiàn)有類似,下圖中的配置,這里面的字段屬性都是針對PagenateArgs參數(shù)類中的屬性名,保持一致。
<if test="pageStart>-1 and pageSize>-1">
limit #{pageStart}, #{pageSize}
</if>
在DemoRun類中創(chuàng)建測試方法:
/*
* 分頁參數(shù)
*/
public static void queryVisitorListWithPagenate(int pageIndex, int pageSize, String orderField, String orderDire) {
PagenateArgs args = new PagenateArgs(pageIndex, pageSize, orderField, orderDire);
SqlSession session = MybatisUtils.getSqlSession();
IVisitorOperation vOperation = session.getMapper(IVisitorOperation.class);
List<Visitor> visitors = vOperation.getListByPagenate(args);
for (Visitor visitor : visitors) {
System.out.println(visitor);
}
MybatisUtils.closeSession(session);
MybatisUtils.showMessages(CRUD_Enum.List, visitors.size());
}
DemoRun.queryVisitorListWithPagenate(0, 100, "id", SortDirectionEnum.DESC.toString());
運行后下測試結(jié)果,先按Id倒序排列,查的Visitor表一共有14條記錄,

假設(shè)我們?nèi)≡诘?頁取5條,執(zhí)行下面也就是6-10條數(shù)據(jù),這樣傳參數(shù)就行了
DemoRun.queryVisitorListWithPagenate(1, 5, "id", SortDirectionEnum.DESC.toString());
結(jié)果如下:

這樣就自己實現(xiàn)了的一個分頁邏輯啦~^0^,這里需要注意的就是我這邊orderFieldStr字段是沒有做過任何判斷的,理論上要處理下防止錯誤了列名傳進去,不過現(xiàn)在網(wǎng)上應(yīng)該有現(xiàn)成封裝好的東西,大家也可以去google下,這里只是給個思路演示下怎么用mybatis分頁。
完成這個后,因為是Mysql的關(guān)系所以在查詢結(jié)果里他沒有自帶rownum序列ID,所以查看測試數(shù)據(jù)是第幾條的時候可能不明顯,不zao急,我們可以自己動手豐衣足食改造下上面的方法,這里我重新在model包里新建一個一模一樣的VisitorWithRn實體里面多帶一個rownum參數(shù)持久化返回的RownumID,如下:
package david.mybatis.model;
import java.text.SimpleDateFormat;
import java.util.Date;
public class VisitorWithRn {
private int id;
private String name;
private String email;
private int status;
private Date createTime;
private int rownum;
public VisitorWithRn() {
// TODO Auto-generated constructor stub
createTime = new Date();
}
public VisitorWithRn(String name, String email) {
this.name = name;
this.email = email;
this.setStatus(1);
this.createTime = new Date();
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public Date getCreateTime() {
return createTime;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getRownum() {
return rownum;
}
public void setRownum(int rownum) {
this.rownum = rownum;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return String.format("{Rownum:%d, Id: %d, Name: %s, CreateTime: %s}", rownum, id, name,
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTime));
}
}
在IVisitorOperation里面在新建一個名為 public List<VisitorWithRn> getListByPagenateWithRn(PagenateArgs args)的方法,同樣我們需要在VisitorMapper中配置下相應(yīng)<select>節(jié)點與腳本,此處唯一的不同就是需要改下sql腳本,如下:
<?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="david.mybatis.demo.IVisitorOperation">
<!-- useGeneratedKeys="true"代表是否使用自增長序列, keyProperty="Id"指定自增長列是哪一列, parameterType="Visitor"指定IVisitorOperation接口類中定義中所傳的相應(yīng)類型 -->
<insert id="add" parameterType="Visitor" useGeneratedKeys="true"
keyProperty="Id">
insert into Visitor (Name, Email, Status, CreateTime)
values (#{name}, #{email}, #{status}, #{createTime})
</insert>
<delete id="delete" parameterType="int">
delete from Visitor where
status>0 and id = #{id}
</delete>
<update id="update" parameterType="Visitor">
update Visitor set Name =
#{name}, Email=#{email}, Status=#{status} where id=#{id} and Status>0;
</update>
<select id="query" parameterType="int" resultType="Visitor">
select Id,
Name, Email, Status, CreateTime from visitor where id=#{id} and
Status>0 order by Id
</select>
<select id="basicQuery" parameterType="int" resultType="Visitor">
select *
from visitor where id=#{id} and
Status>0 order by Id
</select>
<select id="getList" resultMap="visitorRs">
<include refid="getListSql" />
</select>
<sql id="getListSql">
select * from Visitor where
status>0
</sql>
<resultMap type="Visitor" id="visitorRs">
<id column="Id" property="id" />
<result column="Name" property="name" />
<result column="Email" property="email" />
<result column="Status" property="status" />
<result column="CreateTime" property="createTime" />
</resultMap>
<select id="getListByPagenate" parameterType="PagenateArgs"
resultType="Visitor">
select * from (
<include refid="getListSql" /> <include refid="orderBySql"/>
) t <!-- #{}表示參數(shù)化輸出,${}表示直接輸出不進行任何轉(zhuǎn)義操作,自己進行轉(zhuǎn)移 -->
<if test="pageStart>-1 and pageSize>-1">
limit #{pageStart}, #{pageSize}
</if>
</select>
<!--提煉出來為了2個示例共用下 -->
<sql id="orderBySql">
order by ${orderFieldStr} ${orderDirectionStr}
</sql>
<!-- 帶rownum的SQL腳本書寫方式 -->
<resultMap type="VisitorWithRn" id="visitorWithRnRs">
<id column="Id" property="id" />
<result column="Name" property="name" />
<result column="Email" property="email" />
<result column="Status" property="status" />
<result column="CreateTime" property="createTime" />
<result column="Rownum" property="rownum" />
</resultMap>
<select id="getListByPagenateWithRn" resultMap="visitorWithRnRs">
<!-- #{}表示參數(shù)化輸出,${}表示直接輸出不進行任何轉(zhuǎn)義操作,自己進行轉(zhuǎn)移 -->
select t.Rownum, t.Id, t.Name, t.Email, t.Status, t.CreateTime from (<include refid="getListSqlContainsRn" /> <include refid="orderBySql"/>) t
<if test="pageStart>-1 and pageSize>-1">
limit #{pageStart}, #{pageSize}
</if>
</select>
<sql id="getListSqlContainsRn">
select @rownum:=@rownum+1 Rownum,
result.id, result.name, result.email, result.status, result.createTime
FROM (
select @rownum:=0, Visitor.* from Visitor where
status>0) result
</sql>
</mapper>
接下來剩下的就是如剛才在DemoRun下面添加測試方法,這里就不貼圖了,完成后你可以看到剛剛的6-10條數(shù)據(jù)會變成如下

相關(guān)文章
Mybatis plus 配置多數(shù)據(jù)源的實現(xiàn)示例
這篇文章主要介紹了Mybatis plus 配置多數(shù)據(jù)源的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
解決@Test注解在Maven工程的Test.class類中無法使用的問題
這篇文章主要介紹了解決@Test注解在Maven工程的Test.class類中無法使用的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
spring-boot-maven-plugin:unknown的完美解決方法
這篇文章主要介紹了spring-boot-maven-plugin:unknown的完美解決方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
java之TreeUtils生成一切對象樹形結(jié)構(gòu)案例
這篇文章主要介紹了java之TreeUtils生成一切對象樹形結(jié)構(gòu)案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09

