文章目錄
- 1、綜述
- 2、實作Mapper動态代理
-
- 2.1 映射檔案的namespace屬性值
- 2.2 Dao接口方法名
- 2.3 Dao對象的擷取
- 2.4 删除Dao實作類
- 3、測試案例
-
- 3.1 建立測試資料表
- 3.2 建立實體類
- 3.3 建立Dao檔案
- 3.4 建立SQL映射檔案
- 3.5 修改資料源方法
- 3.6 建立測試類
- 3.7 對球員的增删改查操作測試
- 4、注意
- 5、解決多查詢條件無法整體接收問題
-
- 5.1 修改接口Dao檔案
- 5.2 修改SQL映射檔案
- 5.3 修改測試類
1、綜述
一般建立Web工程時,從資料庫取資料的邏輯會放置在Dao層(Data Access Object,資料通路對象)。在之前的系列部落格的執行個體中,我們發現:Dao的實作類其實并沒有幹什麼實質性的工作,它僅僅就是通過SqlSession的相關API定位到映射檔案mapper中相應id的SQL語句,真正對DB進行操作的工作其實是由架構通過mapper中的SQL完成的。
是以在使用MyBatis開發Web工程時,通過Mapper動态代理機制,可以隻編寫資料互動的接口及方法定義,和對應的Mapper映射檔案,具體的互動方法實作由MyBatis來完成, 這樣大大節省了開發Dao層的時間。這種Dao的實作方式稱為Mapper的動态代理方式。Mapper的動态代理方式無須程式員實作Dao接口,接口是由MyBatis結合映射檔案自動生成的動态代理實作的。
2、實作Mapper動态代理
2.1 映射檔案的namespace屬性值
一般情況下,一個Dao接口的實作類方法使用的是同一個SQL映射id。是以,MyBatis架構要求,将映射檔案中mapper标簽的namespace屬性設定為Dao接口的全類名, 則系統會根據方法所屬的Dao接口,自動到相應namespace的映射檔案中查找相關的SQL映射。
簡單來說,通過接口名即可定位到SQL映射檔案。
2.2 Dao接口方法名
MyBatis架構要求,接口中的方法名,與映射檔案中相應的SQL标簽的id值相同。 系統會自動根據方法名到相應的映射檔案中查找同名的SQL映射id。
簡單來說,通過方法名就可以定位到SQL映射檔案中相應的SQL語句。
2.3 Dao對象的擷取
在本部落格之前的所有執行個體中,均是通過com.ccff.mybatis.datasource包下的DataConnection類中的getSqlSession方法來擷取SqlSession對象,最終利用SqlSession對象的API方法實作對資料庫的操作。
在使用Mapper動态代理後,需要用SqlSession對象的getMapper方法,通過動态代理的方式擷取Mapper代理,通過動态代理的方式實作接口相應方法并調用使用。
2.4 删除Dao實作類
由于通過調用Dao接口方法,不僅可以從SQL映射檔案中找到所要執行的SQL語句,還可以通過方法參數及傳回值,将SQL語句的動态參數傳入,将查詢結果傳回。是以,Dao的實作工作,完全可以由MyBatis系統自動根據映射檔案完成。是以,Dao的實作類就不需要了。
Dao實作對象時由JDK的Proxy動态代理自動生成的。
3、測試案例
3.1 建立測試資料表
在mybaits資料庫中建立名為“basketballplayer”的資料表,并添加測試資料。具體的建表語句與插入資料語句如下所示:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `basketballplayer`
-- ----------------------------
DROP TABLE IF EXISTS `basketballplayer`;
CREATE TABLE `basketballplayer` (
`id` int(25) NOT NULL auto_increment,
`name` varchar(255) default NULL,
`age` int(2) default NULL,
`weight` double(25,1) default NULL,
`height` int(25) default NULL,
`number` int(25) default NULL,
`team` varchar(255) default NULL,
`position` varchar(255) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of basketballplayer
-- ----------------------------
INSERT INTO `basketballplayer` VALUES ('1', 'LeBron James', '35', '113.4', '203', '23', 'LAL', 'F');
INSERT INTO `basketballplayer` VALUES ('2', 'Stephen Curry', '31', '86.2', '190', '30', 'WAS', 'G');
INSERT INTO `basketballplayer` VALUES ('3', 'Kevin Durant', '31', '108.8', '205', '35', 'WAS', 'F');
INSERT INTO `basketballplayer` VALUES ('4', 'James Harden', '30', '99.8', '195', '13', 'HOU', 'G');
INSERT INTO `basketballplayer` VALUES ('5', 'Chris Paul', '34', '79.4', '182', '3', 'HOU', 'G');
建立好以後的資料表如下所示:
3.2 建立實體類
在com.ccff.mybatis.model包下建立名為“BasketballPlayer”的實體類,并提供有參和無參構造方法、get和set方法以及toString方法。具體代碼如下:
package com.ccff.mybatis.model;
public class BasketballPlayer {
private int id;
private String name;
private int age;
private Double weight;
private int height;
private int number;
private String team;
private String position;
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age, Double weight, int height, int number, String team, String position) {
this.name = name;
this.age = age;
this.weight = weight;
this.height = height;
this.number = number;
this.team = team;
this.position = position;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getNumber() {
return number;
}
public void setNumber(int num) {
this.number = num;
}
public String getTeam() {
return team;
}
public void setTeam(String team) {
this.team = team;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
@Override
public String toString() {
return "BasketballPlayer{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
", height=" + height +
", number=" + number +
", team='" + team + '\'' +
", position='" + position + '\'' +
'}';
}
}
3.3 建立Dao檔案
在com.ccff.mybatis.dao包下建立名為“IBasketballPlayerDao”的接口,在該接口中定義對籃球運動員的增删改查基礎操作方法。具體代碼如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.BasketballPlayer;
public interface IBasketballPlayerDao{
//添加球員
void addBasketballPlayer(BasketballPlayer player);
//根據id删除球員
void deleteBasketballPlayerById(int id);
//根據id修改球員
void updateBasketballPlayerById(BasketballPlayer player);
//根據id查詢球員
BasketballPlayer selectBasketballPlayerById(int id);
//查詢所有球員
List<BasketballPlayer> selectAllasketballPlayer();
}
3.4 建立SQL映射檔案
在config/sqlmap檔案夾下建立名為“BasketballPlayerMapper”的XML檔案,将mapper标簽的namespace設定為IBasketballPlayerDao的全類名,并實作Mapper動态代理,具體代碼如下:
<?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.ccff.mybatis.dao.IBasketballPlayerDao">
<!--增加球員-->
<insert id="addBasketballPlayer" parameterType="BasketballPlayer">
insert into basketballplayer(name,age,weight,height,number,team,position)
values (#{name},#{age},#{weight},#{height},#{number},#{team},#{position})
</insert>
<!--根據id删除球員-->
<delete id="deleteBasketballPlayerById" parameterType="BasketballPlayer">
delete from basketballplayer where id = #{id}
</delete>
<!--根據id修改球員-->
<update id="updateBasketballPlayerById" parameterType="BasketballPlayer">
update basketballplayer set height = #{height} where id = #{id}
</update>
<!--根據id查詢球員-->
<select id="selectBasketballPlayerById" resultType="BasketballPlayer">
select * from basketballplayer where id = #{id}
</select>
<!--查詢所有球員-->
<select id="selectAllasketballPlayer" resultType="BasketballPlayer">
select * from basketballplayer
</select>
</mapper>
将建立好的SQL映射檔案配置到全局配置檔案SqlMapConfig.xml中,具體配置如下:
<mappers>
<mapper resource="sqlmap/UserMapper.xml"/>
<mapper resource="sqlmap/StudentMapper.xml"/>
<mapper resource="sqlmap/BasketballPlayerMapper.xml"/>
</mappers>
3.5 修改資料源方法
在com.ccff.mybatis.datasource包下修改DataConnection類,在該類中添加名為“getBasketballPlayerMapper”的方法用于通過動态代理的方式擷取IBasketballPlayerDao對象,具體代碼如下:
public IBasketballPlayerDao getBasketballPlayerMapper() {
IBasketballPlayerDao basketballPlayerMapper = sqlSession.getMapper(IBasketballPlayerDao.class);
return basketballPlayerMapper;
}
3.6 建立測試類
在com.ccff.mybatis.test包下建立名為“BasketballPlayerTest”的測試類,具體代碼如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.IBasketballPlayerDao;
import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.BasketballPlayer;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class BasketballPlayerTest {
private IBasketballPlayerDao basketballPlayerMapper;
private SqlSession sqlSession;
public BasketballPlayerTest(){
super();
DataConnection dataConnection = new DataConnection();
try {
sqlSession = dataConnection.getSqlSession();
basketballPlayerMapper = dataConnection.getBasketballPlayerMapper();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void TestAddBasketballPlayer(){
System.out.println("插入球員前:");
TestSelectAllasketballPlayer();
BasketballPlayer player = new BasketballPlayer("Kawhi Leonard",28,104.3,200,2,"TRO","F");
basketballPlayerMapper.addBasketballPlayer(player);
sqlSession.commit();
sqlSession.close();
System.out.println("插入球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestDeleteBasketballPlayerById(){
System.out.println("删除球員前:");
TestSelectAllasketballPlayer();
basketballPlayerMapper.deleteBasketballPlayerById(5);
sqlSession.commit();
sqlSession.close();
System.out.println("删除球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestUpdateBasketballPlayerById(){
System.out.println("修改球員前:");
TestSelectAllasketballPlayer();
BasketballPlayer player = new BasketballPlayer();
BasketballPlayer basketballPlayer = null;
player.setId(3);
player.setHeight(213);
basketballPlayerMapper.updateBasketballPlayerById(player);
sqlSession.commit();
sqlSession.close();
System.out.println("修改球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestSelectBasketballPlayerById(){
BasketballPlayer player = basketballPlayerMapper.selectBasketballPlayerById(3);
sqlSession.close();
System.out.println(player);
}
@Test
public void TestSelectAllasketballPlayer(){
List<BasketballPlayer> players = basketballPlayerMapper.selectAllasketballPlayer();
for (BasketballPlayer player : players){
System.out.println(player);
}
sqlSession.close();
}
}
3.7 對球員的增删改查操作測試
運作添加球員的測試方法TestAddBasketballPlayer,得到如下結果說明測試正确。
運作删除球員的測試方法TestDeleteBasketballPlayerById,得到如下結果說明測試正确。
運作修改球員的測試方法TestUpdateBasketballPlayerById,得到如下結果說明測試正确。
運作查詢球員的測試方法TestSelectBasketballPlayerById,得到如下結果說明測試正确。
運作查詢全部球員的測試方法TestSelectAllasketballPlayer,得到如下結果說明測試正确。
4、注意
MyBatis架構對于Dao查詢的自動實作,底層隻會調用selectOne方法與selectList方法。 而架構選擇方法的标準是測試類中用于接收傳回值的對象類型。若接收類型為List,則自動選擇selectList方法;否則,自動選擇selectOne方法
5、解決多查詢條件無法整體接收問題
在實際工作過程中,表單中所給出的查詢條件有時是無法将其封裝成一個對象的,也就是說,查詢方法隻能攜帶多個參數,而不能攜帶将這多個參數進行封裝的一個對象。對于這個問題,有以下兩種解決辦法。
- 将多個參數封裝成一個Map
- 逐個接收多個參數
5.1 修改接口Dao檔案
修改接口Dao檔案IBasketballPlayerDao,在接口中添加用于測試的方法,具體代碼如下:
package com.ccff.mybatis.dao;
import com.ccff.mybatis.model.BasketballPlayer;
import java.util.List;
public interface IBasketballPlayerDao {
//添加球員
void addBasketballPlayer(BasketballPlayer player);
//根據id删除球員
void deleteBasketballPlayerById(int id);
//根據id修改球員
void updateBasketballPlayerById(BasketballPlayer player);
//根據id查詢球員
BasketballPlayer selectBasketballPlayerById(int id);
//查詢所有球員
List<BasketballPlayer> selectAllasketballPlayer();
//根據指定條件查詢球員(示範多查詢條件接收問題)
List<BasketballPlayer> selectBasketballPlayerByConditionByMap(Map<String,Object> map);
List<BasketballPlayer> selectBasketballPlayerByConditionByIndex(String name,int age);
}
5.2 修改SQL映射檔案
修改接口SQL映射檔案BasketballPlayerMapper.xml,在SQL映射檔案中添加對應的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="com.ccff.mybatis.dao.IBasketballPlayerDao">
<!--增加球員-->
<insert id="addBasketballPlayer" parameterType="BasketballPlayer">
insert into basketballplayer(name,age,weight,height,number,team,position)
values (#{name},#{age},#{weight},#{height},#{number},#{team},#{position})
</insert>
<!--根據id删除球員-->
<delete id="deleteBasketballPlayerById" parameterType="BasketballPlayer">
delete from basketballplayer where id = #{id}
</delete>
<!--根據id修改球員-->
<update id="updateBasketballPlayerById" parameterType="BasketballPlayer">
update basketballplayer set height = #{height} where id = #{id}
</update>
<!--根據id查詢球員-->
<select id="selectBasketballPlayerById" resultType="BasketballPlayer">
select * from basketballplayer where id = #{id}
</select>
<!--查詢所有球員-->
<select id="selectAllasketballPlayer" resultType="BasketballPlayer">
select * from basketballplayer
</select>
<!--根據指定條件查詢球員(示範多查詢條件接收問題)-->
<select id="selectBasketballPlayerByConditionByMap" resultType="BasketballPlayer">
select * from basketballplayer where name like '%' #{nameCondition} '%' and age > #{ageCondition}
</select>
<select id="selectBasketballPlayerByConditionByIndex" resultType="BasketballPlayer">
select * from basketballplayer where name like '%' #{0} '%' and age > #{1}
</select>
</mapper>
通過SQL映射檔案的配置可以看到,若通過将多個參數封裝成一個Map的方式解決多查詢參數問題,則#{}中填寫的是Map的鍵名;若通過逐個接收參數的方式解決多查詢參數問題,則#{}中填寫的是每一個參數的下标,下标的參數時從0開始的。
5.3 修改測試類
修改接口SQL映射檔案BasketballPlayerTest.xml,在測試類中添加名為“TestSelectBasketballPlayerByConditionByMap”和“TestSelectBasketballPlayerByConditionByIndex”的兩個測試方法,具體配置如下:
package com.ccff.mybatis.test;
import com.ccff.mybatis.dao.IBasketballPlayerDao;
import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.BasketballPlayer;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BasketballPlayerTest {
private IBasketballPlayerDao basketballPlayerMapper;
private SqlSession sqlSession;
public BasketballPlayerTest(){
super();
DataConnection dataConnection = new DataConnection();
try {
sqlSession = dataConnection.getSqlSession();
basketballPlayerMapper = dataConnection.getBasketballPlayerMapper();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void TestAddBasketballPlayer(){
System.out.println("插入球員前:");
TestSelectAllasketballPlayer();
BasketballPlayer player = new BasketballPlayer("Kawhi Leonard",28,104.3,200,2,"TRO","F");
basketballPlayerMapper.addBasketballPlayer(player);
sqlSession.commit();
System.out.println("插入球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestDeleteBasketballPlayerById(){
System.out.println("删除球員前:");
TestSelectAllasketballPlayer();
basketballPlayerMapper.deleteBasketballPlayerById(5);
sqlSession.commit();
System.out.println("删除球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestUpdateBasketballPlayerById(){
System.out.println("修改球員前:");
TestSelectAllasketballPlayer();
BasketballPlayer player = new BasketballPlayer();
BasketballPlayer basketballPlayer = null;
player.setId(3);
player.setHeight(213);
basketballPlayerMapper.updateBasketballPlayerById(player);
sqlSession.commit();
System.out.println("修改球員後:");
TestSelectAllasketballPlayer();
}
@Test
public void TestSelectBasketballPlayerById(){
BasketballPlayer player = basketballPlayerMapper.selectBasketballPlayerById(3);
System.out.println(player);
}
@Test
public void TestSelectAllasketballPlayer(){
List<BasketballPlayer> players = basketballPlayerMapper.selectAllasketballPlayer();
for (BasketballPlayer player : players){
System.out.println(player);
}
}
@Test
public void TestSelectBasketballPlayerByConditionByMap(){
Map<String,Object> map = new HashMap<>();
map.put("nameCondition","en");
map.put("ageCondition",29);
List<BasketballPlayer> players = basketballPlayerMapper.selectBasketballPlayerByConditionByMap(map);
for (BasketballPlayer player : players){
System.out.println(player);
}
}
@Test
public void TestSelectBasketballPlayerByConditionByIndex(){
List<BasketballPlayer> players = basketballPlayerMapper.selectBasketballPlayerByConditionByIndex("en",29);
for (BasketballPlayer player : players){
System.out.println(player);
}
}
}
執行測試方法TestSelectBasketballPlayerByConditionByMap後,在控制台列印輸出如下日志資訊,說明測試通過。
執行測試方法TestSelectBasketballPlayerByConditionByIndex後,在控制台列印輸出如下日志資訊,說明測試通過。