Mybatis的接口函數能不能進行重載?
mybatis版本:3.4x
java版本:java 8
語法層面:
1、接口的方法可以進行重載,因爲 java 語法可以讓接口函數進行重載。
Mybatis框架方面:
1、結論:可以有條件的進行重載。
2、爲什麼會有這個問題?:mybatis裏面將接口裏面的方法名稱和配置文件裏面的id屬性進行唯一配對,在同一個命名空間下只能有一個id,那麼所有函數名稱相同的重載函數都會被綁定到一個id上,所以,如果要實現函數的重載,必須讓一個SQL語句去適應多個函數的參數,如果是單純的重載是肯定不行的(重載函數的定義就是參數相關),但是得益於mybatis的多種傳參方式和隱性的分頁功能,可以在接口裏面進行函數重載,但是還是需要將所有的重載函數適配到同一個id的SQL上面去,仍然有很大的侷限性,並不是可以隨意的進行重載。
測試:
1、數據庫環境
DROP TABLE IF EXISTS sys_user;
CREATE TABLE sys_user(
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '用戶的ID',
user_name VARCHAR(50) COMMENT '用戶名稱',
user_password VARCHAR(50) COMMENT '密碼',
user_email VARCHAR(50) COMMENT '郵箱',
user_info TEXT COMMENT '簡介',
head_img BLOB COMMENT '頭像',
create_time DATETIME COMMENT '創建時間',
PRIMARY KEY (id)
)DEFAULT CHARSET utf8;
ALTER TABLE sys_user COMMENT '用戶表';
INSERT INTO sys_user VALUES ('1','admin','123456','[email protected]','管理員',null,'2016-04-01 17:00:58');
INSERT INTO sys_user VALUES ('1001','test','123456','[email protected]','測試用戶',null,'2016-04-01 17:01:52');
2、實體類
public class SysUser {
private Long id;
private String userName;
private String userPassword;
private String userEmail;
private String userInfo;
private byte[] headImg;
private Date createTime;
public SysUser(){
}
/**setter and getter*/
}
3、測試
3.1、測試模板代碼
public class SysUserDaoTest {
private static SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@BeforeClass
public static void beforeClass(){
String resource = "plus/mybatis-config.xml";
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e){
e.printStackTrace();
}
}
@Before
public void before(){
//事物手動提交
sqlSession = sqlSessionFactory.openSession(false);
}
@After
public void after(){
//回滾事物
sqlSession.rollback(true);
//關閉會話
sqlSession.close();
}
}
3.2、測試場景一
重載實現分頁、參數類型轉換實現重載。
3.2.1、接口
public interface SysUserDao {
SysUser selectById(Long id);
List<SysUser> selectById(Long id,RowBounds rowBound);
}
3.2.2、mapper配置文件
<!--
id:注意事項:接口方法不能進行重載,在3.4版本後是錯的!!!!!,因爲至少可以分頁!!!
1、接口中的所有重載方法對應XML裏面的同一個ID,但是3.4之後有一個分頁的插件,所以至少可以接受一個org.apache.ibatis.session.RowBounds的隱性參數。
2、雖然接口裏面的方法可以進行重載,但是需要一個前提:不要指定 parameterType 屬性!!
接口方法如下:
SysUser selectById(Long id);
List<SysUser> selectById(Long id,RowBounds rowBound);
SysUser selectById(String name);
測試代碼如下:
SysUserDao sysUserDao = sqlSession.getMapper(SysUserDao.class);
SysUser sysUser = sysUserDao.selectById(1L);
Assert.assertEquals("admin",sysUser.getUserName());
SysUser sysUser1 = sysUserDao.selectById("1");
Assert.assertEquals("admin",sysUser1.getUserName());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(String)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
RowBounds rowBound = new RowBounds(0,2);
List<SysUser> sysUsers = sysUserDao.selectById(1L,rowBound);
Assert.assertEquals(1,sysUsers.size());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
事實證明,當不指定參數類型的時候(也僅僅當不指定參數類型的時候纔可以,因爲重載重載的是參數),是可以進行
接口方法重載的,因爲mybatis的參數類型推斷會自動的轉換參數類型,比如:
sysUserDao.selectById("楚雲飛");
因爲ID的類型是Long類型,所以數據類型的自動轉換失敗,但是是不會出現異常的,只是它選取出來的數據集爲null,因爲ID的類型是bigint,沒有bigint值爲楚雲飛,所以爲null結果集。
-->
<select id="selectById" resultMap="sysUser" flushCache="true">
SELECT * FROM sys_user WHERE id = #{id};
</select>
<resultMap id="sysUser" type="plus.pojo.SysUser">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPassword" column="user_password"/>
<result property="userEmail" column="user_email"/>
<result property="userInfo" column="user_info"/>
<result property="headImg" column="head_img" jdbcType="BLOB"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
3.3、測試場景二
參數傳遞方式實現重載,map參數傳遞和固定參數名的參數傳遞方式
3.3.1、接口
public interface SysUserDao {
SysUser selectByCondition(@Param("id")Long id,@Param("userName")String userName,@Param("userPassword")String userPassword);
SysUser selectByCondition(Map params);
}
3.3.2、mapper配置文件
<!--
固定參數傳遞:
函數接口:
SysUser selectByCondition(@Param("id")Long id,@Param("userName")String userName,@Param("userPassword")String userPassword);
SysUser selectByCondition(Map params);
測試函數:
@Test
public void selectByCondition(){
SysUserDao sysUserDao = sqlSession.getMapper(SysUserDao.class);
SysUser sysUser = sysUserDao.selectByCondition(1L,"admin","123456");
Assert.assertEquals("[email protected]",sysUser.getUserEmail());
/*
plus.dao.SysUserDao.selectByCondition - ==> Preparing: SELECT * FROM sys_user WHERE id = ? AND user_name = ? AND user_password = ?;
plus.dao.SysUserDao.selectByCondition - ==> Parameters: 1(Long), admin(String), 123456(String)
plus.dao.SysUserDao.selectByCondition - <== Total: 1
*/
Map<String,Object> params = new HashMap<String,Object>(3);
//傳入的鍵值必須和SQL語句裏面的以及重載函數的參數名稱相同纔可以實現正確的重載,否則將重載出錯。
params.put("id",1L);
params.put("userName","admin");
params.put("userPassword","123456");
SysUser sysUser1 = sysUserDao.selectByCondition(params);
/*
plus.dao.SysUserDao.selectByCondition - ==> Preparing: SELECT * FROM sys_user WHERE id = ? AND user_name = ? AND user_password = ?;
plus.dao.SysUserDao.selectByCondition - ==> Parameters: 1(Long), admin(String), 123456(String)
plus.dao.SysUserDao.selectByCondition - <== Total: 1
*/
Assert.assertEquals("[email protected]",sysUser1.getUserEmail());
}
-->
<select id="selectByCondition" resultMap="sysUser" flushCache="true">
SELECT * FROM sys_user WHERE id = #{id} AND user_name = #{userName} AND user_password = #{userPassword};
</select>
3.4、完整的測試代碼
package plus.dao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;
import org.junit.*;
import plus.pojo.SysUser;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SysUserDaoTest {
private static SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@BeforeClass
public static void beforeClass(){
String resource = "plus/mybatis-config.xml";
try{
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e){
e.printStackTrace();
}
}
@Before
public void before(){
//事物手動提交
sqlSession = sqlSessionFactory.openSession(false);
}
@After
public void after(){
//回滾事物
sqlSession.rollback(true);
//關閉會話
sqlSession.close();
}
@Test
public void selectById(){
SysUserDao sysUserDao = sqlSession.getMapper(SysUserDao.class);
SysUser sysUser = sysUserDao.selectById(1L);
Assert.assertEquals("admin",sysUser.getUserName());
SysUser sysUser1 = sysUserDao.selectById("1");
Assert.assertEquals("admin",sysUser1.getUserName());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(String)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
RowBounds rowBound = new RowBounds(0,2);
List<SysUser> sysUsers = sysUserDao.selectById(1L,rowBound);
Assert.assertEquals(1,sysUsers.size());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
}
@Test
public void selectByCondition(){
SysUserDao sysUserDao = sqlSession.getMapper(SysUserDao.class);
SysUser sysUser = sysUserDao.selectByCondition(1L,"admin","123456");
Assert.assertEquals("[email protected]",sysUser.getUserEmail());
/*
plus.dao.SysUserDao.selectByCondition - ==> Preparing: SELECT * FROM sys_user WHERE id = ? AND user_name = ? AND user_password = ?;
plus.dao.SysUserDao.selectByCondition - ==> Parameters: 1(Long), admin(String), 123456(String)
plus.dao.SysUserDao.selectByCondition - <== Total: 1
*/
Map<String,Object> params = new HashMap<String,Object>(3);
params.put("id",1L);
params.put("userName","admin");
params.put("userPassword","123456");
SysUser sysUser1 = sysUserDao.selectByCondition(params);
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
Assert.assertEquals("[email protected]",sysUser1.getUserEmail());
}
}
3.5、完整的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="plus.dao.SysUserDao">
<!--
id:當使用接口時,屬性值必須爲接口裏面的方法名稱,在同一個命名空間裏面不重複,否則將啓動報錯;
在任何情況下,id屬性不能出現英文句點 '.' (Caused by: org.apache.ibatis.builder.BuilderException: Dots are not allowed in element names, please remove it) ;
注意事項:接口方法不能進行重載,在3.4版本後是錯的!!!!!,因爲可以分頁!!!
1、接口中的所有重載方法對應XML裏面的同一個ID,但是3.4之後有一個分頁的插件,所以可以接受一個org.apache.ibatis.session.RowBounds的隱性參數。
2、雖然接口裏面的方法可以進行重載,但是如果要在運行的時候依然不報錯的話,需要一個前提:不要指定 parameterType 屬性!!
接口方法如下:
SysUser selectById(Long id);
List<SysUser> selectById(Long id,RowBounds rowBound);
SysUser selectById(String name);
測試代碼如下:
SysUserDao sysUserDao = sqlSession.getMapper(SysUserDao.class);
SysUser sysUser = sysUserDao.selectById(1L);
Assert.assertEquals("admin",sysUser.getUserName());
SysUser sysUser1 = sysUserDao.selectById("1");
Assert.assertEquals("admin",sysUser1.getUserName());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(String)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
RowBounds rowBound = new RowBounds(0,2);
List<SysUser> sysUsers = sysUserDao.selectById(1L,rowBound);
Assert.assertEquals(1,sysUsers.size());
/*
plus.dao.SysUserDao.selectById - ==> Preparing: SELECT * FROM sys_user WHERE id = ?;
plus.dao.SysUserDao.selectById - ==> Parameters: 1(Long)
plus.dao.SysUserDao.selectById - <== Total: 1
*/
事實證明,當不指定參數類型的時候(也僅僅當不指定參數類型的時候纔可以,因爲重載重載的是參數),是可以進行
接口方法重載的,因爲mybatis的參數類型推斷會自動的轉換參數類型,比如:
sysUserDao.selectById("楚雲飛");
因爲ID的類型是Long類型,所以數據類型的自動轉換失敗,但是是不會出現異常的,只是它選取出來的數據集爲null
-->
<select id="selectById" resultMap="sysUser" flushCache="true">
SELECT * FROM sys_user WHERE id = #{_parameter};
</select>
<!--
參數傳遞問題:
1、接口函數:
SysUser selectByCondition(Long id,String userName,String userPassword);
參數傳遞:
1、默認參數傳遞:默認的參數的命名爲 param + 序位,SQL語句如下:
SELECT * FROM sys_user WHERE id = #{param1} AND user_name = #{param2} AND user_password = #{param3};
2、默認參數傳遞:直接序列參數,SQL語句如下:
SELECT * FROM sys_user WHERE id = #{0} AND user_name = #{1} AND user_password = #{2};
3、固定參數傳遞:
函數接口:
SysUser selectByCondition(@Param("id")Long id,@Param("userName")String userName,@Param("userPassword")String userPassword);
SQL語句如下:
SELECT * FROM sys_user WHERE id = #{id} AND user_name = #{userName} AND user_password = #{userPassword};
4、使用Map傳遞參數:
函數接口:SysUser selectByCondition(Map params);
調用代碼段:
Map<String,Object> params = new HashMap<String,Object>(3);
params.put("id",1L);
params.put("userName","admin");
params.put("userPassword","123456");
SysUser sysUser1 = sysUserDao.selectByCondition(params);
Assert.assertEquals("[email protected]",sysUser1.getUserEmail());
SQL語句:
SELECT * FROM sys_user WHERE id = #{id} AND user_name = #{userName} AND user_password = #{userPassword};
思考:
1、與固定參數的SQL相比,沒有改變SQL語句,而且兩個接口實現重載。
2、接口函數可以實現重載,但是需要SQL語句來兼容這個函數的重載,也就是可以進行有條件的重載。
5、使用 $ 和 # 進行取值的區別:
1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,如果傳入的值是111,那麼解析成sql時的值爲order by "111", 如果傳入的值是id,則解析成的sql爲order by "id".
2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,如果傳入的值是111,那麼解析成sql時的值爲order by user_id, 如果傳入的值是id,則解析成的sql爲order by id.
3. #方式能夠很大程度防止sql注入。
4.$方式無法防止Sql注入。
5.$方式一般用於傳入數據庫對象,例如傳入表名.
6.一般能用#的就別用$.
SELECT * FROM sys_user WHERE id = ${id} AND user_name = '${userName}' AND user_password = '${userPassword}';
-->
<select id="selectByCondition" resultMap="sysUser" flushCache="true">
SELECT * FROM sys_user WHERE id = #{id} AND user_name = #{userName} AND user_password = #{userPassword};
</select>
<resultMap id="sysUser" type="plus.pojo.SysUser">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPassword" column="user_password"/>
<result property="userEmail" column="user_email"/>
<result property="userInfo" column="user_info"/>
<result property="headImg" column="head_img" jdbcType="BLOB"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>
</mapper>
3.6、完整的接口
public interface SysUserDao {
SysUser selectById(Long id);
List<SysUser> selectById(Long id,RowBounds rowBound);
SysUser selectById(String name);
SysUser selectByCondition(@Param("id")Long id,@Param("userName")String userName,@Param("userPassword")String userPassword);
SysUser selectByCondition(Map params);
}