SpringBoot系列(五)Mybatis整合完整詳細(xì)版

SpringBoot系列(五)Mybatis整合

目錄

  1. mybatis簡(jiǎn)介
  2. 項(xiàng)目創(chuàng)建
  3. entity
  4. dao
  5. service
  6. serviceImpl
  7. mapper
  8. controller

1. Mybatis簡(jiǎn)介

?MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射婆硬。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集稠项。MyBatis 可以使用簡(jiǎn)單的 XML 或注解來(lái)配置和映射原生信息,將接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄粥谬。

?換句話說(shuō),我覺(jué)得利用mybatis整合持久層要方便很多,比起以前編寫jdbc代碼操作數(shù)據(jù)庫(kù)的一些連接搂蜓,簡(jiǎn)直不要太爽。

2. 項(xiàng)目創(chuàng)建

?創(chuàng)建一個(gè)簡(jiǎn)單的具有start-web依賴的SpringBoot項(xiàng)目辽装,然后添加mybatis相關(guān)的依賴帮碰。

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

?依賴下載完之后,在yml文件拾积,也可以是properties文件里面配置連接數(shù)據(jù)庫(kù)的相關(guān)配置殉挽。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

?然后我們?cè)跀?shù)據(jù)庫(kù)mybatis下面創(chuàng)建一個(gè)student表

CREATE TABLE `student`(
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '唯一標(biāo)識(shí)id',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '姓名',
  `age` int(3) NOT NULL COMMENT '年齡',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

完成項(xiàng)目初始配置丰涉。

3. entity 實(shí)體類代碼

/**
 * (Student)實(shí)體類
 *
 * @author 全棧學(xué)習(xí)筆記
 * @since 2020-04-14 11:39:10
 */
public class Student  {
    private static final long serialVersionUID = -91969758749726312L;
    /**
    * 唯一標(biāo)識(shí)id
    */
    private Integer id;
    /**
    * 姓名
    */
    private String name;
    /**
    * 年齡
    */
    private Integer age;
}

?以上省略了get,以及set方法斯碌。

4. dao層代碼

package com.example.demomybatis.dao;

import com.example.demomybatis.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;

/**
 * (Student)表數(shù)據(jù)庫(kù)訪問(wèn)層
 *
 * @author 全棧學(xué)習(xí)筆記
 * @since 2020-04-14 11:39:18
 */
@Mapper
@Repository
public interface StudentDao {

    /**
     * 通過(guò)ID查詢單條數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 實(shí)例對(duì)象
     */
    Student queryById(Integer id);

    /**
     * 查詢指定行數(shù)據(jù)
     *
     * @param offset 查詢起始位置
     * @param limit 查詢條數(shù)
     * @return 對(duì)象列表
     */
    List<Student> queryAllByLimit(@Param("offset") int offset, @Param("limit") int limit);


    /**
     * 通過(guò)實(shí)體作為篩選條件查詢
     *
     * @param student 實(shí)例對(duì)象
     * @return 對(duì)象列表
     */
    List<Student> queryAll(Student student);

    /**
     * 新增數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 影響行數(shù)
     */
    int insert(Student student);

    /**
     * 修改數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 影響行數(shù)
     */
    int update(Student student);

    /**
     * 通過(guò)主鍵刪除數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 影響行數(shù)
     */
    int deleteById(Integer id);

}

?
代碼說(shuō)明:dao層屬于數(shù)據(jù)訪問(wèn)層一死,與mybatis 的xml文件相互映射,實(shí)現(xiàn)SQL語(yǔ)句的功能傻唾。

?注解說(shuō)明:在dao層的類需要加上 @Mapper的注解投慈,這個(gè)注解是mybatis提供的,標(biāo)識(shí)這個(gè)類是一個(gè)數(shù)據(jù)訪問(wèn)層的bean冠骄,并交給spring容器管理伪煤。并且可以省去之前的xml映射文件。在編譯的時(shí)候凛辣,添加了這個(gè)類也會(huì)相應(yīng)的生成這個(gè)類的實(shí)現(xiàn)類抱既。

?如果你是用的idea,在serviceImpl中使用 @Autowired注入bean的時(shí)候扁誓,idea會(huì)報(bào)錯(cuò)防泵,但是不影響運(yùn)行,報(bào)錯(cuò)是因?yàn)?@mapper不是spring提供的跋理,當(dāng)需要自動(dòng)注入這個(gè)bean的時(shí)候idea不能 預(yù)檢測(cè)到這個(gè)bean是否可以注入到容器中择克,不知道新版的idea會(huì)不會(huì)有這種問(wèn)題。如果想消除這個(gè)報(bào)錯(cuò)前普,你可以在dao層的類上面加上一個(gè) @Repository肚邢,這個(gè)注解是spring提供的,這樣就可以預(yù)檢測(cè)到mapper的bean是可以注冊(cè)到spring容器里面的拭卿。

?你會(huì)發(fā)現(xiàn)在代碼中骡湖,有的接口的參數(shù)是帶了 @Param這個(gè)注解的,有的參數(shù)是沒(méi)有這個(gè)注解的峻厚。如果你只有一個(gè)參數(shù)响蕴,這個(gè)注解可要可不要。當(dāng)你有兩個(gè)及其以上的注解時(shí)惠桃,你就需要用這個(gè)注解了浦夷,不然在對(duì)應(yīng)的xml文件,它分辨不出來(lái)這個(gè)參數(shù)是哪一個(gè)就會(huì)報(bào)錯(cuò)辜王,用這個(gè)注解的意思就是說(shuō)標(biāo)識(shí)這個(gè)參數(shù)的名稱劈狐,以便讓接受參數(shù)的一方更好的找到并利用這個(gè)值。

5. service層代碼

package com.example.demomybatis.service;

import com.example.demomybatis.entity.Student;
import java.util.List;

/**
 * (Student)表服務(wù)接口
 *
 * @author 全棧學(xué)習(xí)筆記
 * @since 2020-04-14 11:39:19
 */
public interface StudentService {

    /**
     * 通過(guò)ID查詢單條數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 實(shí)例對(duì)象
     */
    Student queryById(Integer id);

    /**
     * 查詢多條數(shù)據(jù)
     *
     * @param offset 查詢起始位置
     * @param limit 查詢條數(shù)
     * @return 對(duì)象列表
     */
    List<Student> queryAllByLimit(int offset, int limit);

    /**
     * 新增數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 實(shí)例對(duì)象
     */
    Student insert(Student student);

    /**
     * 修改數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 實(shí)例對(duì)象
     */
    Student update(Student student);

    /**
     * 通過(guò)主鍵刪除數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 是否成功
     */
    boolean deleteById(Integer id);

}

?代碼說(shuō)明:這是服務(wù)層的接口呐馆,serviceImpl對(duì)應(yīng)服務(wù)層接口的實(shí)現(xiàn)肥缔。

6. serviceImpl層代碼

package com.example.demomybatis.service.impl;

import com.example.demomybatis.entity.Student;
import com.example.demomybatis.dao.StudentDao;
import com.example.demomybatis.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * (Student)表服務(wù)實(shí)現(xiàn)類
 *
 * @author 全棧學(xué)習(xí)筆記
 * @since 2020-04-14 11:39:19
 */
@Service("studentService")
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentDao studentDao;

    /**
     * 通過(guò)ID查詢單條數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 實(shí)例對(duì)象
     */
    @Override
    public Student queryById(Integer id) {
        return this.studentDao.queryById(id);
    }

    /**
     * 查詢多條數(shù)據(jù)
     *
     * @param offset 查詢起始位置
     * @param limit 查詢條數(shù)
     * @return 對(duì)象列表
     */
    @Override
    public List<Student> queryAllByLimit(int offset, int limit) {
        return this.studentDao.queryAllByLimit(offset, limit);
    }

    /**
     * 新增數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 實(shí)例對(duì)象
     */
    @Override
    public Student insert(Student student) {
        this.studentDao.insert(student);
        return student;
    }

    /**
     * 修改數(shù)據(jù)
     *
     * @param student 實(shí)例對(duì)象
     * @return 實(shí)例對(duì)象
     */
    @Override
    public Student update(Student student) {
        this.studentDao.update(student);
        return this.queryById(student.getId());
    }

    /**
     * 通過(guò)主鍵刪除數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer id) {
        return this.studentDao.deleteById(id) > 0;
    }
}

?代碼說(shuō)明:@Service標(biāo)識(shí)這個(gè)bean是service層的,也就是服務(wù)層汹来,并交給spring容器管理续膳。參數(shù)的value屬性是這個(gè)bean的名稱改艇,也可以不寫,默認(rèn)為類名坟岔。

?這里我們可以說(shuō)一下谒兄,@Resource@Autowired,前面我們?cè)?strong>serviceImpl里面需要用到dao層的方法的時(shí)候,不是直接new一個(gè)對(duì)象炮车,在哪需要就在哪new舵变,而是利用注解酣溃,實(shí)現(xiàn)自定注入裝配瘦穆,利用spring容器管理這些bean,這樣寫出來(lái)的代碼是松耦合的赊豌,類之間的耦合度更低扛或,維護(hù)性就相對(duì)提高了。
@Resource@Autowired是可以起到一個(gè)相同的作用碘饼。根據(jù)包名就可以看到熙兔,他們不是一個(gè)包里面的。區(qū)別如下:

  1. @Autowired默認(rèn)按類型裝配,默認(rèn)情況下必須要求依賴對(duì)象必須存在艾恼,如果要允許null值住涉,可以設(shè)置它的required屬性為false,如:@Autowired(required=false) 钠绍,這個(gè)注解是屬于spring的舆声,如果我們想使用名稱裝配可以結(jié)合 @Qualifier 注解進(jìn)行使用。
  2. @Resource默認(rèn)按照名稱進(jìn)行裝配柳爽,名稱可以通過(guò)name屬性進(jìn)行指定媳握,如果沒(méi)有指定name屬性,當(dāng)注解寫在字段上時(shí)磷脯,默認(rèn)取字段名進(jìn)行安裝名稱查找蛾找,如果注解寫在setter方法上默認(rèn)取屬性名進(jìn)行裝配。當(dāng)找不到與名稱匹配的bean時(shí)才按照類型進(jìn)行裝配赵誓。但是需要注意的是打毛,如果name屬性一旦指定挨摸,就只會(huì)按照名稱進(jìn)行裝配狈醉。這個(gè)注解屬于J2EE的

7. mapper層代碼

?所謂的mapper層欺劳,就是xml文件绑雄,與dao層對(duì)應(yīng)的展辞。

<?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.example.demomybatis.dao.StudentDao">
    <resultMap type="com.example.demomybatis.entity.Student" id="StudentMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
    </resultMap>

    <!--查詢單個(gè)-->
    <select id="queryById" resultMap="StudentMap">
        select
          id, name, age
        from mybatis.student
        where id = #{id}
    </select>

    <!--查詢指定行數(shù)據(jù)-->
    <select id="queryAllByLimit" resultMap="StudentMap">
        select
          id, name, age
        from mybatis.student
        limit #{offset}, #{limit}
    </select>

    <!--通過(guò)實(shí)體作為篩選條件查詢-->
    <select id="queryAll" resultMap="StudentMap">
        select
          id, name, age
        from mybatis.student
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="name != null and name != ''">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </where>
    </select>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into mybatis.student(name, age)
        values (#{name}, #{age})
    </insert>

    <!--通過(guò)主鍵修改數(shù)據(jù)-->
    <update id="update">
        update mybatis.student
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="age != null">
                age = #{age},
            </if>
        </set>
        where id = #{id}
    </update>

    <!--通過(guò)主鍵刪除-->
    <delete id="deleteById">
        delete from mybatis.student where id = #{id}
    </delete>

</mapper>

?這里面對(duì)應(yīng)了SQL的增刪改查語(yǔ)句,然后在dao層的方法万牺,對(duì)應(yīng)了每一個(gè)SQL語(yǔ)句罗珍,這里面SQL語(yǔ)句的id洽腺,對(duì)應(yīng)dao層的每一個(gè)接口方法。
默認(rèn)的配置是檢測(cè)不到這個(gè)xml文件的覆旱,然后我們需要做以下的配置蘸朋。
我把xml文件放在resources文件夾下面的dao文件夾下面。
然后我們?cè)趛ml里面加上以下配置扣唱。

mybatis:
  type-aliases-package: com.example.demomybatis.entity
  mapper-locations: classpath:dao/*Mapper.xml

8. controller層代碼

?controller層的代碼我們是用來(lái)測(cè)試的藕坯,一般也是返回?cái)?shù)據(jù)給前端的地方。

package com.example.demomybatis.controller;

import com.example.demomybatis.entity.Student;
import com.example.demomybatis.service.StudentService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * (Student)表控制層
 *
 * @author 全棧學(xué)習(xí)筆記
 * @since 2020-04-14 11:39:20
 */
@RestController
@RequestMapping("student")
public class StudentController {
    /**
     * 服務(wù)對(duì)象
     */
    @Resource
    private StudentService studentService;

    /**
     * 通過(guò)主鍵查詢單條數(shù)據(jù)
     *
     * @param id 主鍵
     * @return 單條數(shù)據(jù)
     */
    @GetMapping("selectOne")
    public Student selectOne(Integer id) {
        return this.studentService.queryById(id);
    }

}

?代碼說(shuō)明:@RestController 這個(gè)注解等效于 @Controller加上 @ResponseBody,添加了這個(gè)注解就是讓這個(gè)類返回json串噪沙,這是spring內(nèi)部提供的json解析炼彪。@RequesMapping 注解是一個(gè)地址映射的注解。就是根據(jù)這個(gè)地址正歼,可以找到這個(gè)方法辐马,這個(gè)類,注解到類上局义,就相當(dāng)于方法的父類地址喜爷。

?測(cè)試一下。我們先在數(shù)據(jù)庫(kù)里面添加一條數(shù)據(jù)萄唇。

insert into student(id,name,age) VALUES(2,'全棧學(xué)習(xí)筆記',22)

?然后在瀏覽器輸入:localhost:8088/student/selectOne?id=2 就可以看到我們拿到的數(shù)據(jù)了檩帐。端口配置根據(jù)自己項(xiàng)目而定。

好了另萤,這一期的mybatis整合就到這里湃密,有興趣的可以 wx search 全棧學(xué)習(xí)筆記 ,給個(gè)關(guān)注仲墨,精彩美文每天推送到你的手中勾缭!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市目养,隨后出現(xiàn)的幾起案子俩由,更是在濱河造成了極大的恐慌,老刑警劉巖癌蚁,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幻梯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡努释,警方通過(guò)查閱死者的電腦和手機(jī)碘梢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)伐蒂,“玉大人煞躬,你說(shuō)我怎么就攤上這事。” “怎么了恩沛?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵在扰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我雷客,道長(zhǎng)芒珠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任搅裙,我火速辦了婚禮皱卓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘部逮。我一直安慰自己娜汁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布甥啄。 她就那樣靜靜地躺著存炮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜈漓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天宫盔,我揣著相機(jī)與錄音融虽,去河邊找鬼。 笑死灼芭,一個(gè)胖子當(dāng)著我的面吹牛有额,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彼绷,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼巍佑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了寄悯?” 一聲冷哼從身側(cè)響起萤衰,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猜旬,沒(méi)想到半個(gè)月后脆栋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洒擦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年椿争,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熟嫩。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秦踪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椅邓,我是刑警寧澤舍扰,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站希坚,受9級(jí)特大地震影響边苹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裁僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一个束、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聊疲,春花似錦茬底、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贡珊,卻和暖如春最爬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背门岔。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工爱致, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寒随。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓糠悯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妻往。 傳聞我的和親對(duì)象是個(gè)殘疾皇子互艾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 本文是作者原創(chuàng)文章,如需轉(zhuǎn)載讯泣,請(qǐng)先聯(lián)系 這幾天一直都在做spring+mybaits的整合項(xiàng)目纫普,但是其中遇到很多問(wèn)...
    eric_lai閱讀 13,518評(píng)論 11 41
  • 1 版本說(shuō)明 springboot:2.0 jdk:1.8 2 創(chuàng)建springBoot項(xiàng)目 創(chuàng)建項(xiàng)目時(shí)勾選必要w...
    Java小鋪閱讀 780評(píng)論 2 5
  • 什么是spring? Spring 是個(gè)java企業(yè)級(jí)應(yīng)用的開(kāi)源開(kāi)發(fā)框架。Spring主要用來(lái)開(kāi)發(fā)Java應(yīng)用判帮,但...
    碼記閱讀 411評(píng)論 0 2
  • 來(lái)源:關(guān)于Spring IOC (DI-依賴注入)你需要知道的一切作者:zejian Dao層(AccountDa...
    楊井閱讀 5,326評(píng)論 0 27
  • 姓名:李淑瑛 289期志工 325期志工 423期志工 公司:紹興翔鷹紡織品有限公司 部門:人事行政部 【堅(jiān)持日精...
    李淑瑛閱讀 116評(píng)論 0 0