- MyBatis 的真正強大在于它的語句映射,這是它的魔力所在。由于它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進行對比,你會立即發(fā)現(xiàn)省掉了將近 95% 的代碼。MyBatis 致力于減少使用成本,讓用戶能更專注于 SQL 代碼
- SQL 映射文件只有很少的幾個頂級元素(按照應被定義的順序列出):
cache – 該命名空間的緩存配置。
cache-ref – 引用其它命名空間的緩存配置。
resultMap – 描述如何從數(shù)據(jù)庫結果集中加載對象,是最復雜也是最強大的元素。
sql – 可被其它語句引用的可重用語句塊。
insert – 映射插入語句。
update – 映射更新語句。
delete – 映射刪除語句。
select – 映射查詢語句。
一、select 元素的屬性
| 屬性 | 描述 |
|---|---|
| id | 在命名空間中唯一的標識符,可以用來被引用 |
| parameterType | 這條語句的參數(shù)的類全限定名或別名 |
| resultType | 期望從這條語句中返回結果的類全限定名或別名。 注意,如果返回的是集合,那應該設置為集合包含的類型,而不是集合本身的類型。 resultType 和 resultMap 之間只能同時使用一個。 |
| resultMap | 對外部 resultMap 的命名引用。 |
| flushCache | 將其設置為 true 后,只要語句被調(diào)用,都會導致本地緩存和二級緩存被清空,默認值:false |
| useCache | 將其設置為 true 后,將會導致本條語句的結果被二級緩存緩存起來,默認值:對 select 元素為 true。 |
| timeout | 這個設置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結果的秒數(shù)。 |
| fetchSize | 這是一個給驅(qū)動的建議值,嘗試讓驅(qū)動程序每次批量返回的結果行數(shù)等于這個設置值 |
| statementType | 可選 STATEMENT,PREPARED 或 CALLABLE。 |
| resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等價于 unset) 中的一個, |
| databaseId | 如果配置了數(shù)據(jù)庫廠商標識(databaseIdProvider),MyBatis 會加載所有不帶 databaseId 或匹配當前 databaseId 的語句;如果帶和不帶的語句都有,則不帶的會被忽略。 |
| resultOrdered | 這個設置僅針對嵌套結果 select 語句:如果為 true,將會假設包含了嵌套結果集或是分組,當返回一個主結果行時,就不會產(chǎn)生對前面結果集的引用。 這就使得在獲取嵌套結果集的時候不至于內(nèi)存不夠用。默認值:false。 |
| resultSets | 這個設置僅適用于多結果集的情況。 |
二、Insert, Update, Delete 元素的屬性
僅適用于insert和update的
| 屬性 | 描述 |
|---|---|
| useGeneratedKeys | MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數(shù)據(jù)庫內(nèi)部生成的主鍵 ,默認值:false。 |
| keyProperty | 指定能夠唯一識別對象的屬性,MyBatis 會使用 getGeneratedKeys 的返回值或 insert 語句的 selectKey 子元素設置它的值,默認值:未設置(unset) |
| keyColumn | 設置生成鍵值在表中的列名,在某些數(shù)據(jù)庫(像 PostgreSQL)中,當主鍵列不是表中的第一列的時候,是必須設置的。 |
- 插入一個user,但不包含
User的id
<insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user (name, address)
values (#{name}, #{address});
</insert>
@Test
public void insertUserTest() {
try (SqlSession sqlSession = Utils.getSqlSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.insertUser(new User(null, "libai", "印度"));
if (res > 0)
System.out.println("插入成功");
else
System.out.println("插入失敗");
sqlSession.commit(); // 提交事務,必須!
}
}
三、插入多個元素(foreach)
int insertUsers(List<User> users);
<insert id="insertUsers" parameterType="list">
insert into user (name, address) values
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.address})
</foreach>
</insert>
collection="list",表示輸入?yún)?shù)類型是List的;item="item"是遍歷每個元素的別名;separator=","是下面句子的分隔符
@Test
public void insertUsersTest() {
try (SqlSession sqlSession = Utils.getSqlSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = new ArrayList<>();
users.add(new User(null, "李白", "中國"));
users.add(new User(null, "王維", "中國"));
users.add(new User(null, "蘇軾", "中國"));
int res = mapper.insertUsers(users);
if (res > 0)
System.out.println("插入成功");
else
System.out.println("插入失敗");
sqlSession.commit(); // 提交事務,必須!
}
}
四、重用sql代碼片段
- 這個元素可以用來定義可重用的 SQL 代碼片段,以便在其它語句中使用
- 參數(shù)可以靜態(tài)地(在加載的時候)確定下來,并且可以在不同的 include 元素中定義不同的參數(shù)值
<sql id="allParam">
select *
from student
</sql>
<select id="getStudentById" resultType="student" parameterType="_int">
<include refid="allParam"/>
where id = ${id};
</select>
<select id="getStudentByName" parameterType="string" resultType="student">
<include refid="allParam"/>
where name = ${name};
</select>
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
五、參數(shù)
-
#{}和${}的區(qū)別-
#{}為參數(shù)占位符?,即sql 預編譯;${}為字符串替換,即 sql 拼接 -
#{}安全,${}容易發(fā)生SQL注入問題。
-
- 將字段名也作為參數(shù)輸入
User selectUserByParameter(Map<String, String> map);
<select id="selectUserByParameter" parameterType="map" resultType="user">
select id, name, address
from user
where ${type} = #{content};
</select>
@Test
public void selectUserByParameterTest() {
try (SqlSession sqlSession = Utils.getSqlSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, String> map = new HashMap<>();
//map.put("type", "name");
//map.put("content", "杜甫");
map.put("type", "id");
map.put("content", "3");
User user = mapper.selectUserByParameter(map);
System.out.println(user);
}
}
這種方法,玩玩就好,不安全。同樣的,會SQL注入。
六、結果映射(resultMap)
- 當出現(xiàn)Java Bean和表的字段名不一樣的情況時,有兩種解決方法:取別名、使用結果映射
- Java Bean
// 利用lombok直接生成那些固定的方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
int id;
String lastName;
String firstName;
}
- 數(shù)據(jù)庫的表
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(20) NOT NULL,
first_name VARCHAR(20) NOT NULL
)ENGINE=INNODB ;
- 取別名
select id, last_name AS lastName, first_name AS firstName
from person;
- 結果映射
<!--修改成對應的接口-->
<mapper namespace="com.du.mybatis.dao.PersonMapper">
<!-- id為該resultMap的唯一標識,用于后續(xù)引用 -->
<!-- type為指定類,可以使用別名 -->
<resultMap id="personResultMap" type="person">
<id property="id" column="id"/>
<result property="lastName" column="last_name"/>
<result property="firstName" column="first_name"/>
</resultMap>
<!-- resultMap用來選擇指定的結果映射 -->
<select id="getPersons" resultMap="personResultMap">
select id, last_name, first_name
from person;
</select>
</mapper>
- 結果映射的各個屬性
| 屬性 | 描述 |
|---|---|
| constructor | 用于在實例化類時,注入結果到構造方法中 |
| id | 一個 ID 結果;標記出作為 ID 的結果可以幫助提高整體性能 |
| result | 注入到字段或 JavaBean 屬性的普通結果 |
| association | 一個復雜類型的關聯(lián);許多結果將包裝成這種類型 |
| collection | 一個復雜類型的集合 |
| discriminator | 使用結果值來決定使用哪個 resultMap |
| case | 基于某些值的結果映射 |
-
id&result- id 和 result 元素都將一個列的值映射到一個簡單數(shù)據(jù)類型(String, int, double, Date 等)的屬性或字段。
- 這兩者之間的唯一不同是,id 元素對應的屬性會被標記為對象的標識符,在比較對象實例時使用。 這樣可以提高整體的性能,尤其是進行緩存和嵌套結果映射(也就是連接映射)的時候
七、關聯(lián)
7.1 嵌套select查詢
- 嵌套 Select 查詢:通過執(zhí)行另外一個 SQL 映射語句來加載期望的復雜類型(簡單來說,就是兩個select語句嵌套)
@Data
public class Teacher {
private String name;
private int id;
}
@Data
public class Student {
private int id;
private Teacher teacher;
private String name;
}
而database中只存放了老師的id,并沒有存放老師。因此需要通過id查詢老師,并返回給Student
<!-- 根據(jù)id查找一個老師 -->
<select id="getTeacher" resultType="teacher" parameterType="_int">
select *
from teacher
where id = #{id};
</select>
首先通過一個簡單的select語句,通過id查詢出指定的老師
<!--getStudents的resultMap-->
<resultMap id="studentResultMap" type="student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- column作為輸入,到getTeacher中查找 -->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
然后在
resultMap中,通過association進行select關聯(lián)。property為java bean的屬性名,column為數(shù)據(jù)庫表中的列名稱,javaType為該關聯(lián)映射的java bean,select為用于查詢該java bean的接口
<select id="getStudents" resultMap="studentResultMap">
select *
from student;
</select>
最后通過一個
resultMap作為參數(shù)即可
7.2 嵌套結果映射
- 嵌套結果映射:使用嵌套的結果映射來處理連接結果的重復子集(其實就是通過兩個表聯(lián)結來處理)
<resultMap id="studentResultMap" type="student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" javaType="teacher">
<id property="id" column="id"/>
<result property="name" column="name"/>
</association>
</resultMap>
通過結果映射,結果輸出一個
Student類。在里面通過association,嵌套查詢Teacher
<select id="getStudents" resultMap="studentResultMap">
select *
from student, teacher
where student.tid = teacher.id;
</select>
八、集合
- 集合,即一對多,一個java bean有一個屬性,由另一個java bean的集合組成。需要通過sql查詢出來?;舅悸放c上面相同,基本有兩種方法:嵌入子查詢、連表查詢
- java bean
@Data
public class Student {
private int id;
private String name;
private int tid; // 對應的Teacher id
}
@Data
public class Teacher {
private String name;
private int id;
private List<Student> students; // 集合
}
8.1 集合的嵌套 Select 查詢
<select id="selectStudentsByTId" resultType="Student" parameterType="_int">
select *
from student
where tid = #{tid};
</select>
<select id="selectTeacherById" resultMap="selectTeacherByIdResultMap" parameterType="_int">
select *
from teacher
where id = #{id};
</select>
<resultMap id="selectTeacherByIdResultMap" type="Teacher">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" select="selectStudentsByTId" javaType="ArrayList" ofType="student" column="id"/>
</resultMap>
- 基本思路:查出所有tid為tid的學生,再查出指定id的老師的信息,通過resultMap映射出結果。
-
resultMap就是用來解析sql查出來的結果的,一一對應的關系可直接不寫。對于集合來說,需要使用到collection屬性,property為java bean的屬性,column為sql中查出來的某一列的名字,javaType為該屬性的類型,ofType為該集合的泛型類型,select為該屬性的查詢語句。
8.2 集合的嵌套結果映射
<select id="selectTeacherById" resultMap="selectTeacherByIdResultMap" parameterType="_int">
select s.id sid, s.NAME sname, t.id t_id, t.name tname
from teacher t,
student s
where t.id = s.tid
and t.id = #{id};
</select>
<resultMap id="selectTeacherByIdResultMap" type="teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="t_id"/>
</collection>
</resultMap>
- 基本思想為:通過一個連表查詢出結果之后,對結果進行一個處理。一一對應的屬性可以忽略,不是一一對應的關系,就用一個
collection接收處理結果。collection里面也用result接收結果。