1、select
我們基於這個持久層接口 GirlDao:
public interface GirlDao {
List<Girl> findByAge(int age);
Girl findById(long id);
int insertGirl(Girl girl);
int updateGirl(Girl girl);
int deleteGirl(long id);
}
12
1
public interface GirlDao {
2
3
List<Girl> findByAge(int age);
4
5
Girl findById(long id);
6
7
int insertGirl(Girl girl);
8
9
int updateGirl(Girl girl);
10
11
int deleteGirl(long id);
12
}
關於select,我們還是先使用最先提到的簡單的mapper的一個例子:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
<select id="findById" parameterType="long" resultType="dulk.learn.mybatis.pojo.Girl">
SELECT * FROM girl WHERE id = #{id}
</select>
</mapper>
5
1
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
2
<select id="findById" parameterType="long" resultType="dulk.learn.mybatis.pojo.Girl">
3
SELECT * FROM girl WHERE id = #{id}
4
</select>
5
</mapper>
這里的 <select> 標簽,即代表的是 select 操作,你應該可以聯想到,我們當然還有 <insert>、<update>、<delete> 標簽。縱觀這段內容,需要提醒你知道的是:
- select 標簽的 id 屬性用來標記和區別該 select,如果需要對應接口則 id 和接口方法名要一致
- parameterType 表示輸入參數的類型
- resultType 表示輸出結果的類型
- #{ } 表示傳入的動態參數的占位符
簡單來說就是,這個語句叫 findById,接受long(或Long)類型參數(#{id} 則說明參數名為id),返回Girl類型的對象。其實這也相當於告訴MyBatis創建一個預處理語句對象,並傳入相應的值,所以以上又相當於JDBC的如下操作:
String findById = "SELECT * FROM girl WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(findById);
ps.setLong(1, id);
3
1
String findById = "SELECT * FROM girl WHERE id = ?";
2
PreparedStatement ps = conn.prepareStatement(findById);
3
ps.setLong(1, id);
1.1 輸入參數 parameterType
在如上的示例中我們已經看到,輸入參數的屬性為 parameterType,其值是將會傳入這條語句的參數類的 “
完全限定名” 或 “
別名”(還記得別名嗎?參見mybatis-config.xml全局配置文件說明中的typeAliases)。
另外可喜的是,這個屬性其實是可選的,因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的參數,所以這個屬性的默認值是 unset,當然,往往我們在開發的時候還是主動進行了限定,因為很多時候我們傳入的將會是一個封裝了參數的類。
上面的含義是說,假如你傳入的是一個自定義的對象,那么占位符的屬性會在該對象屬性中進行查找並賦值:
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
4
1
<insert id="insertUser" parameterType="User">
2
insert into users (id, username, password)
3
values (#{id}, #{username}, #{password})
4
</insert>
如上若 User 類型的參數對象傳遞到了語句中,id、username 和 password 屬性將會被查找,然后將它們的值傳入預處理語句的參數中。
1.2 輸出結果 resultType / resultMap
輸出結果有兩種形式來進行限定,即你要么使用 resultType,要么使用 resultMap,兩者不能同時出現在相同的語句標簽中:
- resultType - 返回的期望類型的類的完全限定名或別名(注:若是集合情形,那應該是集合可包含的類型,而不能是集合本身)
- resultMap - 外部 resultMap 的命名引用
resultType很好理解,其實就和parameterType的使用性質是一樣的。那么來簡單說明一下這里的resultMap和其外部命名引用是什么意思。
其實在select標簽之外,有一個同級的標簽,也叫 resultMap(注意我們剛才提到的resultMap是作為select標簽的屬性出現的),該標簽主要針對一些復雜的結果映射,用來 “
描述語句和對象之間的映射關系”,你可以理解為我們手動地匹配查詢出的列名和對象的屬性之間的對應關系(注意是“查詢出的列名”而非數據庫原本的列名,這意味着你語句中是否有使用“AS”關鍵字會相應對該映射關系的描述造成影響)。
還是之前的mapper的例子,用resultMap來配置的話,就應該是如下面貌:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
<!--定義resultMap-->
<resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
<!--id表查詢結果中的唯一標識-->
<id property="id" column="id" />
<!--result表示對普通列名的映射,propertyi表對象屬性名,column表查詢出的列名-->
<result property="age" column="age" />
</resultMap>
<!--引用外部resultMap,即girlResultMap-->
<select id="findById" parameterType="long" resultMap="girlResultMap">
SELECT * FROM girl WHERE id = #{id}
</select>
</mapper>
14
1
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
2
<!--定義resultMap-->
3
<resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
4
<!--id表查詢結果中的唯一標識-->
5
<id property="id" column="id" />
6
<!--result表示對普通列名的映射,propertyi表對象屬性名,column表查詢出的列名-->
7
<result property="age" column="age" />
8
</resultMap>
9
10
<!--引用外部resultMap,即girlResultMap-->
11
<select id="findById" parameterType="long" resultMap="girlResultMap">
12
SELECT * FROM girl WHERE id = #{id}
13
</select>
14
</mapper>
所以,如果你是使用ResultType作為輸出映射,只有查詢出來的列名和對象屬性名一致才可映射成功。否則,你就需要定義一個resultMap來描述兩者之間的關系。
另外值得一提的是,
對於生成的結果是單個還是集合,往往是根據對應接口的返回值類型來確定的,假如 GirlDao 中 List<Girl> findByAge(int age),則返回集合(MyBatis內部調用selectList方法);而諸如 Girl findById(long id) 返回單個對象調用(MyBatis內部調用selectOne方法),所以哪怕你知道你sql會返回的是多個對象,你在 resultType 中也只需要定義的是單個元素的類型,而不是集合。
2、insert, update 和 delete
我們已經知道了<select>,舉一反三來看,其實 <insert>、<update>、<delete> 也就不難使用了,如下例:
<!--使用了useGeneratedKeys和keyProperty來將生成的主鍵設置到對象屬性中-->
<insert id="insertGirl" parameterType="dulk.learn.mybatis.pojo.Girl" useGeneratedKeys="true" keyProperty="id">
INSERT INTO girl (age)
VALUES (#{age})
</insert>
<update id="updateGirl" parameterType="dulk.learn.mybatis.pojo.Girl">
UPDATE girl
SET age = #{age}
WHERE id = #{id}
</update>
<delete id="deleteGirl" parameterType="long">
DELETE FROM girl
WHERE id = #{id}
</delete>
16
1
<!--使用了useGeneratedKeys和keyProperty來將生成的主鍵設置到對象屬性中-->
2
<insert id="insertGirl" parameterType="dulk.learn.mybatis.pojo.Girl" useGeneratedKeys="true" keyProperty="id">
3
INSERT INTO girl (age)
4
VALUES (#{age})
5
</insert>
6
7
<update id="updateGirl" parameterType="dulk.learn.mybatis.pojo.Girl">
8
UPDATE girl
9
SET age = #{age}
10
WHERE id = #{id}
11
</update>
12
13
<delete id="deleteGirl" parameterType="long">
14
DELETE FROM girl
15
WHERE id = #{id}
16
</delete>
只是需要注意的是:
- insert / update / delete 都沒有 resultType 或 resultMap,他們的返回值是受影響的行數
- 若數據庫支持自動生成主鍵,可設置 useGeneratedKeys 為 true,並使用 keyProperty 將生成的主鍵值設置到目標屬性上(如上例的insert)
做了個單元測試如下:
public class Test {
@org.junit.Test
public void testMyBatis() throws IOException {
//讀取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//獲取工廠類
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//獲取SqlSession數據庫會話對象
SqlSession sqlSession = factory.openSession();
//獲取Dao
GirlDao girlDao = sqlSession.getMapper(GirlDao.class);
Girl girl = new Girl();
girl.setAge(18);
//insert a girl with age 18
int resultInsert = girlDao.insertGirl(girl);
Assert.assertEquals(1, resultInsert);
Assert.assertEquals(18, girlDao.findById(girl.getId()).getAge());
//update girl's age from 18 to 20
girl.setAge(20);
int resultUpdate = girlDao.updateGirl(girl);
Assert.assertEquals(1, resultUpdate);
Assert.assertEquals(20, girlDao.findById(girl.getId()).getAge());
//delete girl
int resultDelete = girlDao.deleteGirl(girl.getId());
Assert.assertEquals(1, resultDelete);
Assert.assertNull(girlDao.findById(girl.getId()));
}
}
34
1
public class Test {
2
3
junit.Test .
4
public void testMyBatis() throws IOException {
5
//讀取配置文件
6
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
7
//獲取工廠類
8
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
9
//獲取SqlSession數據庫會話對象
10
SqlSession sqlSession = factory.openSession();
11
//獲取Dao
12
GirlDao girlDao = sqlSession.getMapper(GirlDao.class);
13
14
Girl girl = new Girl();
15
girl.setAge(18);
16
17
//insert a girl with age 18
18
int resultInsert = girlDao.insertGirl(girl);
19
Assert.assertEquals(1, resultInsert);
20
Assert.assertEquals(18, girlDao.findById(girl.getId()).getAge());
21
22
//update girl's age from 18 to 20
23
girl.setAge(20);
24
int resultUpdate = girlDao.updateGirl(girl);
25
Assert.assertEquals(1, resultUpdate);
26
Assert.assertEquals(20, girlDao.findById(girl.getId()).getAge());
27
28
//delete girl
29
int resultDelete = girlDao.deleteGirl(girl.getId());
30
Assert.assertEquals(1, resultDelete);
31
Assert.assertNull(girlDao.findById(girl.getId()));
32
}
33
34
}
3、sql
sql元素用來定義可重用的SQL代碼片段,其他語句可以用過
<include> 標簽來將之包含其中。看個例子就能明白了:
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
<resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
<id property="id" column="id" />
<result property="age" column="age" />
<result property="cupSize" column="cup_size" />
</resultMap>
<!--定義可重用的sql片段-->
<sql id="girlColumn">
id, age
</sql>
<select id="findById" parameterType="long" resultMap="girlResultMap">
<!--通過include引用外部sql片段-->
SELECT <include refid="girlColumn" />
FROM girl WHERE id = #{id}
</select>
</mapper>
20
1
<mapper namespace="dulk.learn.mybatis.dao.GirlDao">
2
3
<resultMap id="girlResultMap" type="dulk.learn.mybatis.pojo.Girl">
4
<id property="id" column="id" />
5
<result property="age" column="age" />
6
<result property="cupSize" column="cup_size" />
7
</resultMap>
8
9
<!--定義可重用的sql片段-->
10
<sql id="girlColumn">
11
id, age
12
</sql>
13
14
<select id="findById" parameterType="long" resultMap="girlResultMap">
15
<!--通過include引用外部sql片段-->
16
SELECT <include refid="girlColumn" />
17
FROM girl WHERE id = #{id}
18
</select>
19
20
</mapper>