在mybatis映射文件每編寫(xiě)一條sql語(yǔ)句,我們都必須關(guān)注sql的傳參以及返回結(jié)果集映射。傳入?yún)?shù)是為了動(dòng)態(tài)的封裝執(zhí)行的sql語(yǔ)句,而結(jié)果集映射就是告訴mybatis如何將從數(shù)據(jù)庫(kù)查詢到的記錄封裝成我們需要的結(jié)果類型。例如通過(guò)id查詢user對(duì)象,我們就需要將id作為參數(shù)傳遞進(jìn)去,然后將查詢記錄封裝到一個(gè)user對(duì)象中,要實(shí)現(xiàn)這個(gè)功能,我們就必須知道參數(shù)id是如何注入sql以及查詢結(jié)果記錄如何封裝到user對(duì)象里面的。
傳參
使用的實(shí)體類如下:
package com.dahuici.zyb.entity;
import lombok.Data;
@Data
public class User {
private String name;
private Integer age;
private Integer userid;
}
1)傳入基本數(shù)據(jù)類型參數(shù)
傳入單個(gè)的基本數(shù)據(jù)類型,以String為例
對(duì)于String參數(shù),首先在mapper接口的方法傳入?yún)?shù)
User getUserByName(String name);
然后在指定select標(biāo)簽的parameterType="string"(這里指定為string數(shù)據(jù)類型,與綁定的mapper接口方法的參數(shù)保持一致。這里使用了mybatis的系統(tǒng)別名,對(duì)于系統(tǒng)別名的數(shù)據(jù)字典可自行百度),指定返回類型是user實(shí)體類,這里暫時(shí)不管查詢結(jié)果記錄與user實(shí)體類是如何建立聯(lián)系的。
<select id="getUserByName" parameterType="string" resultType="com.dahuici.zyb.entity.User">
select * from user where name = #{name}
</select>
這里需要注意的就是,如果只傳入一個(gè)參數(shù),并且該參數(shù)是基本數(shù)據(jù)類型,那么當(dāng)使用#{}對(duì)參數(shù)值進(jìn)行注入時(shí),不管#{}里面是什么值,mybatis都會(huì)將唯一的參數(shù)注入進(jìn)去,意思就是#{}里面的值不用和mapper接口方法的參數(shù)變量名相同,當(dāng)然,為了方便看出傳遞關(guān)系,一般都將兩個(gè)值定義成相同的。
但是,如果你需要用傳入的參數(shù)進(jìn)行條件判斷,即在mybatis的動(dòng)態(tài)sql中引用參數(shù),那么就必須使用@Param("")定義傳入?yún)?shù)的引用名,如下
User getUserByName(@Param("name")String name);
當(dāng)使用了@Param進(jìn)行定義過(guò)后,就可以在sql中通過(guò)@Param定義的引用名name使用傳入?yún)?shù)的值了,綜合來(lái)說(shuō),建議只要傳入基本數(shù)據(jù)類型的參數(shù),都使用@Param定義引用名。
對(duì)于其他的基本數(shù)據(jù)類型的單個(gè)參數(shù)傳遞,與String類型的使用方式基本相同,不在贅述,以后如果使用其他的基本數(shù)據(jù)類型存在不同之處,再進(jìn)行補(bǔ)充。
多個(gè)基本數(shù)據(jù)類型,以傳入一個(gè)String與一個(gè)Integer類型參數(shù)為例
編寫(xiě)mapper接口的方法,由于是多個(gè)參數(shù),所以必須使用@Param定義引用名。
User getUserByString(@Param("name")String name,@Param("age")Integer age);
給mapper接口方法綁定sql。由于是多個(gè)參數(shù),參數(shù)類型不一定相同,這里就不需要parameterType指定參數(shù)類型了。
<select id="getUserByString" resultType="com.dahuici.zyb.entity.User">
select * from user where name = #{name} and age = #{age}
</select>
2)傳入實(shí)體類參數(shù)
當(dāng)在mapper接口方法中,傳遞實(shí)體類時(shí),方法如下
User getUserByUser(User user);
由于參數(shù)是實(shí)體類,在映射文件的select標(biāo)簽的sql里面,通過(guò) #{實(shí)體類屬性名} 的方式就可以使用該屬性的值了,在動(dòng)態(tài)sql里面也可以通過(guò)屬性名直接使用該屬性的值。
<select id="getUserByUser" parameterType="com.dahuici.zyb.entity.User" resultType="com.dahuici.zyb.entity.User">
select * from user
<if test="name != null">
where name = #{name} and age = #{age}
</if>
</select>
這里使用if只是簡(jiǎn)單的做了一個(gè)非空的判斷。
也可以使用@Param進(jìn)行定義參數(shù),如下
User getUserByUser(@Param("user")User user);
使用時(shí)在前面加上@Param定義的值就行了。使用如下
<select id="getUserByUser" parameterType="com.dahuici.zyb.entity.User" resultType="com.dahuici.zyb.entity.User">
select * from user
<if test="user.name != null">
where name = #{user.name} and age = #{user.age}
</if>
</select>
與多個(gè)基本數(shù)據(jù)類型同理,當(dāng)傳入多個(gè)實(shí)體類對(duì)象時(shí),必須用@Param指定引用別名并不能使用parameterType指定傳入?yún)?shù)類型。
3)傳入一個(gè)Map
需要提一下的是,當(dāng)傳入多個(gè)參數(shù)時(shí),其實(shí)mybatis都會(huì)把這些參數(shù)封裝到一個(gè)map中,通過(guò)@Param指定key,value表示傳入的具體參數(shù)值,所以當(dāng)需要傳入多個(gè)參數(shù)時(shí),其實(shí)都可以用一個(gè)Map代替。
首先定義mapper接口方法,定義map時(shí),由于value的類型不一定相同,這里就定義成Object對(duì)象。
User getUserByMap(Map<String, Object> map);
給接口方法綁定sql,如果map里面value是基本數(shù)據(jù)類型,那么就直接通過(guò)key注入,如果value是一個(gè)對(duì)象,那么就使用 key.屬性名 的方式注入。
<select id="getUserByMap" parameterType="map" resultType="com.dahuici.zyb.entity.User">
select * from user
<if test="name != null">
where name = #{name} and age = #{user.age}
</if>
</select>
執(zhí)行測(cè)試
@Test
public void getUserByMap() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name", "zyb");
User user1 = new User();
user1.setAge(22);
map.put("age", 22);
map.put("user", user1);
User user = mapper.getUserByMap(map);
System.out.println(user);
}
目前覺(jué)得,對(duì)于參數(shù)的傳遞,個(gè)人覺(jué)得以上方式就夠用了,如果遇見(jiàn)新的需求再進(jìn)行補(bǔ)充。另外,還可以傳入集合對(duì)象,里面存儲(chǔ)了很多實(shí)體,用于批量插入,需要?jiǎng)討B(tài)sql,所以在動(dòng)態(tài)sql里面詳解。
結(jié)果集映射
返回一個(gè)實(shí)體對(duì)象
mapper接口方法
User getUserById(Integer id);
給接口方法綁定sql
<!-- 查詢 -->
<select id="getUserById" parameterType="int" resultType="com.dahuici.zyb.entity.User">
select * from user where userid = #{id}
</select>
測(cè)試代碼和執(zhí)行結(jié)果
@Test
public void JunitTest() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//User user1 = (User)sqlSession.selectOne("getUserById", 2);
//System.out.println(user1);
User user = mapper.getUserById(2);
System.out.println(user);
}

可以看出,接口方法返回User對(duì)象然后在<select>標(biāo)簽中定義resultType="com.dahuici.zyb.entity.User",mybatis就會(huì)把返回的記錄自動(dòng)封裝成User對(duì)象返回。這里沒(méi)有對(duì)mysql查詢的結(jié)果記錄與實(shí)體類的屬性建立任何的關(guān)聯(lián),但是當(dāng)沒(méi)有定義關(guān)聯(lián)時(shí),mybatis會(huì)默認(rèn)將mybatis查詢出的結(jié)果字段與其同名的實(shí)體類屬性建立關(guān)聯(lián),進(jìn)行實(shí)體類屬性的封裝,所以這里返回的user對(duì)象的各個(gè)屬性才有值。實(shí)體類與數(shù)據(jù)庫(kù)表的對(duì)應(yīng)關(guān)系如下:


除了使用resultType建立默認(rèn)的映射關(guān)系外,還可以使用resultMap屬性結(jié)合<resultMap>標(biāo)簽手動(dòng)的建立實(shí)體類屬性與mysql結(jié)果集字段映射關(guān)系。主要代碼如下:
<!-- 查詢 -->
<select id="getUserById" parameterType="int" resultMap="usermap">
select * from user where userid = #{id}
</select>
<resultMap type="com.dahuici.zyb.entity.User" id="usermap">
<id column="userid" property="userid"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
這里首先就是要定義一個(gè)<resultMap>標(biāo)簽,通過(guò)type屬性指定實(shí)體類,id定義該<resultMap>的唯一標(biāo)識(shí),然后通過(guò)<id>子標(biāo)簽定義主鍵字段與實(shí)體類屬性的映射關(guān)系,<result>子標(biāo)簽定義普通字段與實(shí)體類屬性的映射關(guān)系,這里只需記住的是column的值為數(shù)據(jù)庫(kù)表的字段名,property的值為實(shí)體類的屬性名。當(dāng)定義好后,將<select>的resultMap屬性值設(shè)置為<resultMap>標(biāo)簽的id值就可以使用該映射關(guān)系,查詢的結(jié)果將使用該映射關(guān)系對(duì)實(shí)體類進(jìn)行封裝。<resultMap>標(biāo)簽的作用就是建立數(shù)據(jù)庫(kù)查詢結(jié)果與實(shí)體類的映射關(guān)系,并且只要定義好后,可以被任何有需求的<select>標(biāo)簽使用。
一對(duì)一關(guān)系
簡(jiǎn)單描述,一般在大學(xué)里面,一個(gè)學(xué)生都只有一個(gè)有效的一卡通,這里就定義一個(gè)學(xué)生與一卡通的實(shí)體類,代碼和數(shù)據(jù)庫(kù)表結(jié)構(gòu)如下。
student類
@Data
public class Student {
private Integer studentid;
private String name;
private Integer age;
private Card card;
}
card類
@Data
public class Card {
private Integer cardid;
private String schooolname;
}
數(shù)據(jù)庫(kù)表結(jié)構(gòu)


需求描述:獲取id為1的學(xué)生以及它的一卡通信息。
方式一 引用其他select標(biāo)簽。
首先在mapper接口中定義兩個(gè)方法
Student getStudentById(Integer studentid);
Card getCardById(Integer cardid);
給接口方法綁定sql
<select id="getStudentById" parameterType="int" resultMap="studentmap">
select * from student where studentid = #{studentid}
</select>
<select id="getCardById" parameterType="int" resultMap="cardmap">
select * from card where cardid = #{cardid}
</select>
<resultMap type="com.dahuici.zyb.entity.Student" id="studentmap">
<id column="studentid" property="studentid"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<association property="card" column="cardid" select="getCardById"></association>
</resultMap>
<resultMap type="com.dahuici.zyb.entity.Card" id="cardmap">
<id column="cardid" property="cardid"/>
<result column="schooolname" property="schooolname"/>
</resultMap>
大概的sql之間的聯(lián)系關(guān)系如下:

主要介紹一下<resultMap>的子標(biāo)簽<association>,使用的代碼如下:
<association property="card" column="cardid" select="getCardById"></association>
<association>就是為了用于綁定實(shí)體類的屬性中有另一個(gè)實(shí)體類的情況,這里是為了封裝學(xué)生對(duì)象(Student)的一卡通對(duì)象屬性(Card),通過(guò)property指定一卡通對(duì)象對(duì)應(yīng)與學(xué)生對(duì)象的名為card的屬性,并引用id為getCardById的<select>標(biāo)簽并指定將id為getStudentById的<select>的查詢結(jié)果的cardid字段值作為參數(shù)傳遞過(guò)去。所以,這種方式執(zhí)行了兩條sql,執(zhí)行代碼與輸出結(jié)果如下:
@Test
public void getStudentById() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Student student = mapper.getStudentById(1);
System.out.println(student);
}

突然靈機(jī)一動(dòng),當(dāng)獲取多個(gè)學(xué)生及其一卡通信息時(shí),sql會(huì)怎樣執(zhí)行。結(jié)果和其原理差不多,每封裝一個(gè)學(xué)生對(duì)象,都會(huì)執(zhí)行一卡通的查詢sql,嚴(yán)重影響性能,不推薦獲取集合時(shí)使用該方式,測(cè)試的sql執(zhí)行情況如下。
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1301664418.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d95d2a2]
==> Preparing: select * from student
==> Parameters:
<== Columns: studentid, name, age, cardid
<== Row: 1, zyb, 23, 1
====> Preparing: select * from card where cardid = ?
====> Parameters: 1(Integer)
<==== Columns: cardid, schooolname
<==== Row: 1, *****大學(xué)
<==== Total: 1
<== Row: 2, 張三, 33, 2
====> Preparing: select * from card where cardid = ?
====> Parameters: 2(Integer)
<==== Columns: cardid, schooolname
<==== Row: 2, *****大學(xué)1
<==== Total: 1
<== Row: 3, 李四, 34, 3
====> Preparing: select * from card where cardid = ?
====> Parameters: 3(Integer)
<==== Columns: cardid, schooolname
<==== Row: 3, *****大學(xué)2
<==== Total: 1
<== Total: 3
[Student(studentid=1, name=zyb, age=23, card=Card(cardid=1, schooolname=*****大學(xué))), Student(studentid=2, name=張三, age=33,
card=Card(cardid=2, schooolname=*****大學(xué)1)), Student(studentid=3, name=李四, age=34, card=Card(cardid=3, schooolname=*****大
學(xué)2))]
方式二 使用sql連接的方式
接口方法
Student getStudentById(Integer studentid);
綁定給方法綁定sql
<select id="getStudentById" parameterType="int" resultMap="studentmap">
select * from student inner join card on card.cardid = student.studentid where studentid = #{studentid}
</select>
<resultMap type="com.dahuici.zyb.entity.Student" id="studentmap">
<id column="studentid" property="studentid"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<association property="card" resultMap="cardmap"></association>
</resultMap>
<resultMap type="com.dahuici.zyb.entity.Card" id="cardmap">
<id column="cardid" property="cardid"/>
<result column="schooolname" property="schooolname"/>
</resultMap>
該方式使用sql的連接查詢將結(jié)果都查詢出來(lái),然后將屬于Card類的屬性與對(duì)應(yīng)的字段建立對(duì)應(yīng)的映射關(guān)系就可以實(shí)現(xiàn)一對(duì)一關(guān)系了,只要使用<association>標(biāo)簽將Student的card屬性區(qū)別出來(lái)就可以了,這里需要注意的時(shí)sql查詢出來(lái)的結(jié)果不能有重復(fù)的字段,有重復(fù)的字段就不好建立映射關(guān)系,會(huì)直接取第一個(gè)出現(xiàn)該名稱的字段。
如果需要查詢Student集合,那么映射關(guān)系保持不變,只需將mapper接口的對(duì)應(yīng)方法(這里是getStudentById方法)的返回值類型變?yōu)?List<實(shí)體類>,然后保證執(zhí)行sql查詢出來(lái)的結(jié)果是多條記錄就可以了,mybatis會(huì)自動(dòng)通過(guò)映射關(guān)系將查詢出來(lái)的多條記錄封裝成一個(gè)個(gè)實(shí)體類并存入list。
一對(duì)多關(guān)系
需要兩個(gè)實(shí)體類,一個(gè)Student,一個(gè)Teacher類,顯然,一個(gè)語(yǔ)文老師有多個(gè)學(xué)生(我這里的設(shè)定為一個(gè)學(xué)生只有一個(gè)老師)
相關(guān)實(shí)體類以及數(shù)據(jù)庫(kù)表結(jié)構(gòu)
package com.dahuici.zyb.entity;
import java.util.List;
import lombok.Data;
@Data
public class Teacher {
private Integer teacherid;
private String name;
private Integer age;
private List<Student> studentlist;
}
package com.dahuici.zyb.entity;
import lombok.Data;
@Data
public class Student {
private Integer studentid;
private String name;
private Integer age;
private Card card;
private Teacher teacher;
}


需求,查出id為1的老師的和其所有的學(xué)生以及學(xué)生的一卡通信息
接口方法
Teacher getTeacherById(Integer id);
<select id="getTeacherById" parameterType="int" resultMap="teachermap">
select
teacher.teacherid as teacherid,teacher.name as tname,
teacher.age as tage,studentid,student.name as sname,
student.age as sage,card.cardid as cardid,schooolname
from
teacher
inner join
student
on
teacher.teacherid = student.teacherid
inner join
card
on
card.cardid = student.studentid
where teacher.teacherid = #{id}
</select>
<resultMap type="com.dahuici.zyb.entity.Teacher" id="teachermap">
<id column="teacherid" property="teacherid"/>
<result column="tname" property="name"/>
<result column="tage" property="age"/>
<collection property="studentlist" resultMap="studentmap"></collection>
</resultMap>
<resultMap type="com.dahuici.zyb.entity.Student" id="studentmap">
<id column="studentid" property="studentid"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<association property="card" resultMap="cardmap"></association>
</resultMap>
<resultMap type="com.dahuici.zyb.entity.Card" id="cardmap">
<id column="cardid" property="cardid"/>
<result column="schooolname" property="schooolname"/>
</resultMap>
執(zhí)行測(cè)試代碼和輸出結(jié)果
@Test
public void getTecherById() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Teacher teacher = mapper.getTeacherById(1);
System.out.println(teacher);
}
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 990416209.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b088d51]
==> Preparing: select teacher.teacherid as teacherid,teacher.name as tname, teacher.age as tage,studentid,student.name as sname, student.age as sage,card.cardid as cardid,schooolname from teacher inner join student on teacher.teacherid = student.teacherid inner join card on card.cardid = student.studentid where teacher.teacherid = ?
==> Parameters: 1(Integer)
<== Columns: teacherid, tname, tage, studentid, sname, sage, cardid, schooolname
<== Row: 1, 張老師, 26, 1, zyb, 23, 1, *****大學(xué)
<== Row: 1, 張老師, 26, 2, 張三, 33, 2, *****大學(xué)1
<== Row: 1, 張老師, 26, 3, 李四, 34, 3, *****大學(xué)2
<== Total: 3
Teacher(teacherid=1, name=張老師, age=26, studentlist=[Student(studentid=1, name=zyb, age=23, card=Card(cardid=1,
schooolname=*****大學(xué)), teacher=null), Student(studentid=2, name=張三, age=33, card=Card(cardid=2, schooolname=*****大學(xué)1),
teacher=null), Student(studentid=3, name=李四, age=34, card=Card(cardid=3, schooolname=*****大學(xué)2), teacher=null)])
由于查詢結(jié)果里存在重復(fù)字段名,比如老師與學(xué)生的name字段,所以這里將重復(fù)的字段都定義了別名。
這里即用到了一對(duì)多關(guān)系(老師對(duì)學(xué)生),也用到了一對(duì)一關(guān)系(學(xué)生對(duì)一卡通)。通過(guò)看映射文件里面的配置,可以看出一對(duì)一與一對(duì)多唯一的區(qū)別就是一對(duì)一使用的是<resultMap>的<association>子標(biāo)簽,而一對(duì)多用的是<collection>
以上代碼都是為了方便自己回憶復(fù)習(xí)時(shí)使用的,僅供參考。