SSM框架 Mybatis联合查询 多表查询 分页

来源:互联网 发布:内网映射端口到外网 编辑:程序博客网 时间:2024/05/16 12:20

这篇文章主要介绍了Java的MyBatis框架中实现多表连接查询和查询结果分页,借助MyBatis框架中带有的动态SQL查询功能可以比普通SQL查询做到更多,需要的朋友可以参考下

Java的MyBatis框架中实现多表连接查询和查询结果分页 实现多表联合查询
还是在david.mybatis.model包下面新建一个Website类,用来持久化数据之用,重写下相应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下面分别新建相应的操作接口:

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映射文件,分别参照上一张所说的把增删改查的单表操作配置分别放进去,这样你可以建造一点测试数据。如下

<?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>

这里今天主要说的就是那个查,现在我们想要查询网站的同时分别把相应的访问者信息一起拿出来,怎么做呢,大家可以参照配置中的query,写下联表查询的SQL,
这里主要要注意的是,Website实体与Visit的实体里面Id与Name这2个属性都是一样的,所以为了避免映射出现出错现象,把相应的查询结果列起上不一样的别名,这样绑定的时候就可以避免。
假如我像下面一样配置会得到什么呢?

<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>

有木有发觉,Visitor的Id也变成2了,这个其实它默认映射了Website的ID,因为SQL语句查询出来的结果2个ID都是变成2了,有人会问为什么不是4呢,因为他默认匹配第一个如果你把Website.Id与Visit.Id的位置,相互换下就会发现结果又神奇的变了

所以需要起个别名避免这种情况,这样你就会发现真相其实只有一个就是下面的:

大家可以看到其实多表处理resultMap的方式和单表是一致的,也无非是吧列明与Javabean属性名成对应上去,可以看到在Website的节点里面前台另外一个resultMap,他就是代表Visit实体所需要映射的实体,可以使用以下方式进行关联

<association property="visitor" javaType="Visitor" resultMap="visitorRs" />

其中的visitor就是Website实体中的visit字段名,必须保证名称一致,否则就会抛出There is no getter for property named ‘XXX’ in ‘class david.mybatis.model.Website’的异常,这在上几章已经讲述了,当然如果你觉得不用嵌套resultMap也行,嵌套也是出于其他地方可以还要用到这个配置那就提炼出来的过程,也是抽象出来的一种思想。具体使用中的ID与Result可以从官网查找相应区别说明:http://mybatis.github.io/mybatis-3/sqlmap-xml.html#Result_Maps
这样,一个简单的多表联合查询就出来啦~,如果还有更加复杂的查询业务费是在这个基础上些许的变通修改。
分页效果逻辑
下面要讲的是关于一个业务问题中我们常碰到的分页问题。在开发web项目的时候我们经常会使用到列表显示,一般我们都会用一些常用的列表控件例如,datatables(个人感觉十分不错),easy ui下面的那些封装好的表格控件。
  思路:在这些控件里要达到分页的效果,一般都会传2个参数,第一个是表示当前页的索引(一般从0开始),第二个表示当前页展示多少条业务记录,然后将相应的参数传递给List getList(PagenateArgs args)方法,最终实现数据库中的分页时候我们可以使用limit关键词(针对mysql)进行分页,如果是oracle或者sql server他们都有自带的rownum函数可以使用。
  针对上述思路,首先我们需要还是一如既往的在demo.mybatis.model下面新建一个名为PagenateArgs的分页参数实体类与一个名为SortDirectionEnum的枚举类,里面包含当前页面索引pageIndex, 当前页展示业务记录数pageSize, pageStart属性表示从第几条开始,(pageStart=pageIndex*pageSize)因为limit关键词用法是表示【limit 起始条数(不包含),取几条】,orderFieldStr排序字段,orderDirectionStr 排序方向,所以具体创建如下:
package david.mybatis.model;

/* * 分页参数实体类 */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接口类中继续添加一个方法public List getListByPagenate(PagenateArgs args),前几章中我们其实已经有getList方法了,这次的分页其实也就是在这个的基础上稍加改动即可,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 {  /*   * 基础查询   */  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配置文件了,新增一个节点id与参数类型参照前几章的方式配置好,如下此处新增的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接口类中定义中所传的相应类型 -->  <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这个提取出来是为了后面有示例复用 -->  <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 <!-- #{}表示参数化输出,${}表示直接输出不进行任何转义操作,自己进行转移 -->    <if test="pageStart>-1 and pageSize>-1">      limit #{pageStart}, #{pageSize}    </if>  </select>  <sql id="orderBySql">    order by ${orderFieldStr} ${orderDirectionStr}  </sql></mapper>

  在上面你会发现有类似,下图中的配置,这里面的字段属性都是针对PagenateArgs参数类中的属性名,保持一致。

<if test="pageStart>-1 and pageSize>-1">  limit #{pageStart}, #{pageSize}</if>

  在DemoRun类中创建测试方法:

/* * 分页参数 */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());

运行后下测试结果,先按Id倒序排列,查的Visitor表一共有14条记录,

假设我们取在第2页取5条,执行下面也就是6-10条数据,这样传参数就行了

DemoRun.queryVisitorListWithPagenate(1, 5, "id", SortDirectionEnum.DESC.toString());

结果如下:
201646153410351.png (598×334)
这样就自己实现了的一个分页逻辑啦~^0^,这里需要注意的就是我这边orderFieldStr字段是没有做过任何判断的,理论上要处理下防止错误了列名传进去,不过现在网上应该有现成封装好的东西,大家也可以去google下,这里只是给个思路演示下怎么用mybatis分页。
  完成这个后,因为是Mysql的关系所以在查询结果里他没有自带rownum序列ID,所以查看测试数据是第几条的时候可能不明显,不zao急,我们可以自己动手丰衣足食改造下上面的方法,这里我重新在model包里新建一个一模一样的VisitorWithRn实体里面多带一个rownum参数持久化返回的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 getListByPagenateWithRn(PagenateArgs args)的方法,同样我们需要在VisitorMapper中配置下相应节点与脚本,此处唯一的不同就是需要改下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接口类中定义中所传的相应类型 -->  <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 <!-- #{}表示参数化输出,${}表示直接输出不进行任何转义操作,自己进行转移 -->    <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">    <!-- #{}表示参数化输出,${}表示直接输出不进行任何转义操作,自己进行转移 -->    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条数据会变成如下


转自 脚本之家 http://www.jb51.net/article/82020.htm

原创粉丝点击