MyBatis之XML映射器

  • 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 元素的屬性

僅適用于insertupdate

屬性 描述
useGeneratedKeys MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數(shù)據(jù)庫內(nèi)部生成的主鍵 ,默認值:false。
keyProperty 指定能夠唯一識別對象的屬性,MyBatis 會使用 getGeneratedKeys 的返回值或 insert 語句的 selectKey 子元素設置它的值,默認值:未設置(unset)
keyColumn 設置生成鍵值在表中的列名,在某些數(shù)據(jù)庫(像 PostgreSQL)中,當主鍵列不是表中的第一列的時候,是必須設置的。
  • 插入一個user,但不包含Userid
<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接收結果。
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容