(五)測試學習JavaWeb之MyBatis上篇

前言

Web開發離不開數據庫的操作,該篇文章咱們一起來學習MyBatis 這款優秀的持久層框架,官網對MyBatis描述如下。

MyBatis支持定製化 SQL、存儲過程以及高級映射。它避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。

原理

每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲中心的。SqlSessionFactory 的實例可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置文件或一個預先定製的 Configuration 的實例構建出 SqlSessionFactory 的實例。

下面以mysql爲例,通過實現數據庫增刪改查、多表關聯、動態sql、整合Spring等功能來逐步深入學習MyBatis。

pom依賴

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

<!-- mysql-connector -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.29</version>
</dependency>

MyBatis增刪改查入門

1、建表
CREATE TABLE mybatis(
idno VARCHAR(30) PRIMARY KEY,
idtype INT(2),
username VARCHAR(30),
phone VARCHAR(20),
address VARCHAR(100)
);
2、新建實體類

建立與數據庫映射的是實體類DataPoolEntity。

package com.entity;

public class DataPoolEntity{

    private String idno;
    private int idtype;
    private String username;
    private String phone;
    private String address;

    public DataPoolEntity(){
    }

    public DataPoolEntity(String idno,int idtype,String username,String phone,String address){
        this.idno = idno;
        this.idtype = idtype;
        this.username = username;
        this.phone = phone;
        this.address = address;
    }

    public String getIdno() {
        return idno;
    }

    public int getIdtype() {
        return idtype;
    }

    public String getUsername() {
        return username;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public void setIdno(String idno) {
        this.idno = idno;
    }

    public void setIdtype(int idtype) {
        this.idtype = idtype;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

3、新建映射接口
package com.mapper;

import com.entity.DataPoolEntity;

import java.util.List;

public interface DataPoolMapper {

    //根據idno查詢
    public DataPoolEntity selectByIdno(String idno);

    //根據姓名和手機查詢
    public List<DataPoolEntity> selectByUsernameAndPhone(String username, String phone);

    //新增記錄
    public void insertData(DataPoolEntity dataPoolEntity);

    //刪除記錄
    public void deleteByIdno(String idno);

    //更新記錄
    public void updateData(DataPoolEntity dataPoolEntity);
}

4、新建工具類
package com.utils;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MybatisUtil {
    private static SqlSessionFactory sqlSessionFactory;

    public static SqlSessionFactory getSqlSessionFactory(){
        if(sqlSessionFactory == null){
            InputStream inputStream = MybatisUtil.class.getClassLoader().getResourceAsStream("mybatisConfig.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }

        return sqlSessionFactory;
    }

    public static SqlSession openSession(){
        return getSqlSessionFactory().openSession();
    }
}

5、新建配置文件

mysql.properties定義數據庫的鏈接信息,下文的mybatisConfig.xml配置文件可通過properties屬性引用。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/iiaccount_datapool
jdbc.username=root
jdbc.password=root

mybatisMapper.xml爲映射文件,定義了sql語句的參數及返回類型等,與第3點的映射接口相關聯。如果表的列名與實體類的成員變量名不一致的話,此時不應該使用resultType,而應該使用resultMap,否則mybatis將無法進行映射,下文會舉例驗證。

<?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">
<!--namespace爲映射接口全路徑,否則會報錯-->
<mapper namespace="com.mapper.DataPoolMapper">
    <!--id名稱與DataPoolMapper接口的方法名一致-->
    <insert id="insertData" useGeneratedKeys="true" keyProperty="idno">
        insert into mybatis(idno,idtype,username,phone,address) values (#{idno},#{idtype},
        #{username},#{phone},#{address})
    </insert>
    
    <update id="updateData">
        update mybatis set username=#{username},phone=#{phone},address=#{address} where idno
        = #{idno}
    </update>

    <delete id="deleteByIdno">
        delete from mybatis where idno = #{idno}
    </delete>

    <!--id與mapper類方法名一致-->
    <!--entity爲mybatisConfig.xml創建的別名-->
    <!--可以使用resultType或resultMap-->
    <!--如果是多參數則不能使用parameterType, 改用#{index}來表示selectByUsernameAndPhone方法的第幾個參數,索引從0開始-->
    <select id="selectByUsernameAndPhone" resultType="entity">
        select * from mybatis where username = #{0} and phone = #{1}
    </select>

    <!--單個參數可以使用parameterType-->
    <select id="selectByIdno" parameterType="String" resultType="entity">
        select * from mybatis where idno = #{idno}
    </select>
</mapper>

mybatisConfig.xml是 mybatis 用來建立 SqlSessionFactory用的,具體運用見第4點的工具類MybatisUtil 。該配置文件包含了數據庫的鏈接信息以及數據庫映射文件mybatisMapper.xml。

<?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="mysql.properties">
    </properties>

    <typeAliases>
        <!--定義別名-->
        <typeAlias type="com.entity.DataPoolEntity" alias="entity"/>
    </typeAliases>


    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <!-- mybatis提供了3種數據源類型,分別是:POOLED,UNPOOLED,JNDI -->
            <!-- POOLED 表示支持JDBC數據源連接池 -->
            <!-- UNPOOLED 表示不支持數據源連接池 -->
            <!-- JNDI 表示支持外部數據源連接池 -->
            <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="mybatisMapper.xml"/>
    </mappers>
</configuration>
6、增刪改查驗證

新建測試類MybatisTest驗證增刪改查操作

package com.mybatis;

import com.entity.DataPoolEntity;
import com.mapper.DataPoolMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class MybatisTest {

    public static void main(String[] args) {
        insert();
        //delete();
        selectByUsernameAndPhone();
        update();
        selectById();

    }

    public static void insert() {
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        DataPoolEntity dataPoolEntity = new DataPoolEntity("4408810", 1, "Tomandy", "13692464000", "PK");
        try {
            dataPoolMapper.insertData(dataPoolEntity);
            sqlSession.commit();
            System.out.println("新增成功!");
            System.out.println("----------------");
        } catch (Exception e) {
            sqlSession.rollback();
        } finally {
            sqlSession.close();
        }

    }

    public static void update() {
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        //先查詢再修改
        DataPoolEntity dataPoolEntity = dataPoolMapper.selectByIdno("4408810");
        dataPoolEntity.setAddress("XIAN");
        dataPoolEntity.setPhone("13692464999");
        dataPoolEntity.setUsername("Tomandy123");

        try {
            dataPoolMapper.updateData(dataPoolEntity);
            sqlSession.commit();
            System.out.println("修改成功");
            System.out.println("----------------");
        } catch (Exception e) {
            sqlSession.rollback();
        } finally {
            sqlSession.close();
        }
    }

    public static void delete() {
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        try {
            dataPoolMapper.deleteByIdno("4408810");
            sqlSession.commit();
            System.out.println("刪除成功!");
            System.out.println("----------------");
        } catch (Exception e) {
            sqlSession.rollback();
        } finally {
            sqlSession.close();
        }

    }

    public static void selectById(){
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        DataPoolEntity dataPoolEntity = dataPoolMapper.selectByIdno("4408810");
        System.out.println("selectById查詢信息如下:");
        System.out.println("idno:"+dataPoolEntity.getIdno());
        System.out.println("idtype:"+dataPoolEntity.getIdtype());
        System.out.println("username:"+dataPoolEntity.getUsername());
        System.out.println("phone:"+dataPoolEntity.getPhone());
        System.out.println("address:"+dataPoolEntity.getAddress());
        System.out.println("----------------");
    }

    public static void selectByUsernameAndPhone(){
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        List<DataPoolEntity> dataPoolEntityList  = dataPoolMapper.selectByUsernameAndPhone("Tomandy","13692464000");
        for(DataPoolEntity dataPoolEntity:dataPoolEntityList){
            System.out.println("selectByUsernameAndPhone查詢信息如下:");
            System.out.println("idno:"+dataPoolEntity.getIdno());
            System.out.println("idtype:"+dataPoolEntity.getIdtype());
            System.out.println("username:"+dataPoolEntity.getUsername());
            System.out.println("phone:"+dataPoolEntity.getPhone());
            System.out.println("address:"+dataPoolEntity.getAddress());
            System.out.println("----------------");
        }
    }

}

執行結果如下

新增成功!
----------------
selectByUsernameAndPhone查詢信息如下:
idno:4408810
idtype:1
username:Tomandy
phone:13692464000
address:PK
----------------
修改成功
----------------
selectById查詢信息如下:
idno:4408810
idtype:1
username:Tomandy123
phone:13692464999
address:XIAN
----------------
7、xml映射文件resultType和resultMap的區別

上文提到,如果表的列名與實體類的成員變量名不一致的話,此時不應該使用resultType,而應該使用resultMap,下面通過例子來進行驗證。
修改實體類原成員變量username爲username_,其他配置文件還是跟上文的一致。

package com.entity;

public class DataPoolEntity{

    private String idno;
    private int idtype;
    private String username_;
    private String phone;
    private String address;

    public DataPoolEntity(){
    }

    public DataPoolEntity(String idno,int idtype,String username,String phone,String address){
        this.idno = idno;
        this.idtype = idtype;
        this.username_ = username;
        this.phone = phone;
        this.address = address;
    }

    public String getIdno() {
        return idno;
    }

    public int getIdtype() {
        return idtype;
    }

    public String getUsername_() {
        return username_;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public void setIdno(String id_no) {
        this.idno = id_no;
    }

    public void setIdtype(int idtype) {
        this.idtype = idtype;
    }

    public void setUsername_(String username) {
        this.username_ = username;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

編寫測試類進行驗證。

package com.mybatis;

import com.entity.DataPoolEntity;
import com.mapper.DataPoolMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class MybatisTest {

    public static void main(String[] args) {
        selectById();
    }

    public static void selectById(){
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        DataPoolEntity dataPoolEntity = dataPoolMapper.selectByIdno("4408810");
        System.out.println("selectById查詢信息如下:");
        System.out.println("idno:"+dataPoolEntity.getIdno());
        System.out.println("idtype:"+dataPoolEntity.getIdtype());
        System.out.println("username:"+dataPoolEntity.getUsername_());
        System.out.println("phone:"+dataPoolEntity.getPhone());
        System.out.println("address:"+dataPoolEntity.getAddress());
        System.out.println("----------------");
    }
}

運行測試,輸出如下,可發現username輸出了null,這說明映射失敗。

selectById查詢信息如下:
idno:4408810
idtype:1
username:null
phone:13692464000
address:XIAN
----------------

我們把mybatisMapper.xml改爲resultMap的形式試試。

<?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.mapper.DataPoolMapper">
    
    <resultMap id="map" type="entity">
        <!-- id 屬性專門用來映射主鍵信息,其他信息用result節點
        column 數據庫字段
        property 實體類成員屬性 -->
        <id column="idno" property="idno"/>
        <result column="idtype" property="idtype"/>
        <result column="username" property="username_"/>
        <result column="phone" property="phone"/>
        <result column="address" property="address"/>
    </resultMap>

    <!--單個參數可以使用parameterType-->
    <select id="selectByIdno" parameterType="String" resultMap="map">
        select * from mybatis where idno = #{idno}
    </select>
</mapper>

接着運行上面的測試類,可發現輸出結果中username不再爲null。

selectById查詢信息如下:
idno:4408810
idtype:1
username:Tomandy
phone:13692464000
address:XIAN
----------------

由此可見,resultMap 僅僅是作用數據庫表字段跟實體類的屬性的映射關係而存在,當需要映射的實體類的屬性跟數據庫字段不一樣的時候使用,
但是如果數據庫字段名跟需要映射的實體類的屬性名完全一致,resultMap可以不用,resultType足夠了。

8、映射文件屬性“useGeneratedKeys”和“keyProperty”詳解

上文的mybatisMapper.xml配置文件insert中用到了“useGeneratedKeys”和“keyProperty”屬性(由於上文例子沒有用到自增列主鍵,這兩個屬性沒有實際意義),其中,useGeneratedKeys設置爲 true 時,表示如果插入的表id以自增列爲主鍵,則允許 JDBC 支持自動生成主鍵,並可將自動生成的主鍵id返回,舉例如下:

<insert id="insert" parameterType="user"   
        useGeneratedKeys="true" keyProperty="id">  
        insert into user(user_id,user_name,  
            user_phone)  
        values(#{id},#{name},#{phone})  
    </insert> 

Mybatis執行完插入語句後,自動將自增長值賦值給對象user的屬性id,可通過user實體類對應的getterId()方法獲取!
另外,對於useGeneratedKeys和keyProperty的用法,官網也給了相應的說明。

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

如果你的數據庫支持自動生成主鍵的字段(比如 MySQL 和 SQL Server),那麼你可以設置 useGeneratedKeys=”true”,然後再把 keyProperty 設置到目標屬性上就OK了。例如,如果上面的 Author 表已經對 id 使用了自動生成的列類型,那麼語句可以修改爲:

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

MyBatis多表關聯操作

1、ResultMap詳解

在開始多表關聯操作學習之前,首先來深入瞭解一下ResultMap,前文提到ResultMap是作用數據庫表字段跟實體類屬性的映射關係而存在的。結合官網的相關例子,我們來學習下ResultMap的高級用法。
resultMap元素裏包含了以下子元素。

名稱 用途
id 一個 ID 結果;標記出作爲 ID 的結果可以幫助提高整體性能
result 注入到字段或 JavaBean 屬性的普通結果
constructor 用於在實例化類時,注入結果到構造方法中
association 一個複雜類型的關聯;許多結果將包裝成這種類型
collection 一個複雜類型的集合
discriminator 使用結果值來決定使用哪個 resultMap

id和result子元素上文已舉例說明,下面通過舉例對其他幾個子元素做詳細的說明。

  • constructor子元素舉例說明
    mabatisMapper.xml配置,column屬性順序需與實體類構造函數入參順序一致,官網也提供了順序不一致的解決方法,詳情參考官網。
<?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.mapper.DataPoolMapper">
    
    <resultMap id="map" type="entity">
        <!-- id 屬性專門用來映射主鍵信息,其他信息用result節點
        column 數據庫字段
        property 實體類成員屬性 -->
        <!--
        <id column="idno" property="idno"/>
        <result column="idtype" property="idtype"/>
        <result column="username" property="username_"/>
        <result column="phone" property="phone"/>
        <result column="address" property="address"/>
        -->
        <constructor>
            <idArg column="idno" javaType="String"/>
            <arg column="idtype" javaType="_int"/>
            <arg column="username" javaType="String"/>
            <arg column="phone" javaType="String"/>
            <arg column="address" javaType="String"/>
        </constructor>
    </resultMap>
    
    <!--單個參數可以使用parameterType-->
    <select id="selectByIdno" parameterType="String" resultMap="map">
        select * from mybatis where idno = #{idno}
    </select>
</mapper>

編寫實體類DataPoolEntity

package com.entity;

public class DataPoolEntity{

    private String idno;
    private int idtype;
    private String username_;
    private String phone;
    private String address;

    public DataPoolEntity(){
    }

    public DataPoolEntity(String idno,int idtype,String username,String phone,String address){
        this.idno = idno;
        this.idtype = idtype;
        this.username_ = username;
        this.phone = phone;
        this.address = address;
    }

    public String getIdno() {
        return idno;
    }

    public int getIdtype() {
        return idtype;
    }

    public String getUsername_() {
        return username_;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }
}

編寫測試類MybatisTest

package com.mybatis;

import com.entity.DataPoolEntity;
import com.mapper.DataPoolMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class MybatisTest {

    public static void main(String[] args) {
        selectById();
    }
     public static void selectById(){
        SqlSession sqlSession = MybatisUtil.openSession();
        DataPoolMapper dataPoolMapper = sqlSession.getMapper(DataPoolMapper.class);

        DataPoolEntity dataPoolEntity = dataPoolMapper.selectByIdno("4408810");
        System.out.println("selectById查詢信息如下:");
        System.out.println("idno:"+dataPoolEntity.getIdno());
        System.out.println("idtype:"+dataPoolEntity.getIdtype());
        System.out.println("username:"+dataPoolEntity.getUsername_());
        System.out.println("phone:"+dataPoolEntity.getPhone());
        System.out.println("address:"+dataPoolEntity.getAddress());
        System.out.println("----------------");
    }

執行後輸出結果如下:

selectById查詢信息如下:
idno:4408810
idtype:1
username:Tomandy
phone:13692464000
address:PK
----------------

對比前文配置的mybatisMapper.xml文件和實體類,可發現使用constructor子元素的話,mybatis也可映射成功。

  • association子元素舉例說明
    association用於一對一的關聯,常見的有三種用法,下面舉例說明。

場景:
一個用戶有一張攜程的會員卡。分別對應客戶信息表和會員卡信息表,通過id關聯。

先建表並insert記錄。

#客戶信息表
drop table if exists customer;
create table customer(
id varchar(20) primary key,
idtype int(2),  #證件類型
idno varchar(30), #證件號碼
username varchar(50) not null, #名稱
sex varchar(10) not null #性別
);

#會員卡表
drop table if exists membershipCard;
create table membershipCard(
id varchar(20) primary key,
level int(2) not null, #會員等級
phone varchar(20) not null, #電話
address varchar(100) #地址
);

insert into customer(id,idtype,idno,username,sex) values("123",1,"44088118","Tomandy","male");
insert into membershipcard(id,level,phone,address) values("123",1,"18601111","Pk");

新建實體類CustomerEntity及MembershipCardEntity。

package com.entity;

public class CustomerEntity {

    private String id;
    private int idtype;
    private String idno;
    private String username;
    private String sex;
    private MembershipCardEntity membershipCardEntity;

    public String getId() {
        return id;
    }

    public int getIdtype() {
        return idtype;
    }

    public String getIdno() {
        return idno;
    }

    public String getUsername() {
        return username;
    }

    public String getSex() {
        return sex;
    }

    public MembershipCardEntity getMembershipCardEntity() {
        return membershipCardEntity;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setIdtype(int idtype) {
        this.idtype = idtype;
    }

    public void setIdno(String idno) {
        this.idno = idno;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setMembershipCardEntity(MembershipCardEntity membershipCardEntity) {
        this.membershipCardEntity = membershipCardEntity;
    }
}

package com.entity;

public class MembershipCardEntity {

    private String id;
    private int level;
    private String phone;
    private String address;

    public String getId() {
        return id;
    }

    public int getLevel() {
        return level;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

新建映射接口XieChengMapper

package com.mapper;

import com.entity.CustomerEntity;

public interface XieChengMapper {

    CustomerEntity queryById(String id);
}

association第一種用法:
新建映射配置文件xiechengMapper.xml,注意resultMap裏面用到了association子元素。

<?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.mapper.XieChengMapper">
    <resultMap id="cmmap" type="cutomerEntity">
        <id property="id" column="id"/>
        <result property="idtype" column="idtype"/>
        <result property="idno" column="idno"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <!--property屬性值需要在CustomerEntity有相應的getter方法-->
        <!--javaType爲mybatisConfig.xml定義com.entity.MembershipCardEntity的別名-->
        <association property="membershipCardEntity" javaType="membershipCardEntity">
            <id property="id" column="id"/>
            <result property="level" column="level"/>
            <result property="phone" column="phone"/>
            <result property="address" column="address"/>
        </association>
    </resultMap>
    
    <select id="queryById" resultMap="cmmap" parameterType="String">
        select
        a.id,a.sex,a.idno,a.username,b.phone,b.address
        from customer a,membershipCard b
        where a.id = b.id
        and a.id = #{id}
    </select>

</mapper>

添加映射文件到mybatisConfig.xml配置文件,相比上文的配置文件,此處增加了cutomerEntity,membershipCardEntity兩個別名,另外也引入了xiechengMapper.xml。

<?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="mysql.properties">
    </properties>

    <typeAliases>
        <!--定義別名-->
        <typeAlias type="com.entity.DataPoolEntity" alias="entity"/>
        <typeAlias type="com.entity.CustomerEntity" alias="cutomerEntity"/>
        <typeAlias type="com.entity.MembershipCardEntity" alias="membershipCardEntity"/>
    </typeAliases>


    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <!-- mybatis提供了3種數據源類型,分別是:POOLED,UNPOOLED,JNDI -->
            <!-- POOLED 表示支持JDBC數據源連接池 -->
            <!-- UNPOOLED 表示不支持數據源連接池 -->
            <!-- JNDI 表示支持外部數據源連接池 -->
            <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="mybatisMapper.xml"/>
        <!--此處新加了映射配置文件-->
        <mapper resource="xiechengMapper.xml"/>
    </mappers>
</configuration>

編寫測試類

package com.mybatis;

import com.entity.CustomerEntity;
import com.mapper.XieChengMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

public class XieChengTest {

    public static void main(String args[]){
        queryByid();
    }

    public static void queryByid(){
        SqlSession sqlSession = MybatisUtil.openSession();
        XieChengMapper xieChengMapper = sqlSession.getMapper(XieChengMapper.class);
        CustomerEntity customerEntity = xieChengMapper.queryById("123");

        System.out.println("id: "+customerEntity.getId());
        System.out.println("idno: "+customerEntity.getIdno());
        System.out.println("username: "+customerEntity.getUsername());
        System.out.println("sex: "+customerEntity.getSex());
        System.out.println("address: "+customerEntity.getMembershipCardEntity().getAddress());
        System.out.println("phone: "+customerEntity.getMembershipCardEntity().getPhone());
    }
}

執行後運行結果輸出如下。

id: 123
idno: 44088118
username: Tomandy
sex: male
address: Pk
phone: 18601111

association第二種用法:
修改xiechengMapper.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="com.mapper.XieChengMapper">
    <resultMap id="cmmap" type="cutomerEntity">
        <id property="id" column="id"/>
        <result property="idtype" column="idtype"/>
        <result property="idno" column="idno"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <!--property屬性值需要在CustomerEntity有相應的getter方法-->
        <!--javaType爲mybatisConfig.xml定義com.entity.MembershipCardEntity的別名-->
        <!--
        <association property="membershipCardEntity" javaType="membershipCardEntity">
            <id property="id" column="id"/>
            <result property="level" column="level"/>
            <result property="phone" column="phone"/>
            <result property="address" column="address"/>
        </association>
        -->
        <association property="membershipCardEntity" resultMap="mscmap"/>
    </resultMap>

    <resultMap id="mscmap" type="membershipCardEntity">
        <id property="id" column="id"/>
        <result property="level" column="level"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
    </resultMap>
    
    <select id="queryById" resultMap="cmmap" parameterType="String">
        select
        a.id,a.sex,a.idno,a.username,b.phone,b.address
        from customer a,membershipCard b
        where a.id = b.id
        and a.id = #{id}
    </select>

</mapper>

對比一下association第一種用法,區別在於把membershipCardEntity的映射獨立放在了一個resultMap裏。

association第三種用法:該方法通過association的select屬性來實現。假如給membershipCard表加上一列cardname(MembershipCardEntity實體類也加上了cardname對應的getter和setter方法),其值與customer表的username字段一樣,修改xiechengMapper.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="com.mapper.XieChengMapper">
    <resultMap id="cmmap" type="cutomerEntity">
        <id property="id" column="id"/>
        <result property="idtype" column="idtype"/>
        <result property="idno" column="idno"/>
        <result property="username" column="username"/>
        <result property="sex" column="sex"/>
        <!--property屬性值需要在CustomerEntity有相應的getter方法-->
        <!--javaType爲mybatisConfig.xml定義com.entity.MembershipCardEntity的別名-->
        <!--
        <association property="membershipCardEntity" javaType="membershipCardEntity">
            <id property="id" column="id"/>
            <result property="level" column="level"/>
            <result property="phone" column="phone"/>
            <result property="address" column="address"/>
        </association>
        -->
        <!--
        <association property="membershipCardEntity" resultMap="mscmap"/>
        -->
        <!--如果涉及多參數查詢的話,可通過column="{id=id,phone=phone}"格式來傳遞變量 -->
        <association property="membershipCardEntity" column="{id=id,cardname=username}" select="queryByCardId"/>
    </resultMap>

    <!--
    <resultMap id="mscmap" type="membershipCardEntity">
        <id property="id" column="id"/>
        <result property="level" column="level"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
    </resultMap>
    -->

    <!--對於多參數傳遞的場景,parameterType屬性值需使用Map-->
    <select id="queryByCardId" parameterType="java.util.Map" resultType="membershipCardEntity">
        select id,level,phone,address from membershipCard where id = #{id} and cardname =#{cardname}
    </select>
    <!--
    <select id="queryById" resultMap="cmmap" parameterType="String">
        select
        a.id,a.sex,a.idno,a.username,b.phone,b.address
        from customer a,membershipCard b
        where a.id = b.id
        and a.id = #{id}
    </select>
    -->
    <select id="queryById" resultMap="cmmap" parameterType="String">
        select
        *
        from customer 
        where id = #{id}
    </select>

</mapper>

執行上文的測試類,可發現輸出結果也是一樣的。由xiechengMapper.xml配置文件的內容,我們也可以推斷出先執行了queryById的select語句,然後再把對應列的值傳遞給queryByCardId的select語句變量進行查詢,從而獲取查詢結果。

  • collection子元素舉例說明
    collection用於一對多的關聯,與association類似,常見也有三種用法,下面舉例說明。

場景:
攜程會員可享受多種產品優惠(一對多)。

在上文association例子的基礎上,再新建一張產品優惠信息表並insert數據。

drop table if exists product;
create table product(
id varchar(20) not null,
productno varchar(30) not null,
productname varchar(100) not null,
PRIMARY KEY (`id`,`productno`)
);

insert into product(id,productno,productname) values("123","01","hotel");
insert into product(id,productno,productname) values("123","02","train tickets");
insert into product(id,productno,productname) values("123","03","plane tickets");

新增實體類ProductEntity,並修改MembershipCardEntity。

package com.entity;

public class ProductEntity {

    private String id;
    private String productno;
    private String productname;

    public String getId() {
        return id;
    }

    public String getProductno() {
        return productno;
    }

    public String getProductname() {
        return productname;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setProductno(String productno) {
        this.productno = productno;
    }

    public void setProductname(String productname) {
        this.productname = productname;
    }
}

package com.entity;

import java.util.List;

public class MembershipCardEntity {

    private String id;
    private int level;
    private String phone;
    private String address;
    private String cardname;
    private List<ProductEntity> productEntities;

    public String getId() {
        return id;
    }

    public int getLevel() {
        return level;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public String getCardname() {
        return cardname;
    }

    public List<ProductEntity> getProductEntities() {
        return productEntities;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setCardname(String cardname) {
        this.cardname = cardname;
    }

    public void setProductEntities(List<ProductEntity> productEntities) {
        this.productEntities = productEntities;
    }
}

修改映射接口XieChengMapper如下。

package com.mapper;

import com.entity.CustomerEntity;
import com.entity.MembershipCardEntity;

public interface XieChengMapper {

    CustomerEntity queryById(String id);

    //增加根據id查詢優惠產品信息
    MembershipCardEntity queryProductById(String id);
}

collection第一種用法如下:
修改映射文件xiechengMapper.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="com.mapper.XieChengMapper">
    <resultMap id="cmmap" type="membershipCardEntity">
        <id property="id" column="id"/>
        <result property="level" column="level"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
        <result property="cardname" column="cardname"/>
        <collection property="productEntities" javaType="java.util.List" ofType="com.entity.ProductEntity">
            <id property="id" column="id"/>
            <id property="productno" column="productno"/>
            <result property="productname" column="productname"/>
        </collection>
    </resultMap>

    <select id="queryProductById" parameterType="String" resultMap="cmmap">
        select * from membershipCard a,product b
        where a.id = b.id
        and
        a.id = #{id}
    </select>

</mapper>

修改測試類如下。

package com.mybatis;

import com.entity.CustomerEntity;
import com.entity.MembershipCardEntity;
import com.entity.ProductEntity;
import com.mapper.XieChengMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

public class XieChengTest {

    public static void main(String args[]){
        queryProductByid();
    }

    public static void queryProductByid(){
        SqlSession sqlSession = MybatisUtil.openSession();
        XieChengMapper xieChengMapper = sqlSession.getMapper(XieChengMapper.class);
        MembershipCardEntity membershipCardEntity = xieChengMapper.queryProductById("123");

        System.out.println("id: "+membershipCardEntity.getId());
        System.out.println("phone: "+membershipCardEntity.getPhone());
        System.out.println("address: "+membershipCardEntity.getAddress());
        System.out.println("cardname: "+membershipCardEntity.getCardname());
        System.out.println("Listsize: "+membershipCardEntity.getProductEntities().size());

        for(ProductEntity productEntity:membershipCardEntity.getProductEntities()){
            System.out.println("------------------------");
            System.out.println("id: "+productEntity.getId());
            System.out.println("productno: "+productEntity.getProductno());
            System.out.println("productname: "+productEntity.getProductname());

        }
    }

}

運行後,輸出結果爲如下。

id: 123
phone: 18601111
address: Pk
cardname: Tomandy
Listsize: 3
------------------------
id: 123
productno: 01
productname: hotel
------------------------
id: 123
productno: 02
productname: train tickets
------------------------
id: 123
productno: 03
productname: plane tickets

collection第二種用法如下:
修改xiechengMapper.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="com.mapper.XieChengMapper">
    <resultMap id="cmmap" type="membershipCardEntity">
        <id property="id" column="id"/>
        <result property="level" column="level"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
        <result property="cardname" column="cardname"/>
        
        <collection property="productEntities" resultMap="pmap"/>

        <!--
        <collection property="productEntities" javaType="java.util.List" ofType="com.entity.ProductEntity">
            <id property="id" column="id"/>
            <id property="productno" column="productno"/>
            <result property="productname" column="productname"/>
        </collection>
        -->
    </resultMap>

    <resultMap id="pmap" type="com.entity.ProductEntity">
        <id property="id" column="id"/>
        <id property="productno" column="productno"/>
        <result property="productname" column="productname"/>
    </resultMap>

    <select id="queryProductById" parameterType="String" resultMap="cmmap">
        select * from membershipCard a,product b
        where a.id = b.id
        and
        a.id = #{id}
    </select>

</mapper>

執行上文的測試類,可發現輸出同樣的結果。
collection第三種用法如下:
與association第三種用法類似,可通過select屬性來實現collection一對多的映射。修改xiechengMapper.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="com.mapper.XieChengMapper">
    <resultMap id="cmmap" type="membershipCardEntity">
        <id property="id" column="id"/>
        <result property="level" column="level"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
        <result property="cardname" column="cardname"/>

        <!--
        <collection property="productEntities" resultMap="pmap"/>
        -->

        <!--
        <collection property="productEntities" javaType="java.util.List" ofType="com.entity.ProductEntity">
            <id property="id" column="id"/>
            <id property="productno" column="productno"/>
            <result property="productname" column="productname"/>
        </collection>
        -->

        <collection property="productEntities" column="id" select="queryProductById"/>
    </resultMap>

    <!--
    <resultMap id="pmap" type="com.entity.ProductEntity">
        <id property="id" column="id"/>
        <id property="productno" column="productno"/>
        <result property="productname" column="productname"/>
    </resultMap>
    -->
    
    <select id="queryProductById" parameterType="java.util.Map" resultType="com.entity.ProductEntity">
        select * from product where id = #{id}
    </select>

    <select id="queryCardById" parameterType="String" resultMap="cmmap">
        select * from membershipCard where id = #{id}
    </select>

</mapper>

修改XieChengMapper映射接口如下。

package com.mapper;

import com.entity.CustomerEntity;
import com.entity.MembershipCardEntity;
import com.entity.ProductEntity;

import java.util.List;

public interface XieChengMapper {

    CustomerEntity queryById(String id);

    MembershipCardEntity queryCardById(String id);

    List<ProductEntity> queryProductById(String id);
}

編寫測試類

package com.mybatis;

import com.entity.CustomerEntity;
import com.entity.MembershipCardEntity;
import com.entity.ProductEntity;
import com.mapper.XieChengMapper;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

public class XieChengTest {

    public static void main(String args[]){
        queryProductByid();
    }

    public static void queryProductByid(){
        SqlSession sqlSession = MybatisUtil.openSession();
        XieChengMapper xieChengMapper = sqlSession.getMapper(XieChengMapper.class);
        MembershipCardEntity membershipCardEntity = xieChengMapper.queryCardById("123");

        System.out.println("id: "+membershipCardEntity.getId());
        System.out.println("phone: "+membershipCardEntity.getPhone());
        System.out.println("address: "+membershipCardEntity.getAddress());
        System.out.println("cardname: "+membershipCardEntity.getCardname());
        System.out.println("Listsize: "+membershipCardEntity.getProductEntities().size());

        for(ProductEntity productEntity:membershipCardEntity.getProductEntities()){
            System.out.println("------------------------");
            System.out.println("id: "+productEntity.getId());
            System.out.println("productno: "+productEntity.getProductno());
            System.out.println("productname: "+productEntity.getProductname());

        }
    }

}

運行後發現輸出結果與上文的一致。

  • discriminator子元素舉例說明
    discriminator稱爲鑑別器,依照官網的描述,主要用於以下場景。

有時一個單獨的數據庫查詢也許返回很多不同 (但是希望有些關聯) 數據類型的結果集。 鑑別器元素就是被設計來處理這個情況的, 還有包括類的繼承層次結構。 鑑別器非常容易理 解,因爲它的表現很像 Java 語言中的 switch 語句。

關於鑑別器的說明參考文章《MyBatis級聯探討第二篇——鑑別器(discriminator)》

參考資料

《開發測試的Spring應用》
mybatis實戰教程(mybatis in action),mybatis入門到精通
官方文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章