前言
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入門到精通
官方文檔