Mapper.xml對映檔案---Mybatis學習筆記(八)
Mapper.xml對映檔案中定義了操作資料庫的sql,每個sql是一個statement,對映檔案是mybatis的核心。
parameterType(輸入型別)
1.#
{}與$
{}
#
{}實現的是向prepareStatement中的預處理語句中設定引數值,sql語句中#{}表示一個佔位符即?。
<!-- 根據id查詢使用者資訊 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select >
使用佔位符#
{}可以有效防止sql注入,在使用時不需要關心引數值的型別,mybatis會自動進行java型別和jdbc型別的轉換。
#
{}可以接收簡單型別值或pojo屬性值,如果parameterType傳輸單個簡單型別值,#{}括號中可以是value或其它名稱。
$
{}和#
{}不同,通過$
{}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc型別轉換。
$
{}可以接收簡單型別值或pojo屬性值,如果parameterType傳輸單個簡單型別值,$
{}括號中只能是value。使用
<!-- 根據名稱模糊查詢使用者資訊 -->
<select id="selectUserByName" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
如果本例子使用#
{}則傳入的字串中必須有%號,而%是人為拼接在引數中,顯然有點麻煩,如果採用$
{}在sql中拼接為%的方式則在呼叫mapper介面傳遞引數就方便很多。
//如果使用佔位符號則必須人為在傳引數中加%
List<User> list = userMapper.selectUserByName("%管理員%" );
//如果使用${}原始符號則不用人為在引數中加%
List<User>list = userMapper.selectUserByName("管理員");
再比如order by排序,如果將列名通過引數傳入sql,根據傳的列名進行排序,應該寫為:
ORDER BY ${columnName}
如果使用#
{}將無法實現此功能。
2.傳遞簡單型別
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
3.傳遞pojo物件
Mybatis使用ognl表示式解析物件欄位的值,如下例子:
<!—傳遞pojo物件綜合查詢使用者資訊 -->
<select id="findUserByUser" parameterType="user" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
上邊sql語句中的id和username是user物件中的欄位名稱。
注意:parameterType中的user是事先在sqlMapConfig.xml檔案中配置了別名的,如下:
<typeAliases>
<!-- 針對單個別名的定義 -->
<typeAlias alias="user" type="com.huihui.pojo.User"/>
</typeAliases>
4.傳遞pojo包裝物件
需求:
開發中通過pojo傳遞查詢條件 ,查詢條件是綜合的查詢條件,不僅包括使用者查詢條件還包括其它的查詢條件(比如將使用者購買商品資訊也作為查詢條件),這時可以使用包裝物件傳遞輸入引數。
1.自定義包裝物件:
定義包裝物件將查詢條件(pojo)以類組合的方式包裝起來。
package com.huihui.pojo;
/**
* 使用者的包裝型別
* @author 62347
*
*/
public class UserQueryVo {
//使用者查詢條件
private User user;
//用到的其它類條件
//......
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
2.對映檔案:
<select id="findUserList" parameterType="com.huihui.pojo.UserQueryVo" resultType="user">
select * from user where username=#{user.username} and sex=#{user.sex}
</select>
說明:mybatis底層通過ognl從pojo中獲取屬性值:#
{user.username},user即是傳入的包裝物件的屬性。com.huihui.pojo.UserQueryVo是定義的包裝物件型別。
3.Mapper介面
public interface UserMapper {
public List<User> findUserList(UserQueryVo queryVo) throws Exception;
}
5.傳遞hashmap
對映檔案:
<!-- 傳遞hashmap綜合查詢使用者資訊 -->
<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
注意:
1.sql語句中的id和username是hashmap的key。
2.parameterType中的hashmap是別名
Mapper介面:
public interface UserMapper {
public User findUserByHashMap(Map map) throws Exception;
}
測試類:
@Test
public void testFindUserByHashMap() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id", 26);
map.put("username", "王五");
User user = userMapper.findUserByHashMap(map);
System.out.println(user);
}
1.resultType(輸出型別)
使用resultType進行輸出對映,只有查詢出來的列名和pojo中的屬性名一致,該列才可以對映成功。
如果查詢出來的列名和pojo中的屬性名全部不一致,那麼就不會建立pojo物件。
只要查詢出來的列名和pojo中的屬性有一個一致,就會建立pojo物件。
輸出簡單型別:
對映檔案:
<!-- 獲取使用者列表總數 -->
<select id="findUserCount" resultType="int">
select count(*) from user
</select>
Mapper介面:
public int findUserCount() throws Exception;
測試類:
Public void testFindUserCount() throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲取mapper介面例項
UserMapper userMapper = session.getMapper(UserMapper.class);
int count = userMapper.findUserCount();
//關閉session
session.close();
}
總結:
輸出簡單型別必須查詢出來的結果集有一條記錄,最終將第一個欄位的值轉換為輸出型別。
使用session的selectOne可查詢單條記錄。
輸出pojo物件:
對映檔案:
<!-- 根據id查詢使用者資訊 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
mapper介面:
public User findUserById(int id) throws Exception;
測試:
Public void testFindUserById() throws Exception {
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper介面例項
UserMapper userMapper = session.getMapper(UserMapper.class);
//通過mapper介面呼叫statement
User user = userMapper.findUserById(1);
System.out.println(user);
//關閉session
session.close();
}
這裡使用session呼叫selectOne查詢單條記錄。
輸出pojo列表:
對映檔案:
<!-- 根據名稱模糊查詢使用者資訊 -->
<select id="findUserByUsername" parameterType="string" resultType="user">
select * from user where username like '%${value}%'
</select>
mapper介面:
public List<User> findUserByUsername(String username) throws Exception;
測試:
Public void testFindUserByUsername()throws Exception{
//獲取session
SqlSession session = sqlSessionFactory.openSession();
//獲限mapper介面例項
UserMapper userMapper = session.getMapper(UserMapper.class);
//如果使用佔位符號則必須人為在傳引數中加%
//List<User> list = userMapper.selectUserByName("%管理員%");
//如果使用${}原始符號則不用人為在引數中加%
List<User> list = userMapper.findUserByUsername("管理員");
//關閉session
session.close();
}
這裡使用session的selectList方法獲取pojo列表。
輸出hashmap:
輸出pojo物件可以改用hashmap輸出型別,將輸出的欄位名稱作為map的key,value為欄位值。
resultType總結:
輸出pojo物件和輸出pojo列表在sql中定義的resultType是一樣的。
返回單個pojo物件要保證sql查詢出來的結果集為單條,內部使用session.selectOne方法呼叫,mapper介面使用pojo物件作為方法返回值。
返回pojo列表表示查詢出來的結果集可能為多條,內部使用session.selectList方法,mapper介面使用List物件作為方法返回值。
2.resultMap(輸出型別)
resultType可以指定pojo將查詢結果對映為pojo,但需要pojo的屬性名和sql查詢的列名一致方可對映成功。
如果sql查詢欄位名和pojo的屬性名不一致,可以通過resultMap將欄位名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果對映到pojo物件中。
resultMap可以實現將查詢結果對映為複雜型別的pojo,比如在查詢結果對映物件中包括pojo和list實現一對一查詢和一對多查詢。
1.對映檔案:
<!--將select id id_,username username_ from user查詢結果與User類中的屬性做一個對映關係 -->
<!--
type:resultMap最終對映的java物件型別
id:對resultMap的唯一標識
-->
<resultMap type="user" id="userResultMap">
<!--
id表示對查詢結果集中唯一列的標識
column:查詢出來的列名
property:上面type所指定的pojo中的屬性名
最終resultMap對column和property做一個對映關係(對應關係)
-->
<id column="id_" property="id"/>
<!--
result表示對查詢結果集中普通列的標識
column:查詢出來的列名
property:上面type所指定的pojo中的屬性名
最終resultMap對column和property做一個對映關係(對應關係)
-->
<result column="username_" property="username"/>
</resultMap>
<!--
resultMap中填寫上面的<resultMap>標籤的id
注意:如果這個resultMap在其它的<mapper>標籤中,前邊需要加<mapper>標籤的namespace,eg:<mapper>標籤的namespace+<resultMap>標籤的id
-->
<select id="findUserInfo" parameterType="int" resultMap="userResultMap">
select id id_,username username_ from user where id=#{value}
</select>
說明:
<id />
:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個欄位為複合唯一約束則定義多個<id />
。
Property:表示person類的屬性。
Column:表示sql查詢出來的欄位名。
Column和property放在一塊兒表示將sql查詢出來的欄位對映到指定的pojo類屬性上。
:普通結果,即pojo的屬性。
2.Mapper介面
public User findUserInfo(int id) throws Exception;
3.測試:
@Test
public void testFindUserInfo() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserInfo(24);
System.out.println(user);
sqlSession.close();
}
動態sql
通過mybatis提供的各種標籤方法實現動態拼接sql。
if
<!-- 傳遞pojo綜合查詢使用者資訊 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意要做不等於空字串校驗。
where
上邊的sql也可以改為:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
可以自動處理第一個and。
sql片段
Sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的,如下:
<!-- 傳遞pojo綜合查詢使用者資訊 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
將where條件抽取出來:
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
使用include引用:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:如果引用其它mapper.xml的sql片段,則在引用時需要加上namespace,如下:
<include refid="namespace.sql片段”/>
經驗:
- 一個sql片段中最好是基於一張表,這樣的話sql片段的可重用性才高。
- 在sql片段中不要包括where
foreach
向sql傳遞陣列或List,mybatis使用foreach解析,如下:
1. 通過pojo傳遞list
需求:
傳入多個id查詢使用者資訊,用下邊兩個sql實現:
SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%張%' id IN (10,89,16)
在pojo中定義list屬性ids儲存多個使用者id,並新增getter/setter方法
public class UserQueryIds {
//傳遞多個使用者id
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
對映檔案(mapper.xml):
<select id="findUserByIds" parameterType="com.huihui.pojo.UserQueryIds" resultType="user">
select * from user
<where>
<if test="ids!=null and ids.size>0">
<foreach collection="ids" item="id" open="and id in (" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
說明:
collection:指定輸入物件中集合屬性名(也就是上面pojo中定義的屬性名)
item:定義每次遍歷後生成的物件的物件名
open:開始遍歷時要拼接的字串
close:結束遍歷時要拼接的字串
separator:定義遍歷後產生的每個物件之間的字串
<foreach></foreach>
之間的內容:表示每次遍歷需要拼接的字串
mapper介面:
public List<User> findUserByIds(UserQueryIds ids) throws Exception;
測試:
@Test
public void testFindUserByIds() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserQueryIds ids = new UserQueryIds();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(10);
list.add(28);
ids.setIds(list);
List<User> users = userMapper.findUserByIds(ids);
System.out.println(users.size());
sqlSession.close();
}
2.傳遞單個List
傳遞List型別在編寫mapper.xml沒有區別,唯一不同的是隻有一個List引數時它的引數名為list。
對映檔案:
<select id="findUserBymIds" parameterType="java.util.List" resultType="user">
select * from user
<where>
<if test="list!=null">
<foreach collection="list" item="id" open="and id in(" close=")" separator="," >
#{id}
</foreach>
</if>
</where>
</select>
mapper介面:
public List<User> findUserBymIds(List<Integer> list) throws Exception;
測試:
@Test
public void testFindUserBymIds() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(10);
list.add(28);
List<User> users = userMapper.findUserBymIds(list);
System.out.println(users.size());
sqlSession.close();
}
3.傳遞單個數組(陣列中時pojo)
對映檔案:
<select id="findUserByArray" parameterType="Object[]" resultType="user">
select * from user
<where>
<if test="array!=null">
<foreach collection="array" index="index" item="item" open="and id in(" close=")" separator=",">
#{item.id}
</foreach>
</if>
</where>
</select>
說明:
sql只接收一個數組引數,這時sql解析引數的名稱mybatis固定為array,如果陣列是通過一個pojo傳遞到sql則引數的名稱為pojo中的屬性名。
index:為陣列的下標。
item:為遍歷後生成的陣列每個元素的名稱,名稱隨意定義
open:迴圈開始需要拼接的字串
close:迴圈結束需要拼接的字串
separator:定義遍歷後產生的每個物件之間的字串
mapper介面:
public List<User> findUserByArray(Object[] objs) throws Exception;
測試:
@Test
public void testFindUserByArray() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setId(1);
User user2 = new User();
user2.setId(10);
User user3 = new User();
user3.setId(28);
User[] users = {user1,user2,user3};
List<User> list = userMapper.findUserByArray(users);
System.out.println(list.size());
sqlSession.close();
}
4.傳遞單個數組(陣列中是字串型別)
對映檔案:
<select id="findUserByArray1" parameterType="Object[]" resultType="user">
select * from user
<where>
<if test="array!=null">
<foreach collection="array" index="index" item="item" open="and id in(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
</select>
mapper介面:
public List<User> findUserByArray1(Object[] objs) throws Exception;
測試:
@Test
public void testFindUserByArray1() throws Exception{
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
String[] ids = {"1","10","28"};
List<User> users = userMapper.findUserByArray1(ids);
System.out.println(users.size());
sqlSession.close();
}