Mybatis入門

這是學習顏群老師的mybatis教程后整理的筆記

Ⅰ台谊、mybatis介紹與使用

1.mybatis是什么

MyBatis可以簡化JDBC操作竣贪,實現(xiàn)數(shù)據(jù)的持久化

  • ORM的概念:Object Relational Mapping對象關系映射捡鱼,例如person表對應一個person對象

    Mybatis是ORM的一個實現(xiàn)几于。有了mybatis,開發(fā)人員像操作對象一樣操作數(shù)據(jù)庫表

2.怎么用mybatis

  • 用maven

    <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>x.x.x</version>
    </dependency>
    
  • 導入jar包,去官網(wǎng)下載(mybatis-x.x.x.jar)

3.第一次用mybatis操作數(shù)據(jù)庫

構建路徑/源路徑:java代碼在的路徑

  • conf.xml:配置數(shù)據(jù)庫信息和需要加載的映射文件,放在源路徑下

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!--運行的環(huán)境-->
       <environments default="development">
             <!--設置環(huán)境的id-->
           <environment id="development">
           <transactionManager type="JDBC"/> 
              <!--配置數(shù)據(jù)源類型-->
               <dataSource type="POOLED">
               <!-- 配置數(shù)據(jù)庫信息 -->
               <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mysqldemo?serverTimezone=UTC"/>
               <property name="username" value="root"/>
               <property name="password" value="123456"/>
           </dataSource>
           </environment>
       </environments>
       <mappers>
          <!-- 加載數(shù)據(jù)庫映射文件 -->
          <mapper resource="com/whl/mapper/personMapper.xml"/>
       </mappers>
    </configuration>
    
    • dataSource標簽的type屬性:配置數(shù)據(jù)源類型
      • UNPOOLED:傳統(tǒng)的JDBC模式(每次訪問數(shù)據(jù)庫顶伞、均需要打開、關閉數(shù)據(jù)庫連接)
      • POOLED:使用數(shù)據(jù)庫連接池
      • JNDI:從tomcat中獲取一個內(nèi)置的數(shù)據(jù)庫連接池(數(shù)據(jù)庫連接池--數(shù)據(jù)源)
    • environment標簽的id屬性:設置這個運行環(huán)境的id(測試剑梳、運行...)
    • environments標簽的default的屬性:填寫當前運行需要的環(huán)境id值唆貌,與environment標簽的id屬性相對應
    • mapper標簽的resource屬性:數(shù)據(jù)庫映射文件mapper.xml的相對路徑
    • property標簽:用于配置數(shù)據(jù)庫連接字符串、用戶名垢乙、密碼锨咙、驅(qū)動等信息
    • transactionManager標簽:用于設置事務方式
      • JDBC
      • MANAGER
  • personMapper.xml

    <?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="com.whl.entity.PersonMapper">
      <select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int">
          select * from person where id = #{id}
      </select>
      
      <insert id="addPerson" parameterType="com.whl.entity.Person">
          insert into person(id,name,age) values(#{id},#{name},#{age})
      </insert>
      
      <delete id="deletePersonById" parameterType="int">
          delete from person where id = #{id}
      </delete>
      
      <update id="updatePersonById" parameterType="com.whl.entity.Person">
          update person set name=#{name},age=#{age} where id=#{id}
      </update>
      
      <select id="queryAllPerson" resultType="com.whl.entity.Person">
          select * from person
      </select>
    </mapper>
    
    • mapper標簽的namespace屬性:放映射文件的路徑,不用xml后綴

    • SQL語句類型的標識符

      • select
      • insert
      • delete
      • update

      以上四種標簽中的id值來標識唯一一條SQL語句

  • 實體類Person

    package com.whl.entity;
    
    public class Person {
      private int id;
      private String name;
      private int age;
      
      public Person() {
    
      }
      public Person(int id, String name, int age) {
          this.id = id;
          this.name = name;
          this.age = age;
      }
      public int getId() {
          return id;
      }
      public void setId(int id) {
          this.id = id;
      }
      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
      public int getAge() {
          return age;
      }
      public void setAge(int age) {
          this.age = age;
      }
      @Override
      public String toString() {
          return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
      }
      
    }
    
  • mysql中創(chuàng)建person表如下

    -------------------------------------+
    | person | CREATE TABLE `person` (
      `id` int DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL,
      `age` int DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
    
  • 測試類

    package com.whl.entity;
    
    import java.io.IOException;
    import java.io.Reader;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    public class TestMybatis {
      public static void main(String[] args) throws IOException {
            //
          Reader reader=Resources.getResourceAsReader("conf.xml");
          SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
          //session -- connection
          SqlSession session = sqlSessionFactory.openSession();
            //通過id值來定位寫在mapper中的SQL語句
          String statement = "com.whl.entity.PersonMapper.queryPersonById";
            //查詢一條追逮,用selectOne酪刀,第一個參數(shù)是sql,第二個參數(shù)是sql中的?
          Person person = session.selectOne(statement,1);
          System.out.println(person);
            //關閉數(shù)據(jù)庫連接
          session.close();
      }
    }
    

Ⅱ钮孵、基礎方式和mapper動態(tài)代理方式

1.基礎方式

第一個mybatis程序使用的就是基礎方式

<select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int">
        select * from person where id = #{id}
 </select>
<select id="queryAllPerson" resultType="com.whl.entity.Person">
        select * from person
 </select>
  • 輸入?yún)?shù)parameterType和輸出參數(shù)resultType蓖宦,在形式上都只能有一個
  • 輸入?yún)?shù) :是簡單類型(8個基本類型+String)是可以使用任何占位符,#{xxxx};如果是對象類型油猫,則必須是對象的屬性#{屬性名}
  • 輸出參數(shù):如果返回值類型是一個 對象(如Student)稠茂,則無論返回一個、還是多個情妖,在resultType都寫成org.lanqiao.entity.Student睬关,即 resultType="org.lanqiao.entity.Student"
  • 如果使用的事務方式為jdbc,則需要手工commit提交,即session.commit();(增刪改需要提交)
  • 所有的標簽 <select> <update>等毡证,都必須有sql語句电爹,但是sql參數(shù)值可選

2.mapper動態(tài)代理方式

約定優(yōu)于配置

與基礎方式的不同點:

約定的目標:省略掉statement,即根據(jù)約定直接可以定位出SQL語句

接口:遵循以下規(guī)定

  1. 方法名和mapper.xml文件中標簽的id值相同

  2. 方法的輸入?yún)?shù)和mapper.xml文件中標簽的parameterType類型一致(如果mapper.xml的標簽中沒有 parameterType,則說明方法沒有輸入?yún)?shù))

  3. 方法的返回值和mapper.xml文件中標簽的 resultType類型一致(無論查詢結果是一個 還是多個(student料睛、List<Student>)丐箩,在mapper.xml標簽中的resultType中只寫 一個(Student);如果沒有resultType恤煞,則說明方法的返回值為void)

personMapper.xml

<?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">
<!--
與基礎方式的不同
namespace寫的是PersonMapper接口的全類名
-->
<mapper namespace="com.whl.mapper.PersonMapper">
    <select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int">
        select * from person where id = #{id}
    </select>
    
    <insert id="addPerson" parameterType="com.whl.entity.Person">
        insert into person(id,name,age) values(#{id},#{name},#{age})
    </insert>
    
    <delete id="deletePersonById" parameterType="int">
        delete from person where id = #{id}
    </delete>
    
    <update id="updatePersonById" parameterType="com.whl.entity.Person">
        update person set name=#{name},age=#{age} where id=#{id}
    </update>
    
    <select id="queryAllPerson" resultType="com.whl.entity.Person">
        select * from person
    </select>
</mapper>

PersonMapper接口

package com.whl.mapper;

import java.util.List;
import com.whl.entity.Person;

//操作mybatis的接口
public interface PersonMapper {
    Person queryPersonById(int id);
    List<Person> queryAllPerson();
    void addPerson(Person person);
    void deletePersonById(int id);
    void updatePersonById(Person person);
}

測試類

package com.whl.entity;


import java.io.IOException;
import java.io.Reader;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.javassist.expr.NewArray;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.whl.mapper.PersonMapper;

public class Test {

    public static void queryPersonById(int id) throws IOException {
        Reader reader=Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
        //session -- connection
        SqlSession session = sqlSessionFactory.openSession();
        PersonMapper personMapper=session.getMapper(PersonMapper.class);
        Person person=personMapper.queryPersonById(1);
        System.out.println(person);
        session.close();
    }
    
    public static void queryAllPerson() throws IOException {
        Reader reader=Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);

        SqlSession session = sqlSessionFactory.openSession();
        PersonMapper personMapper=session.getMapper(PersonMapper.class);
        List<Person> persons=personMapper.queryAllPerson();
        System.out.println(persons);
        session.close();
    }
    
    public static void addPerson(Person person) throws IOException {
        Reader reader=Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);   
        SqlSession session = sqlSessionFactory.openSession();
        PersonMapper personMapper=session.getMapper(PersonMapper.class);
        personMapper.addPerson(person);
        session.commit();
        System.out.println("增加成功");
        session.close();
    }
    
    public static void deletePersonById(int id) throws IOException {
        Reader reader=Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);   
        SqlSession session = sqlSessionFactory.openSession();
        PersonMapper personMapper=session.getMapper(PersonMapper.class);
        personMapper.deletePersonById(id);
        session.commit();
        System.out.println("刪除成功");
        session.close();
    }
    
    public static void updatePersonById(Person person) throws IOException {
        Reader reader=Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);   
        SqlSession session = sqlSessionFactory.openSession();
        PersonMapper personMapper=session.getMapper(PersonMapper.class);
        personMapper.updatePersonById(person);
        session.commit();
        System.out.println("修改成功");
        session.close();
    }
    public static void main(String[] args) throws IOException {
        //queryPersonById(1);
        //addPerson(new Person(3,"xxx",19));
        //updatePersonById(new Person(2,"whl",19));
        deletePersonById(3);
        queryAllPerson();       
    }
}

測試類中最重要的代碼

StudentMapper studentMapper = session.getMapper(StudentMapper.class) ;
studentMapper.方法();

通過session對象獲取接口(session.getMapper(接口.class);)屎勘,再調(diào)用該接口中的方法,程序會自動執(zhí)行該方法對應的SQL居扒。這樣就不需要statement和selectone了

匹配的過程:

  1. 根據(jù) 接口名 找到 mapper.xml文件(根據(jù)的是namespace=接口全類名)
  2. 根據(jù) 接口的方法名 找到 mapper.xml文件中的SQL標簽 (方法名=SQL標簽Id值)

以上2點可以保證: 當我們調(diào)用接口中的方法時概漱,程序能自動定位到某一個Mapper.xml文件中的sqL標簽

習慣:SQL映射文件(mapper.xml)和接口放在同一個包中(注意修改conf.xml中加載mapper.xml文件的路徑)

3.優(yōu)化

  • 可以將配置信息單獨放入db.properties文件中,然后再動態(tài)引入

    形式:kv對

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mysqldemo?serverTimezone=UTC
    username=root
    password=123456
    

    db.properties放在源路徑

    在conf.xml中用properties標簽引入

    <configuration>
        <properties resource="db.properties"/>
        ...
    </configuration>
    

    通過${key}來取值(類似于EL表達式)

    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    
  • MyBatis全局參數(shù)喜喂,在conf.xml中設置(一般不進行手動設置瓤摧,使用默認值)

    在setting標簽中進行設置

    <settings>
      <setting name="cacheEnabled" value="false"  />
      <setting name="lazyLoadingEnabled" value="false"  />
    </settings>
    
  • 別名(單個別名竿裂、批量別名),在conf.xml中的typeAliases標簽進行設置

    <typeAliases>
          <!-- 單個別名 (別名 忽略大小寫) -->
          <!-- <typeAlias type="org.lanqiao.entity.Student" alias="student"/> -->
          <!--  批量定義別名  (別名 忽略大小寫),以下會自動將該包中的所有類 批量定義別名: 別名就是類名(不帶包名照弥,忽略大小寫)   -->
          <package name="org.lanqiao.entity"/>
    </typeAliases>
    

    除了自定義別名外腻异,MyBatis還內(nèi)置了一些常見類的別名。

Ⅲ这揣、類型處理器

類型處理器:把Java代碼中的類型轉(zhuǎn)換成JDBC類型

1.自帶類型處理器

MyBatis自帶一些常見的類型處理器

image-20200819110702060.png

2.自定義MyBatis類型處理器

例:

實體類Student :  boolean   stuSex;
true:男
false:女
表student:   number  stuSex
1:男
0:女
  1. 創(chuàng)建轉(zhuǎn)換器

    • 直接實現(xiàn)TypeHandler接口

    • 或繼承BaseTypeHandler

      package org.lanqiao.converter;
      
      import java.sql.CallableStatement;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      
      import org.apache.ibatis.type.BaseTypeHandler;
      import org.apache.ibatis.type.JdbcType;
      
      //BaseTypeHandler<java類型>
      public class BooleanAndIntConverter extends BaseTypeHandler<Boolean>{
      
        //java(boolean)-DB(number)
        /*
         * ps:PreparedStatement對象
         * i:PreparedStatement對象操作參數(shù)的位置
         * parameter:java值
         * jdbcType:jdbc操作的數(shù)據(jù)庫類型
         */
        @Override
        public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
                throws SQLException {
                if(parameter) {
                    //1
                    ps.setInt(i, 1); 
                }else {
      //                0
                    ps.setInt(i, 0); 
                }
        }
      
        //db(number)->java(boolean)
        @Override
        public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException {
            int sexNum = rs.getInt(columnName) ;//rs.getInt("stuno") ;
      
            return sexNum == 1?true:false ;
        }
      
        @Override
        public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            int sexNum = rs.getInt(columnIndex) ;//rs.getInt(1)
            return sexNum == 1?true:false ;
        }
      
        @Override
        public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            int sexNum = cs.getInt(columnIndex) ;//rs.getInt(1)
            return sexNum == 1?true:false ;
        }
      }
      
  1. 配置conf.xml,通過typeHandler標簽配置類型轉(zhuǎn)換器

    <typeHandlers>
     <typeHandler handler="org.lanqiao.converter.BooleanAndIntConverter" javaType="Boolean" jdbcType="INTEGER" />
    </typeHandlers>
    
  2. 配置mapper.xml

    <select id="queryStudentByStunoWithConverter"    parameterType="int"     
     resultMap="studentResult" ><!--注意這里使用了resultMap而不是resultType-->
         select * from student where stuno = #{stuno}
    </select>
    <!--配置了一些表字段名到屬性名的映射-->  
    <resultMap type="student" id="studentResult">
         <!-- 分為主鍵id 和非主鍵 result-->
         <id property="stuNo"  column="stuno"  />
         <result property="stuName"  column="stuname" />
         <result property="stuAge"  column="stuage" />
         <result property="graName"  column="graname" />
         <result property="stuSex"  column="stusex"  javaType="boolean" jdbcType="INTEGER"/>
    </resultMap>
    

    何時使用resultMap悔常?

    • 如果類中屬性名和表中的字段名能夠合理識別(stuNo -stuno)則可以使用resultType;否則(id-stuno)使用resultMap
    • 如果類中屬性和表中的字段類型能夠合理識別(String-varchar2),則可以使用resultType;否則(boolean-number)使用resultMap

Ⅳ曾沈、輸入?yún)?shù)

1.簡單類型

  • {}这嚣、${}的區(qū)別

    1. {任意值}

      ${value} ,其中的標識符只能是value

    2. {}自動給String類型加上' '(自動類型轉(zhuǎn)換)

      ${}原樣輸出塞俱,但是適合于動態(tài)排序(動態(tài)字段)

      select stuno,stuname,stuage  from student where stuname = #{value}
      select stuno,stuname,stuage  from student where stuname = '${value}'
      動態(tài)排序:
      select stuno,stuname,stuage  from student  order by ${value} asc
      
    3. {}可以防止SQL注入姐帚,${}不防止

  • 相同之處:都可以獲取對象的值(嵌套類型對象)

2.模糊查詢獲取對象值

  • 方式一:用#{}
  select stuno,stuname,stuage  from student where stuage= #{stuAge}  or stuname like #{stuName} 
  Student student = new Student();
  student.setStuAge(24);
  student.setStuName("%w%");//在set時就加入了通配符
  List<Student> students = studentMapper.queryStudentBystuageOrstuName(student) ;//接口的方法->SQL
  • 方式二:用${}

    student.setStuName("w");
    
    select stuno,stuname,stuage  from student where stuage= #{stuAge}  or stuname like '%${stuName}%'
    

3.輸入對象為HashMap

在框架學習中HashMap一般是這樣聲明的(String,Object對)

Map<String,Object> map = new HashMap<>();
map.put("stuAge",19);
<select id="..." parameterType="HashMap" resultType="...">
    select ... where stuage= #{stuAge}<!--通過stuAge取到了map中的值作為參數(shù)-->
</select>

Ⅴ、輸出參數(shù)

輸出參數(shù)的四種情況:

  1. 簡單類型(8個基本+String)
  2. 輸出參數(shù)為實體對象類型
  3. 輸出參數(shù)為實體對象類型的集合 :雖然輸出類型為集合障涯,但是resultType依然寫 集合的元素類型(resyltType="Student")
  4. 輸出參數(shù)類型為HashMap
    --HashMap本身是一個集合罐旗,可以存放多個元素,
    但是根據(jù)提示發(fā)現(xiàn) 返回值為HashMap時 唯蝶,查詢的結果只能是1個學生(no,name)九秀;
    -->結論:一個HashMap 對應一個學生的多個元素(多個屬性) [一個map,一個學生]

1.resultMap

當實體類的屬性粘我、數(shù)據(jù)表的字段:類型鼓蜒、名字不同時,使用resultMap建立起字段名與屬性名的映射

<resultMap type="student" id="queryStudentByIdMap">
    <!-- 指定類中的屬性和表中的字段對應關系-->
    <id property="stuNo"  column="id" />
    <result property="stuName" column="name" />
</resultMap>
  • 用id標簽指定主鍵的映射
  • 用result標簽指定非主鍵的映射
  • property屬性的值為屬性名征字,column屬性的值為字段名

2.resultType+HashMap

通過hashmap把屬性名與字段名進行對應

  • 可用AS取別名的方式進行對應都弹,也可以在select語句字段名后空格給出屬性名
<!--注意一定要把返回類型改成hashmap-->
<select id="queryPersonByIdWithHashMap" parameterType="int" resultType="hashmap">
    select id as personId,name as personName,age as personAge from person where id=#{id}
 </select>
或
<select id="queryPersonByIdWithHashMap" parameterType="int" resultType="hashmap">
    select id "personId",name "personName",age "personAge" from person where id=#{id}
 </select>
<!--雙引號可加可不加-->
HashMap<String, Object> person=personMapper.queryPersonByIdWithHashMap(1);
System.out.println(person);
//{personName=lxl, personAge=19, personId=1}

注意:以hashmap作為返回類型,其中hashmap存儲的是某一個學生的多個屬性的鍵值對匙姜,而不是多個學生

如何返回多個學生畅厢?List<HashMap<String, Object>> persons

List<HashMap<String, Object>> persons = personMapper.queryPersonAllWithHashMap();
System.out.println(persons);
//[{name=lxl, id=1, age=19}, {name=whl, id=2, age=19}]

Ⅵ、動態(tài)SQL

1.動態(tài)生成where子句

<select id="queryPersonByNameAndAge" parameterType="person" resultMap="person_map">
    select id,name,age from person
    <where>
        <if test="personName!=null and personName!=''">
            and name = #{personName}
        </if>
        <if test="personAge!=null and personAge!=0">
            and age = #{personAge}
        </if>
    </where>        
 </select>
    
 <resultMap type="person" id="person_map">
     <id property="personId" column="id"/>
     <result property="personName" column="name"/>
     <result property="personAge" column="age"/>
 </resultMap>
  • where標簽:寫了后就不用在SQL中手寫where關鍵字了
  • if標簽
    • test屬性寫條件表達式氮昧,為true時執(zhí)行標簽內(nèi)的SQL
    • 注意:條件表達式中寫的是對屬性名的判斷框杜,不同條件用and、or連接
    • 第一個if標簽中的SQL是可以加and關鍵字的袖肥,因為mybatis會自動忽略這個and關鍵字

我踩的坑:怎么執(zhí)行查詢都是null咪辱,后來發(fā)現(xiàn)沒有配置resultMap。只要是字段和屬性名不一樣就必須配置昭伸!

2.輸入?yún)?shù)為集合或數(shù)組

輸入?yún)?shù)是list的情況

 <!--屬性名和字段的映射-->   
<resultMap type="person" id="person_map">
        <id property="personId" column="id"/>
        <result property="personName" column="name"/>
        <result property="personAge" column="age"/>
 </resultMap>

 <select id="queryPersonByNolist" parameterType="list" resultMap="person_map">
    select * from person
    <where>
        <if test="list!=null and list.size>0">
            <foreach collection="list" open=" and id in (" close=")" item="personNo" separator=",">
                #{personNo}
            </foreach>
        </if>
    </where>
 </select>
  • 所有集合在mapper中都用list來指代梧乘,并不使用集合本身的名稱
  • foreach標簽用于迭代集合
    • collection屬性:指定要遍歷的集合
    • open和close:只遍歷集合中元素,不遍歷集合前后的SQL語句庐杨。用這兩個屬性指定前后的SQL
    • item:給集合中每個元素取一個臨時的名稱选调。這個名稱是無所謂的,只要和標簽中#{}的一樣就行了
    • separator:分隔符灵份,不加就會變成"id in (123)"仁堪,需要的是"id in (1,2,3)"
  • 用#{}來取集合中元素的值
personNos.add(1);
personNos.add(2);
//personNos.add(3);
List<Person> persons=personMapper.queryPersonByNolist(personNos);;
System.out.println(persons);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19]]

輸入?yún)?shù)是int數(shù)組的情況:

 <select id="queryPersonByNoarray" parameterType="int[]" resultMap="person_map">
    select * from person
    <where>
        <if test="array!=null and array.length>0">
            <foreach collection="array" open=" and id in (" close=")" item="personNo" separator=",">
                #{personNo}
            </foreach>
        </if>
    </where>
</select>
  • 需要注意集合的大小是size,數(shù)組是length
  • 傳入的簡單類型的數(shù)組在mapper中一律以array表示
int[] personNos=new int[] {1,2,3};
List<Person> persons=personMapper.queryPersonByNoarray(personNos);;
System.out.println(persons);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19], Person [personId=3, personName=abc, personAge=19]]

輸入?yún)?shù)是對象數(shù)組的情況:

<select id="queryPersonByObjectarray" parameterType="Object[]" resultMap="person_map">
    select * from person
    <where>
        <if test="array!=null and array.length>0">
            <foreach collection="array" open=" and id in (" close=")" item="person" separator=",">
                #{person.personId}
            </foreach>
        </if>
    </where>
 </select>
  • 注意:所有類的對象數(shù)組的parameterType都是Object[]
  • 級聯(lián)獲取對象中的屬性值
Person p1=new Person();
Person p2=new Person();
Person p3=new Person();
p1.setPersonId(1);
p2.setPersonId(2);
p3.setPersonId(3);
Person[] persons1=new Person[] {p1,p2,p3};
List<Person> result=personMapper.queryPersonByObjectarray(persons1);;
System.out.println(result);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19], Person [personId=3, personName=abc, personAge=19]]

3.提取SQL片段

<sql id="sql1">
    select * from person
 </sql>

 <select id="queryPersonByObjectarray" parameterType="Object[]" resultMap="person_map">
    <include refid="sql1"></include>
    <where>
        <if test="array!=null and array.length>0">
            <foreach collection="array" open=" and id in (" close=")" item="person" separator=",">
                #{person.personId}
            </foreach>
        </if>
    </where>
 </select>
  • 用sql標簽提取出常用的SQL語句填渠,id標識唯一的SQL語句
  • 用include標簽中的refid屬性指定要使用的SQL語句的id

Ⅶ弦聂、關聯(lián)查詢

image-20200822120148468.png
image-20200822120206489.png

一對一:association
一對多:collection

1.一對一

方法一:

1.建一個類,繼承自屬性多的類氛什,再把多出來的屬性寫在這個新類里

package com.whl.entity;

public class PersonBusiness extends Person{
    private int cardId;
    private String cardInfo;
    
    public int getCardId() {
        return cardId;
    }
    public void setCardId(int cardId) {
        this.cardId = cardId;
    }
    public String getCardInfo() {
        return cardInfo;
    }
    public void setCardInfo(String cardInfo) {
        this.cardInfo = cardInfo;
    }
    @Override
    public String toString() {
        return super.toString()+"PersonBusiness [cardId=" + cardId + ", cardInfo=" + cardInfo + "]";
    }   
}
  • 注意tostring方法要重寫并調(diào)用父類的tostring莺葫!

2.設置person表的cardid為外鍵

3.寫SQL

<select id="queryPersonByIdOO" parameterType="int" resultType="PersonBusiness">
    select p.*,c.* from person as p inner join personcard as c
    on p.cardid=c.cardid
    where p.id=#{id}
 </select>
  • 注意返回類型為PersonBusiness(第三個類)

4.執(zhí)行

PersonBusiness personBusiness=personMapper.queryPersonByIdOO(1);
System.out.println(personBusiness);
//Person [id=1, name=lxl, age=19]PersonBusiness [cardId=1, cardInfo=lxl info...]

我踩的坑:第一次查詢結果只顯示了PersonBusiness增加的屬性,person中的顯示都為null或者0枪眉,最后發(fā)現(xiàn)是數(shù)據(jù)庫字段名與屬性名不一致捺檬。修改一致后就可正常顯示

方法二:寫一個person類和personcard類,在person類中有一個personcard對象

public class PersonCard {
    private int cardId;
    private String cardInfo;
}

public class Person {
    private int id;
    private String name;
    private int age;
    private PersonCard card;//把PersonCard類的對象作為Person類的屬性
}
<select id="queryPersonByIdOO" parameterType="int" resultMap="person_card_map">
    select p.*,c.* from person as p inner join personcard as c
    on p.cardid=c.cardid
    where p.id=#{id}
 </select>
    
 <resultMap type="person" id="person_card_map">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <association property="card" javaType="PersonCard" >
        <id property="cardId" column="cardid"/>
        <result property="cardInfo" column="cardinfo"/>
    </association>
 </resultMap>
  • resultMap中的type屬性為person贸铜,因為person包含了PersonCard
  • 一對一關聯(lián):用association標簽指定一對一
    • property屬性的值為Person類中PersonCard對象成員的名稱
    • javaType屬性指定了card對象的類名
    • id和result標簽指定了personcard表與personcard對象字段名堡纬、屬性名之間的映射關系
Person person=personMapper.queryPersonByIdOO(1);
System.out.println(person);
//Person [id=1, name=lxl, age=19, card=PersonCard [cardId=1, cardInfo=lxl info...]]

2.一對多

1.寫xml

<select id="queryPersonByClassId" parameterType="int" resultMap="person_class_map">
    select c.*,p.* from person as p
    inner join personclass c
    on c.classid=p.classid
    where c.classid=#{classid}
 </select>
    
 <resultMap type="PersonClass" id="person_class_map">
    <id property="classId" column="classid"/>
    <result property="className" column="classname"/>   
    <collection property="persons" ofType="Person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </collection>
 </resultMap>
  • collection標簽:指定一對多關聯(lián)查詢

2.寫屬性類

public class PersonClass {
    private int classId;
    private String className;
    List<Person> persons;
    ...
}

public class Person {
    private int id;
    private String name;
    private int age;
    private PersonCard card;
    ...
}
  • 班級和學生為一對多關系,所以在PersonClass中用List保存學生
  • card對象作為Person類的成員

3.測試運行

PersonClass personClass=personMapper.queryPersonByClassId(1);
System.out.println(personClass);
//PersonClass [classId=1, className=c1, persons=[Person [id=1, name=lxl, age=19, card=null]]]

Ⅷ蒿秦、日志

可以通過日志信息烤镐,相信的閱讀mybatis執(zhí)行情況(觀察mybatis實際執(zhí)行sql語句以及SQL中的參數(shù)和返回結果)

以log4j為例

  1. 引入log4j.jar

  2. 在conf.xml中開啟日志

    <settings>
     <!-- 開啟日志,并指定使用的具體日志 -->
     <setting name="logImpl" value="LOG4J"/> 
    </settings>
    

    如果不指定棍鳖,Mybatis就會根據(jù)以下順序 尋找日志
    SLF4J →Apache Commons Logging →Log4j 2 → Log4j →JDK logging

  3. 編寫配置日志輸出文件

    在src下創(chuàng)建log4j.properties

    log4j.rootLogger=DEBUG, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    

日志級別:DEBUG<INFO<WARN<ERROR

如果設置為info炮叶,則只顯示 info及以上級別的信息;
建議:在開發(fā)時設置debug渡处,在運行時設置為info或以上镜悉。

Ⅸ、延遲加載

也稱“懶加載”

什么是延遲加載:

在關聯(lián)查詢(一對一骂蓖、一對多积瞒、多對多)中,如果不采用延遲加載(立即加載)登下,查詢時會將一和多都查詢茫孔,班級、班級中的所有學生被芳。如果想要暫時只查詢1的一方缰贝,而多的一方先不查詢而是在需要的時候再去查詢-->延遲加載

  1. 開啟延遲加載conf.xml配置settings

    <settings>
     <!-- 開啟延遲加載 -->
     <setting name="lazyLoadingEnabled" value="true"/>
         
     <!-- 關閉立即加載 -->
     <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    
  2. 寫接口

    List<Person> queryPersonByIdOO();
    
  3. 寫SQL

    <select id="queryPersonByIdOO"  resultMap="person_card_map">
         select * from person
     </select>
         
     <resultMap type="person" id="person_card_map">
         <id property="id" column="id"/>
         <result property="name" column="name"/>
         <result property="age" column="age"/>
         <association property="card" javaType="com.whl.entity.PersonCard" select="com.whl.mapper.PersonCardMapper.queryCardById" column="cardid">
     </association>
     </resultMap>
    

    新建PersonCardMapper,注意要在conf.xml加載該映射!

    <mapper namespace="com.whl.mapper.PersonCardMapper">
         <select id="queryCardById" parameterType="int" resultType="com.whl.entity.PersonCard">
             select * from personcard where cardid = #{cardId}
         </select>
    </mapper>
    
    • association標簽表示一對一畔濒,collection表示一對多
      • property表示person類中的card屬性
      • javaType寫card的類名
      • select屬性寫PersonCardMapper中的sql的id剩晴。形式:namespace.id
      • column關聯(lián)的外鍵,作為queryCardById的輸入?yún)?shù)
    • queryCardById中的resultType屬性:寫card類的全名
  4. 測試運行

    List<Person> persons=personMapper.queryPersonByIdOO();
    System.out.println(persons);
    for(Person person:persons) {
     //System.out.println(person.getId()+"----"+person.getName());
     PersonCard personCard=person.getCard();
     System.out.println(person);
    }
    //Person [id=1, name=lxl, age=19, card=PersonCard [cardId=1, cardInfo=lxl info...]]
    //Person [id=2, name=whl, age=19, card=PersonCard [cardId=2, cardInfo=whl info...]]
    

Ⅹ、查詢緩存

一級緩存 :同一個SqlSession對象

MyBatis默認開啟一級緩存赞弥,如果用同樣的SqlSession對象查詢相同的數(shù)據(jù)毅整,則只會在第一次 查詢時 向數(shù)據(jù)庫發(fā)送SQL語句,并將查詢的結果 放入到SQLSESSION中(作為緩存存在)绽左;后續(xù)再次查詢該同樣的對象時悼嫉,則直接從緩存中查詢該對象即可(即省略了數(shù)據(jù)庫的訪問)

二級緩存:MyBatis默認情況沒有開啟二級緩存,需要手工打開拼窥。
1.conf.xml

<!-- 開啟二級緩存 -->
<setting name="cacheEnabled" value="true"/>

2.在具體的mapper.xml中聲明開啟(studentMapper.xml中)

<mapper namespace="org.lanqiao.mapper.StudentMapper">
    <cache/>
<mapper/>
  • cache標簽:聲明此namespace開啟二級緩存

根據(jù)異常提示:NotSerializableException可知戏蔑,MyBatis的二級緩存 是將對象 放入硬盤文件中

準備緩存的對象,必須實現(xiàn)了序列化接口(如果開啟的緩存Namespace="org.lanqiao.mapper.StudentMapper")鲁纠,可知序列化對象為Student总棵,因此需要將Student序列化(序列化Student類,以及Student的級聯(lián)屬性改含、和父類)

  • 觸發(fā)將對象寫入二級緩存的時機:SqlSession對象的close()方法情龄。

Mybatis自帶二級緩存:[同一個namespace]生成的mapper對象

回顧:namespace的值就是接口的全類名(包名.類名),通過接口可以產(chǎn)生代理對象(studentMapper對象)

namespace決定了studentMapper對象的產(chǎn)生

結論:只要產(chǎn)生的xxxMapper對象 來自于同一個namespace候味,則 這些對象 共享二級緩存刃唤。

注意:二級緩存 的范圍是同一個namespace, 如果有多個xxMapper.xml的namespace值相同,則通過這些xxxMapper.xml產(chǎn)生的xxMapper對象仍然共享二級緩存白群。

  • 禁用 :select標簽中useCache="false"

清理:

1.與清理一級緩存的方法相同
commit(); (一般執(zhí)行增刪改時 會清理掉緩存尚胞;設計的原因 是為了防止臟數(shù)據(jù))
在二級緩存中,commit()不能是查詢自身的commit帜慢。

commit會清理一級和二級緩存笼裳;但是 清理二級緩存時,不能是查詢自身的commit粱玲;
2. 在select標簽中 增加屬性 flushCache="true"

命中率:緩存中存在就是命中躬柬。命中數(shù)/查詢數(shù)

三方提供的二級緩存:ehcache、memcache

要想整合三方提供的二級緩存 (或者自定義二級緩存)抽减,必須實現(xiàn)org.apache.ibatis.cache.Cache接口允青,該接口的默認實現(xiàn)類是PerpetualCache

整合ehcache二級緩存:

  1. ehcache-core.jar mybatis-Ehcache.jar slf4j-api.jar
  2. 編寫ehcache配置文件 Ehcache.xml
  3. 開啟EhCache二級緩存
    在xxxMapper.xml中開啟
<cache  type="org.mybatis.caches.ehcache.EhcacheCache">
    <!-- 通過property覆蓋Ehcache.xml中的值 -->
    <property name="maxElementsInMemory" value="2000"/>
    <property name="maxElementsOnDisk" value="3000"/>
</cache>
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卵沉,隨后出現(xiàn)的幾起案子颠锉,更是在濱河造成了極大的恐慌,老刑警劉巖史汗,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琼掠,死亡現(xiàn)場離奇詭異,居然都是意外死亡停撞,警方通過查閱死者的電腦和手機瓷蛙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艰猬,你說我怎么就攤上這事横堡。” “怎么了姥宝?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵翅萤,是天一觀的道長恐疲。 經(jīng)常有香客問我腊满,道長,這世上最難降的妖魔是什么培己? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任碳蛋,我火速辦了婚禮,結果婚禮上省咨,老公的妹妹穿的比我還像新娘肃弟。我一直安慰自己,他們只是感情好零蓉,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布笤受。 她就那樣靜靜地躺著,像睡著了一般敌蜂。 火紅的嫁衣襯著肌膚如雪箩兽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天章喉,我揣著相機與錄音汗贫,去河邊找鬼。 笑死秸脱,一個胖子當著我的面吹牛落包,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摊唇,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼咐蝇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了巷查?” 一聲冷哼從身側(cè)響起有序,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吮便,沒想到半個月后笔呀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡髓需,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年许师,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡微渠,死狀恐怖搭幻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逞盆,我是刑警寧澤檀蹋,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站云芦,受9級特大地震影響俯逾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舅逸,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一桌肴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琉历,春花似錦坠七、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽售碳。三九已至惕稻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赊抖,已是汗流浹背艘包。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工的猛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人想虎。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓卦尊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舌厨。 傳聞我的和親對象是個殘疾皇子岂却,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359