MyBatis--單表的CURD操作(使用mapper動(dòng)態(tài)代理)

單表的CURD操作(使用mapper動(dòng)態(tài)代理)

MyBatis框架拋開dao的實(shí)現(xiàn)類,直接定位到映射文件mapper的相應(yīng)sql語句,對(duì)DB進(jìn)行操作。這種dao的實(shí)現(xiàn)方式成為mapper的動(dòng)態(tài)代理方式。

mapper動(dòng)態(tài)代理方式無需程序員實(shí)現(xiàn)dao接口。接口是由MyBatis結(jié)合映射文件自動(dòng)生成的動(dòng)態(tài)代理實(shí)現(xiàn)的。

1.映射文件的namespace屬性值

一般情況下,一個(gè)dao接口的實(shí)現(xiàn)類方法使用的是同一個(gè)sql映射文件中的sql映射id。所以,MyBatis框架要求,將映射文件中<mapper/>標(biāo)簽的namespace屬性設(shè)為dao接口的全類名,則系統(tǒng)會(huì)根據(jù)方法所屬dao接口,自動(dòng)到相應(yīng)namespace的映射文件中查找相關(guān)的sql映射。

簡(jiǎn)單來說,通過接口名即可定位到映射文件mapper。

2.修改日志輸出控制文件

mapper的namespace修改了,則需要將日志輸出控制文件中的logger的輸出對(duì)象進(jìn)行修改

##define a logger
#log4j.logger.namespace_value=trace,console
log4j.logger.com.hcx.dao.IStudentDao=trace,console

3.dao接口方法名

MyBatis框架要求,接口中的方法名,與映射文件中相應(yīng)的sql標(biāo)簽的id值相同。系統(tǒng)會(huì)自動(dòng)根據(jù)方法名到相應(yīng)的映射文件中查找同名的sql映射id。

簡(jiǎn)單來說,通過方法名就可定位到映射文件mapper中相應(yīng)的sql語句。

接口:

public interface IStudentDao {
    
    int insertStudent(Student student); 
    void deleteStudentById(int id);
    void updateStudent(Student student);
    
    List<Student> selectAllStudents();
    Map<String, Object> selectAllStudentsMap();
    
    Student selectStudentById(int id);
    List<Student> selectStudentsByName(String name);
    
}

映射:

<select id="selectStudnetById" parameterType="int" resultType="com.hcx.beans.Student">
    select id,name,age,score,birthday from student where id=#{id}
</select>

4.dao對(duì)象的獲取

使用時(shí),只需要調(diào)用Sqlsession的getMapper()方法,即可獲取指定接口的實(shí)現(xiàn)類對(duì)象。該方法的參數(shù)為指定dao接口類的class值。

session = factory.openSession();
dao = session.getMapper(IStudentDao.class);

5.刪除dao實(shí)現(xiàn)類

由于通過調(diào)用dao接口的方法,不僅可以從sql映射文件中找到所要執(zhí)行sql語句,還可通過方法參數(shù)及返回值,將sql語句的動(dòng)態(tài)參數(shù)傳入,將查詢結(jié)果返回。所以,dao的實(shí)現(xiàn)工作,完全可以由MyBatis系統(tǒng)自動(dòng)根據(jù)映射文件完成。所以,dao的實(shí)現(xiàn)類就不再需要了。

dao實(shí)現(xiàn)對(duì)象是由jdk的proxy動(dòng)態(tài)代理自動(dòng)生成的。

6.測(cè)試類

1.在before注解方法中獲取到Sqlsession對(duì)象后,通過Sqlsession的getMapper方法創(chuàng)建dao接口實(shí)現(xiàn)類的動(dòng)態(tài)代理對(duì)象。在after注解方法中關(guān)閉Sqlsession對(duì)象。

public class MyTest {
    
    private IStudentDao dao;
    private SqlSession session;
    
    @Before
    public void setUp(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IStudentDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
}

2.添加Sqlsession的提交方法

在增刪改測(cè)試方法的最后,添加Sqlsession的commit方法,完成提交。

@Test
public void test01(){
    Student student = new Student("張三",23,99.8);
    dao.insertStudent(student);
    session.commit();
}

@Test
public void test02(){
    Student student = new Student("張三",23,99.8);
    dao.insertStudentCatchId(student);
    System.out.println("student="+student);
    session.commit();
}

@Test
public void test03(){
    dao.deleteStudentById(3);   
    session.commit();
}

3.刪除selectStudentMap()方法測(cè)試

MyBatis框架對(duì)于dao查詢的自動(dòng)實(shí)現(xiàn),底層只會(huì)調(diào)用selectOne與selectList()方法。而框架選擇方法的標(biāo)準(zhǔn)是測(cè)試類中用戶接收返回值得對(duì)象類型。若接收類型為list,則自動(dòng)選擇selectList()方法;否則,自動(dòng)選擇selectOne()方法。

接收類型為map,所以框架選擇了selectOne()方法,會(huì)報(bào)錯(cuò)。

7.多查詢條件無法整體接收問題的解決

在實(shí)際工作中,表單中所給出的查詢條件有時(shí)是無法將其封裝為一個(gè)對(duì)象的,也就是說,查詢方法只能攜帶多個(gè)參數(shù),而不能攜帶將著這多個(gè)參數(shù)進(jìn)行封裝的一個(gè)對(duì)象。對(duì)于這個(gè)問題,有兩種解決方案。

方案一:將這多個(gè)參數(shù)封裝為一個(gè)map

將這多個(gè)參數(shù)封裝為一個(gè)Map<String,Object>,根據(jù)Map進(jìn)行查詢。

1.dao接口

List<Student> selectStudentByMap(Map<String, Object> map);

2.測(cè)試類

@Test
public void test10(){
    Map<String, Object> map = new HashMap<String,Object>();
    map.put("nameCondition", "張");
    map.put("ageCondition",22);
    
    List<Student> students = dao.selectStudentByMap(map);
    for (Student student : students) {
        System.out.println(student);
    }
}

3.修改映射文件

<select id="selectStudentByMap" resultType="Student">
    select *from student
    where name like '%' #{nameCondition} '%'
    and age > #{ageCondition}
</select>

方案二:多個(gè)參數(shù)逐個(gè)接收

對(duì)于mapper中的SQL語句,可以通過參數(shù)索引#{index}的方式逐個(gè)接收每個(gè)參數(shù)。

1.dao接口

List<Student> selectStudentByConditions(String name,int age);

2.測(cè)試類

@Test
public void test11(){
    List<Student> students = dao.selectStudentByConditions("張", 22);
    for (Student student : students) {
        System.out.println(student);
    }
}

3.映射文件

<select id="selectStudentByconditions" resultType="Student">
    select * from student
    where name like '%' #{0} '%'
    and age > #{1}
</select>

動(dòng)態(tài)SQL

動(dòng)態(tài)sql,主要用于解決查詢條件不確定的情況:在程序運(yùn)行期間,根據(jù)用戶提交的查詢條件進(jìn)行查詢。提交的查詢條件不同,執(zhí)行的sql語句不同。若將每種可能的情況均逐一列出,對(duì)所有條件進(jìn)行排列組合,將會(huì)出現(xiàn)大量的sql語句。此時(shí),可使用動(dòng)態(tài)sql來解決這樣的問題

用戶自定義查詢.PNG

動(dòng)態(tài)sql,即通過MyBatis提供的各種標(biāo)簽對(duì)條件作出判斷以實(shí)現(xiàn)拼接sql語句

這里的條件判斷使用的表達(dá)式為OGNL表達(dá)式。常用的動(dòng)態(tài)SQL標(biāo)簽有<if>、<where>、<choose/>、<foreach>等。

1.實(shí)體類:

public class Student {
    
    private Integer id;
    private String name;
    private int age;
    private double score;
    //無參構(gòu)造器與帶參構(gòu)造器
    //getter 和 setter
    //toString()
}

2.測(cè)試類

public class MyTest {
    
    private IStudentDao dao;
    private SqlSession session;
    
    @Before
    public void setUp(){
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(IStudentDao.class);
    }
    
    @After
    public void tearDown(){
        if(session!=null){
            session.close();
        }
    }
}

注意事項(xiàng):

在mapper的動(dòng)態(tài)sql中若出現(xiàn)大于號(hào)(>)、小于號(hào)(<)、大于等于號(hào)(>=)、小于等于號(hào)(<=)等符號(hào),最好將其轉(zhuǎn)換為實(shí)體符號(hào)。否則,xml可能會(huì)出現(xiàn)解析出錯(cuò)問題。

替換規(guī)則.PNG

1.<if/>標(biāo)簽

對(duì)于該標(biāo)簽的執(zhí)行,當(dāng)test的值為true時(shí),會(huì)將其包含的sql片段拼接到其后所在的sql語句中。

例:查詢出滿足用戶提交查詢條件的所有學(xué)生。用戶提交的查詢條件可以包含一個(gè)姓名的模糊查詢,同時(shí)還可以包含一個(gè)年齡的下限。當(dāng)然,用戶在提交表單時(shí)可能兩個(gè)條件均作出了設(shè)定,也可能兩個(gè)條件均不做設(shè)定,也可以只做其中一項(xiàng)設(shè)定。

查詢條件不確定,查詢條件依賴于用戶提交的內(nèi)容。此時(shí),就可使用動(dòng)態(tài)sql語句,根據(jù)用戶提交內(nèi)容對(duì)將要執(zhí)行的sql進(jìn)行拼接。

dao接口:

public interface IStudentDao {      
    List<Student> selectStudentsIf(Student student) 
}

映射文件:

<select id="selectStudentsIf" resultType="Student">
    select * from student
    where 1=1
    <if test="name !=null and name!=''">
        and name like '%' #{name} '%'
    </if>
    <if test="age > 0">
        and age > #{age}
    </if>
</select>

測(cè)試類:

@Test
public void test01(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsIf(student);
    System.out.println(students);
}

2.<where/>標(biāo)簽

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
}

映射文件:

<select id="selectStudentsWhere" resultType="Student">
    select * from student
    <where>
        <if test="name!=null and name!=''">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </where>
</select>

測(cè)試:

@Test
public void test02(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsWhere(student);
    System.out.println(students);
}

3.<choose/>標(biāo)簽

該標(biāo)簽只可以包含<when/><otherwise/>,可以包含多個(gè)<when/>與一個(gè)<otherwise/>。他們聯(lián)合使用,完成java中的開關(guān)語句switch..case功能。

需求:若姓名不空,則按姓名查詢;若姓名為空,則按年齡查詢;若沒有查詢條件,則沒有查詢結(jié)果。

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
}

映射文件:

對(duì)于<choose/>標(biāo)簽,其會(huì)從第一個(gè)<when/>開始逐個(gè)向后進(jìn)行條件判斷。若出現(xiàn)<when/>中的test屬性值為true的情況,則直接結(jié)束<choose/>標(biāo)簽,不再向后進(jìn)行判斷查找。若所有<when/>的test判斷結(jié)果均為false,則最后會(huì)執(zhí)行<otherwise/>標(biāo)簽。

<select id="selectStudentsChoose" resultType="Student">
    select * from student
    <where>
        <choose>
            <when test="name!=null and name!=''">
                and name like '%' #{name} '%'
            </when>
            <when test="age>0">
                and age < #{age}
            </when>
            <otherwise>
                and 1 != 1
            </otherwise>
        </choose>
    </where>
</select>

測(cè)試類:

@Test
public void test03(){
    Student student = new Student();
    List<Student> students = dao.selectStudentsChoose(student);
    System.out.println(students);
}

4.<foreach/>標(biāo)簽--遍歷數(shù)組

<foreach/>標(biāo)簽用于實(shí)現(xiàn)對(duì)于數(shù)組于集合的遍歷。對(duì)其使用,需要注意:

  • collection表示要遍歷的集合類型,這里是數(shù)組,即array
  • open、close、separator為對(duì)遍歷內(nèi)容的SQL拼接

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
    List<Student> selectStudentsForeachArray(Object[] studentIds);
}

映射文件

動(dòng)態(tài)sql的判斷中使用的都是OGNL表達(dá)式。OGNL表達(dá)式中的數(shù)組使用array表示,數(shù)組長(zhǎng)度使用array.length表示。

<select id="selectStudentsForeachArray" resultType="Student">
    select * from student
    <if test="array !=null and array.length >0">
        where id in
        <foreach collection="array" open="(" close=")" item="myid" separator=",">
            #{myid}
        </foreach>
    </if>
</select>
foreach標(biāo)簽.PNG

測(cè)試類:

@Test
public void test04(){   
    Object[] studentIds = new Object[]{1,3};
    List<Student> students = dao.selectStudentsForeachArray(studentIds);
    System.out.println(students);
}

5.<foreach/>標(biāo)簽--遍歷泛型為基本類型的List

dao接口:

public interface IStudentDao {
    
    List<Student> selectStudentsIf(Student student);
    List<Student> selectStudentsWhere(Student student);
    List<Student> selectStudentsChoose(Student student);
    List<Student> selectStudentsForeachArray(Object[] studentIds);
    List<Student> selectStudentsForeachList(List<Integer> studentIds);
}

映射文件:

OGNL表達(dá)式中的List使用list表示,其大小使用list.size表示。

<select id="selectStudentsForeachList" resultType="Student">
    select * from student
    <if test="list!=null and list.size > 0">
        where id in
        <foreach collection="list"  open="(" close=")" item="myid" separator=",">
            #{myid}
        </foreach>
    </if>
</select>

測(cè)試類:

@Test
public void test05(){
    List<Integer> studentIds = new ArrayList<Integer>();
    student.add(1);
    student.add(3);
    
    List<Student> students = dao.selectStudentsForeachList(studentIds);
    System.out.println(students);   
}

6.<foreach/>標(biāo)簽--遍歷泛型為自定義類型的List

dao接口:

List<Student> selectStudentsForeachList2(List<Student> students);

映射文件:

注意,這里的當(dāng)前遍歷對(duì)象類型是List中的泛型,即是Student對(duì)象。

<select id="selectStudentsForeachList2" resultType="Student">
    <!-- select * from student where id in(1,3) -->
    select * from student
    <if test="list!=null and list.size > 0">
        where id in
        <foreach collection="list"  open="(" close=")" item="stu" separator=",">
            #{stu.id}
        </foreach>
    </if>
</select>

測(cè)試類:

@Test
public void test09(){
    Student student1 = new Student();
    student1.setId(1);
    Student student3 = new Student();
    student3.setId(3);
    
    List<Student> students = new ArrayList<Student>();
    students.add(student1);
    students.add(student3);
    
    students = dao.selectStudentsForeachList2(students);
    System.out.println(students);
}

<foreach/>標(biāo)簽執(zhí)行insert操作:
在執(zhí)行插入時(shí)要注意不能用外部的括號(hào):

/**
     * 保存品牌id和類目id到中間表
     * @param bid 品牌id
     * @param cidList 所屬類目id集合
     */
    void insertCategoryAndBrand(@Param("bid")Long bid, @Param("cidList") List cidList);

xml:

<insert id="insertCategoryAndBrand" parameterType="map">
      INSERT INTO tb_category_brand(category_id,brand_id)
      VALUES
      <foreach collection="cidList" item="item" separator=",">
          (#{item},#{bid})
      </foreach>
</insert>

真正執(zhí)行時(shí)的sql語句:insert into tb_category_brand(category_id,brand_id) values(1000,1000),(1000,2000);
是分別每一組數(shù)據(jù)都有括號(hào),因?yàn)椴荒苁褂胦pen和close標(biāo)簽來追加括號(hào)在最外面,否則會(huì)報(bào)以下錯(cuò)誤:

image.png

7.<sql/>標(biāo)簽

<sql/>標(biāo)簽用于定義sql片段,以便其它sql標(biāo)簽復(fù)用。而其他標(biāo)簽使用該SQL片段,需要使用<include/>子標(biāo)簽。該<sql/>標(biāo)簽可以定義SQL語句中的任何部分,所以<include/>子標(biāo)簽可以放在動(dòng)態(tài)sql的任何位置

dao接口:

List<Student> selectStudentsBySQLFragment(List<Student> students);

映射文件:

使用sql片段.PNG

測(cè)試類:

@Test
public void test09(){
    Student student1 = new Student();
    student1.setId(1);
    Student student3 = new Student();
    student3.setId(3);
    
    List<Student> students = new ArrayList<Student>();
    students.add(student1);
    students.add(student3);
    
    students = dao.selectStudentsBySQLFragment(students);
    System.out.println(students);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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