当前位置:   article > 正文

MyBatis笔记_构建config client时,在resources文件下新建名称为文件

构建config client时,在resources文件下新建名称为文件

目录

一、MyBatis简介

二、MyBatis框架部署

三、MyBatis框架使用

四、单元测试

五、MyBatis的CRUD操作

六、MyBatis工具类封装

七、事务管理

八、MyBatis主配置文件

九、映射文件

十、分页插件

十一、关联映射

十二、动态SQL

十三、模糊查询

十四、MyBatis日志配置

十五、配置数据库连接池-整合Druid

十六、MyBatis缓存

十七、延迟加载


一、MyBatis简介

1.1 框架概念

框架,就是软件的半成品,完成了软件开发过程中的通用操作,程序员只需很少或者不用进行加工就能够实现特定的功能,从而简化开发人员在软件开发中的步骤,提高开发效率。

1.2 常用框架

  • MVC框架:简化了Servlet的开发步骤

  • 持久层框架:完成数据库操作的框架

  • 胶水框架:Spring

SSM Spring SpringMVC MyBatis

SSH Spring Struts2 Hibernate

1.3 MyBatis介绍

MyBatis是一个半自动ORM框架

ORM(Object Relational Mapping)对象关系映射,将Java中的一个对象与数据表中一行记录一一对应。

ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。

  • MyBatis的前身是iBatis,iBatis是Apache软件基金会提供的一个开源项目

  • 2010年iBatis迁移到Google code,正式更名为MyBatis

  • 2013年迁移到Github托管

  • MyBatis特点:

    • 支持自定义SQL、存储过程

    • 对原有的JDBC进行了封装,几乎消除了所有JDBC代码,让开发者只需关注SQL本身

    • 支持XML和注解配置方式自定完成ORM操作,实现结果映射

二、MyBatis框架部署

框架部署,就是将框架引入到我们的项目中

2.1 创建Maven项目

  • Java工程

  • Web工程

2.2 在项目中添加MyBatis依赖

  • 在pom.xml中添加依赖

    • mybatis

    • mysql driver

    1. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    2. <dependency>
    3.    <groupId>mysql</groupId>
    4.    <artifactId>mysql-connector-java</artifactId>
    5.    <version>5.1.47</version>
    6. </dependency>
    7. <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    8. <dependency>
    9.    <groupId>org.mybatis</groupId>
    10.    <artifactId>mybatis</artifactId>
    11.    <version>3.4.6</version>
    12. </dependency>

2.3 创建MyBatis配置文件

  • 创建自定义模板:选择resources----右键New----Edit File Templates

  • 在resources中创建名为mybatis-config.xml的文件

  • mybatis-config.xml文件配置数据库连接信息

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    3.        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    4. <configuration>
    5.    
    6.    <!-- 在environments配置数据库连接信息 -->
    7.    <!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
    8.    <!-- default属性,用来指定使用哪个environment标签 -->
    9.    <environments default="mysql">
    10.        
    11.        <environment id="mysql">
    12.            <!--transactionManager标签用于配置数据库管理方式-->
    13.            <transactionManager type="JDBC"></transactionManager>
    14.            <!--dataSource标签就是用来配置数据库连接信息 -->
    15.            <dataSource type="POOLED">
    16.                <property name="driver" value="com.mysql.jdbc.Driver"/>
    17.                <property name="url" value="jdbc:mysql://localhost:3306/db_2010_fmwy?characterEncoding=utf-8"/>
    18.                <property name="username" value="root"/>
    19.                <property name="password" value="admin123"/>
    20.            </dataSource>
    21.        </environment>
    22.        
    23.    </environments>
    24. </configuration>

三、MyBatis框架使用

案例:学生信息的数据库操作

3.1 创建数据表

tb_students

3.2 创建实体类

Student.java

3.3 创建DAO接口,定义操作方法

StudentDAO.java

3.4 创建DAO接口的映射文件

  • resources目录下,新建名为mappers文件夹

  • mappers中新建名为StudentMapper.xml的映射文件(根据模板创建)

  • 在映射文件中对DAO中定义的方法进行实现:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <!--mapper文件相当于DAO接口的‘实现类’,namespace属性要指定`实现`DAO接口的全限定名-->
  6. <mapper namespace="com.qfedu.dao.StudentDAO">
  7.    <insert id="insertStudent">
  8.       insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
  9.       values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
  10.    </insert>
  11.    <delete id="deleteStudent">
  12.       delete from tb_students where stu_num=#{stuNum}
  13.    </delete>
  14. </mapper>

3.5 将映射文件添加到主配置文件

mybatis-config.xml

四、单元测试

4.1 添加单元测依赖

  1. <dependency>
  2.    <groupId>junit</groupId>
  3.    <artifactId>junit</artifactId>
  4.    <version>4.12</version>
  5. </dependency>

4.2 创建单元测试类

在被测试类名后alt+insert --- 选择Test

4.3 测试代码

  1. package com.qfedu.dao;
  2. import com.qfedu.pojo.Student;
  3. import org.apache.ibatis.io.Resources;
  4. import org.apache.ibatis.session.SqlSession;
  5. import org.apache.ibatis.session.SqlSessionFactory;
  6. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import static org.junit.Assert.*;
  10. public class StudentDAOTest {
  11.    @org.junit.Test
  12.    public void insertStudent() {
  13.        try {
  14.            //加载mybatis配置文件
  15.            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
  16.            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  17.            //会话工厂
  18.            SqlSessionFactory factory = builder.build(is);
  19.            //会话(连接)
  20.            SqlSession sqlSession = factory.openSession();
  21.            //通过会话获取DAO对象
  22.            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
  23.            //测试StudentDAO中的方法
  24.            int i = studentDAO.insertStudent(new Student(0, "10001", "张三", "男", 21));
  25.            //is和session需要关闭
  26.            is.close();
  27.            sqlSession.close();
  28.            //需要手动提交
  29.            sqlSession.commit();
  30.            System.out.println(i);
  31.       } catch (IOException e) {
  32.            e.printStackTrace();
  33.       }
  34.   }
  35.    @org.junit.Test
  36.    public void deleteStudent() {
  37.   }
  38. }

五、MyBatis的CRUD操作

案例:学生信息的增删查改

5.1 添加操作

5.2 删除操作

根据学号删除一条学生信息

  • 在StudentDAO中定义删除方法

StudentDAO

  • 在StudentMapper.xml中对接口方法进行“实现”

StudentMapper.xml

  • 测试:在StudentDAO的测试类中添加测试方法

StudentDAOTest

5.3 修改操作

根据学生学号,修改其他字段信息

  • 在StudentDAO接口中定义修改方法

StudentDAO

  • 在StudentMapper.xml中“实现”接口中定义的修改方法

StudentMapper.xml
  • 单元测试

StudentDAOTest

5.4 查询操作-查询所有

  • 在StudentDAO接口定义操作方法

StudentDAO

  • 在StudentMapper.xml中“实现”DAO中定义的方法

StudentMapper.xml

  • 单元测试

StudentDAOTest

5.5 查询操作-查询一条记录

根据学号查询一个学生信息

  • 在StudentDAO接口中定义方法

StudentDAO

  • 在StudentDAOMapper.xml中配置StudentDAO接口的方法实现——SQL

StudentDAOMapper.xml
  • 单元测试

StudentDAOTest

5.6 查询操作-多参数查询

分页查询(参数 start , pageSize)

  • 在StudentDAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名

StudentDAO
  • 在StudentMapper.xml配置sql时,使用#{别名}获取到指定的参数

StudentMapper.xml

注意 如果DAO操作方法没有通过@Param指定参数别名,在SQL中也可以通过arg0,arg1...或者param1,param2,...获取参数

5.7 查询操作-查询总记录数

  • 在StudentDAO接口中定义操作方法

StudentDAO

  • 在StudentMapper.xml配置sql,通过resultType指定当前操作的返回类型为int

5.8 添加操作回填生成的主键

  • StduentMapper.xml的添加操作标签——insert

  1. <!-- useGeneratedKeys 设置添加操作是否需要回填生成的主键 -->
  2. <!-- keyProperty 设置回填的主键值赋值到参数对象的哪个属性 -->
  3. <insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId">
  4.   insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
  5.   values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
  6. </insert>
主键会回填到你刚插入的对象里,打印这个对象即可

六、MyBatis工具类封装

  • MyBatisUtil

  1. public class MyBatisUtil {
  2.    private static SqlSessionFactory factory;
  3.    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
  4.    static{
  5.        try {
  6.            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
  7.            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  8.            factory = builder.build(is);
  9.       } catch (IOException e) {
  10.            e.printStackTrace();
  11.       }
  12.   }
  13.    public static SqlSessionFactory getFactory(){
  14.        return factory;
  15.   }
  16.    public static SqlSession getSqlSession(){
  17.        SqlSession sqlSession = local.get();
  18.        if(sqlSession == null ){
  19.            sqlSession = factory.openSession();
  20.            local.set(sqlSession);
  21.       }
  22.        return sqlSession;
  23.   }
  24.    public static <T extends Object> T getMapper(Class<T> c){
  25.        SqlSession sqlSession = getSqlSession();
  26.        return sqlSession.getMapper(c);
  27.   }
  28. }

七、事务管理

SqlSession 对象

  • getMapper(DAO.class) : 获取Mapper(DAO接口的实例)

  • 事务管理

7.1 手动提交事务

  • sqlSession.commit();提交事务

  • sqlSession.rollback();事务回滚

测试类中进行事务管理

  1. @Test
  2. public void insertStudent() {
  3.    SqlSession sqlSession = MyBatisUtil.getSqlSession();
  4.    //1.当我们获取sqlSession对象时,就默认开启了事务
  5.    try{
  6.        //通过会话获取DAO对象
  7.        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
  8.        //测试StudentDAO中的方法
  9.        Student student = new Student(0, "10005", "Lily", "女", 21);
  10.        int i = studentDAO.insertStudent(student);
  11.        //2.操作完成并成功之后,需要手动提交
  12.        sqlSession.commit();
  13.   }catch (Exception e){
  14.        //3.当操作出现异常,调用rollback进行回滚
  15.        sqlSession.rollback();
  16.   }
  17. }

业务逻辑层手动事务管理

  1. public class StudentServiceImpl implements StudentService {
  2.    public boolean addStudent(Student student) {
  3.        boolean b = false;
  4.        SqlSession sqlSession = MyBatisUtil.getSqlSession();
  5.        try{
  6.            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
  7.            int i = studentDAO.insertStudent(student);
  8.            b = i>0;
  9.            sqlSession.commit();
  10.       }catch (Exception e){
  11.            sqlSession.rollback();
  12.       }
  13.        return b;
  14.   }
  15. }

7.2 自动提交事务

通过SqlSessionFactory调用openSession方法获取SqlSession对象时,可以通过参数设置事务是否自动提交:

  • 如果参数设置为true,表示自定提交事务: factory.openSession(true);

  • 如果参数设置为false,或者不设置参数,表示手动提交:factory.openSession();/factory.openSession(false);

MyBatisUtil优化

  1. public class MyBatisUtil {
  2.    private static SqlSessionFactory factory;
  3.    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
  4.    static{
  5.        try {
  6.            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
  7.            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  8.            factory = builder.build(is);
  9.       } catch (IOException e) {
  10.            e.printStackTrace();
  11.       }
  12.   }
  13.    public static SqlSessionFactory getFactory(){
  14.        return factory;
  15.   }
  16.    private static SqlSession getSqlSession(boolean isAutoCommit){
  17.        SqlSession sqlSession = local.get();
  18.        if(sqlSession == null ){
  19.            sqlSession = factory.openSession(isAutoCommit);
  20.            local.set(sqlSession);
  21.       }
  22.        return sqlSession;
  23.   }
  24.    //手动事务管理
  25.    public static SqlSession getSqlSession(){
  26.        return getSqlSession(false);
  27.   }
  28.    //自动事务提交
  29.    public static <T extends Object>T getMapper(Class<T> c){
  30.        SqlSession sqlSession = getSqlSession(true);
  31.        return sqlSession.getMapper(c);
  32.   }
  33. }

测试操作

  1. @Test
  2. public void testDeleteStudent() {
  3.    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
  4.    int i = studentDAO.deleteStudent("10001");
  5. }

业务逻辑层自动事务管理

  1. public class StudentServiceImpl implements StudentService {
  2.    private StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
  3.    public boolean addStudent(Student student) {
  4.        int i = studentDAO.insertStudent(student);
  5.        boolean b = i>0;
  6.        return b;
  7.   }
  8. }

八、MyBatis主配置文件

mybatis-config.xml 是MyBatis框架的主配置文件,只要用于配置MyBatis数据源及属性信息

8.1 properties标签

用于设置键值对,或者加载属性文件

  • 在resources目录下创建jdbc.properties文件,配置键值对如下:

  1. mysql_driver=com.mysql.jdbc.Driver
  2. mysql_url=jdbc:mysql://localhost:3306/db_2010_fmwy?characterEncoding=utf-8
  3. mysql_username=root
  4. mysql_password=admin123
  • 在mybatis-config.xml中通过properties标签引用jdbc.properties文件;引入之后,在配置environment时可以直接使用jdbc.properties的key获取对应的value

mybatis-config.xml

8.2 settings标签

  1. <!--设置mybatis的属性-->
  2. <settings>
  3.    <!-- 启动二级缓存-->
  4.    <setting name="cacheEnabled" value="true"/>
  5.    <!-- 启动延迟加载 -->
  6.    <setting name="lazyLoadingEnabled" value="true"/>
  7. </settings>

8.3 typeAliases标签

  1. <!--typeAliases标签用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名-->
  2. <typeAliases>
  3.   //不区分大小写这里定义大写,在resultMap中也可以用student
  4.    <typeAlias type="com.qfedu.pojo.Student" alias="Student"></typeAlias>
  5.    <typeAlias type="com.qfedu.pojo.Book" alias="Book"></typeAlias>
  6. </typeAliases>

8.4 plugins标签

  1. <!--plugins标签,用于配置MyBatis插件(分页插件)-->
  2. <plugins>
  3.    <plugin interceptor=""></plugin>
  4. </plugins>

8.5 environments标签

  1. <!-- 在environments配置数据库连接信息 -->
  2. <!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
  3. <!-- default属性,用来指定使用哪个environment标签 -->
  4. <environments default="mysql">
  5.    <!-- environment 标签用于配置数据库连接信息 -->
  6.    <environment id="mysql">
  7.        <!--transactionManager标签用于配置数据库管理方式
  8.            type="JDBC" 可以进行事务的提交和回滚操作
  9.            type="MANAGED" 依赖容器完成事务管理,本身不进行事务的提交和回滚操作 -->
  10.        <transactionManager type="JDBC"></transactionManager>
  11.        <!--dataSource标签就是用来配置数据库连接信息 POOLED|UNPOOLED -->
  12.        <dataSource type="POOLED">
  13.            <property name="driver" value="${mysql_driver}"/>
  14.            <property name="url" value="${mysql_url}"/>
  15.            <property name="username" value="${mysql_username}"/>
  16.            <property name="password" value="${mysql_password}"/>
  17.        </dataSource>
  18.    </environment>
  19. </environments>
  20. <!--连接池配置--->
  21. <environments default="mysql">
  22.    <environment id="mysql">
  23.        <transactionManager type="JDBC"></transactionManager>
  24.        <!--   POOLED 使用MyBatis内置的连接池实现   -->
  25.        <!-- mybatis需要一个连接池工厂,这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
  26.        <dataSource type="com.qfedu.utils.DruidDataSourceFactory">
  27.            <property name="driverClass" value="${driver}"/>
  28.            <property name="jdbcUrl" value="${url}"/>
  29.            <property name="username" value="${username}"/>
  30.            <property name="password" value="${password}"/>
  31.        </dataSource>
  32.    </environment>
  33. </environments>

8.6 mappers标签

加载映射配置(映射文件、DAO注解)

  1. <!--mappers标签用于载入映射文件-->
  2. <mappers>
  3.    <mapper resource="mappers/StudentMapper.xml"></mapper>
  4. </mappers>

九、映射文件

9.1 MyBatis Mapper初始化

XML文件解析:读取xml文件中的标签配置封装到Java对象中

9.2 mapper根标签

mapper文件相当于DAO接口的‘实现类’,namespace属性要指定实现DAO接口的全限定名

9.3 insert标签

声明添加操作(sql: insert ...)

常用属性

id属性,绑定对应DAO接口中的方法

parameterType属性,用以指定接口中对应方法的参数类型(可省略)

useGeneratedKeys属性, 设置添加操作是否需要回填生成的主键

keyProperty属性,指定回填的id设置到参数对象中的哪个属性

timeout属性,设置此操作的超时时间,如果不设置则一直等待

主键回填

  1. <insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId">
  2.   insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
  3.   values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
  4. </insert>
  5. <insert id="insertStudent" >
  6.    <selectKey keyProperty="stuId" resultType="java.lang.Integer">
  7.       select last_insert_id()
  8.    </selectKey>
  9.   insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
  10.   values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
  11. </insert>

9.4 delete标签

声明删除操作

9.5 update标签

声明修改操作

9.6 select标签

声明查询操作

  • id属性, 指定绑定方法的方法名

  • parameterType属性,设置参数类型

  • resultType属性,指定当前sql返回数据封装的对象类型(实体类)

  • resultMap属性,指定从数据表到实体类的字段和属性的对应关系

  • useCache属性,指定此查询操作是否需要缓存

  • timeout属性,设置超时时间 数据连接数目达到最大

9.7 resultMap标签

  1. <!-- resultMap标签用于定义实体类与数据表的映射关系(ORM) -->
  2. <resultMap id="studentMap" type="Student">
  3.    <id column="sid" property="stuId"/>
  4.    <result column="stu_num" property="stuNum"/>
  5.    <result column="stu_name" property="stuName"/>
  6.    <result column="stu_gender" property="stuGender"/>
  7.    <result column="stu_age" property="stuAge"/>
  8. </resultMap>

9.8 cache标签

设置当前DAO进行数据库操作时的缓存属性设置

<cache type="" size="" readOnly="false"/>

9.9 sql和include

SQL片段,相同SQL语句片段的封装,供其他SQL语句引用

  1. 公共查询语句
  2. <sql id="wanglaoji">sid , stu_num , stu_name , stu_gender , stu_age</sql>
  3. 引用公共查询语句
  4. <select id="listStudents" resultMap="studentMap">
  5.   select <include refid="wanglaoji"/> from tb_students
  6. </select>

十、分页插件

分页插件是一个独立于MyBatis框架之外的第三方插件;

10.1 添加分页插件的依赖

PageHelper

  1. <!-- pagehelper分页插件 -->
  2. <dependency>
  3.    <groupId>com.github.pagehelper</groupId>
  4.    <artifactId>pagehelper</artifactId>
  5.    <version>5.1.10</version>
  6. </dependency>

10.2 配置插件

在mybatis的主配置文件mybatis-config.xml中通过plugins标签进行配置

  1. <!--plugins标签,用于配置MyBatis插件(分页插件)-->
  2. <plugins>
  3.    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
  4. </plugins>

10.3 分页实例

对学生信息进行分页查询

  1. @Test
  2. public void testListStudentsByPage() {
  3.    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class); //sqlSession
  4.    PageHelper.startPage(2,4);
  5.    //查询所有
  6.    List<Student> students = studentDAO.listStudents();
  7.    PageInfo<Student> pageInfo = new PageInfo<Student>(students);
  8.    //pageInfo中就包含了数据及分页信息
  9. }

带条件分页

  1. @Test
  2. public void testListStudentsByPage() {
  3.    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class); //sqlSession
  4.    PageHelper.startPage(2,4);
  5.    //List<Student> students = studentDAO.listStudents();查询所有女生信息
  6.    List<Student> list = studentDAO.listStudentsByGender("女");
  7.    PageInfo<Student> pageInfo = new PageInfo<Student>(list);
  8.    //pageInfo中就包含了数据及分页信息
  9. }

十一、关联映射

11.1 实体关系

实体——数据实体,实体关系指的就是数据与数据之间的关系

例如:用户和角色、房屋和楼栋、订单和商品

实体关系分为以下四种:

一对一关联

实例:人和身份证、学生和学生证、用户基本信息和详情

数据表关系:

  • 主键关联(用户表主键 和详情主键相同时,表示是匹配的数据)

  • 唯一外键关联

一对多关联多对一关联

实例:

  • 一对多: 班级和学生 、 类别和商品、楼栋和房屋

  • 多对一:学生和班级 、 商品和类别

数据表关系:

  • 在多的一端添加外键和一的一段进行关联

多对多关联

实例:用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品

数据表关系:建立第三张关系表添加两个外键分别与两张表主键进行关联

用户(user_id) 用户角色表(uid,rid) 角色(role_id)

11.2 创建项目,部署MyBatis框架

  • 创建web项目(maven)

    1. <!-- 添加web依赖 -->
    2. <dependency>
    3.    <groupId>javax.servlet</groupId>
    4.    <artifactId>jsp-api</artifactId>
    5.    <version>2.0</version>
    6.    <scope>provided</scope>
    7. </dependency>
    8. <dependency>
    9.    <groupId>javax.servlet</groupId>
    10.    <artifactId>javax.servlet-api</artifactId>
    11.    <version>4.0.1</version>
    12.    <scope>provided</scope>
    13. </dependency>
     
  • 添加完web依赖之后,依赖需要在服务器运行,关于如何在Idea配置服务器,在之前JavaWeb的项目(忘了)某一个笔记中有详细解答

  • 部署MyBatis框架

    • 添加依赖

    1. <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    2. <dependency>
    3.    <groupId>org.mybatis</groupId>
    4.    <artifactId>mybatis</artifactId>
    5.    <version>3.4.6</version>
    6. </dependency>
    7. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    8. <dependency>
    9.    <groupId>mysql</groupId>
    10.    <artifactId>mysql-connector-java</artifactId>
    11.    <version>5.1.47</version>
    12. </dependency>
    • 配置文件

    • 帮助类

    1. public class MyBatisUtil {
    2.    private static SqlSessionFactory factory;
    3.    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
    4.    static{
    5.        try {
    6.            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    7.            factory = new SqlSessionFactoryBuilder().build(is);
    8.       } catch (IOException e) {
    9.            e.printStackTrace();
    10.       }
    11.   }
    12.    public static SqlSessionFactory getSqlSessionFactory(){
    13.        return factory;
    14.   }
    15.    public static SqlSession getSqlSession(boolean isAutoCommit){
    16.        SqlSession sqlSession = local.get();
    17.        if(sqlSession == null){
    18.            sqlSession = factory.openSession(isAutoCommit);
    19.            local.set(sqlSession);
    20.       }
    21.        return sqlSession;
    22.   }
    23.    public static SqlSession getSqlSession(){
    24.        return getSqlSession(false);
    25.   }
    26.    public static <T extends  Object>T getMapper(Class<T> c){
    27.        SqlSession sqlSession = getSqlSession(true);
    28.        return sqlSession.getMapper(c);
    29.   }
    30. }

11.3 一对一关联

实例:用户---详情

11.3.1 创建数据表

  1. -- 用户信息表
  2. create table users(
  3.   user_id int primary key auto_increment,
  4.   user_name varchar(20) not null unique,
  5.   user_pwd varchar(20) not null,
  6.   user_realname varchar(20) not null,
  7.   user_img varchar(100) not null
  8. );
  9. -- 用户详情表
  10. create table details(
  11. detail_id int primary key auto_increment,
  12.   user_addr varchar(50) not null,
  13.   user_tel char(11) not null,
  14.   user_desc varchar(200),
  15. uid int not null unique
  16.    -- constraint FK_USER foreign key(uid) references users(user_id)//这是物理关联,因为建立物理关联,信息表删除数据必须首先删除详情表中的数据,一般开发中不适用物理关联,使用唯一键
  17. );

11.3.2 创建实体类

  • User

    1. @Data
    2. @NoArgsConstructor
    3. @AllArgsConstructor
    4. @ToString
    5. public class User {
    6.    private int userId;
    7.    private String userName;
    8.    private String userPwd;
    9.    private String userRealname;
    10.    private String userImg;
    11. }

  • Detail

    1. @Data
    2. @NoArgsConstructor
    3. @AllArgsConstructor
    4. @ToString
    5. public class Detail {
    6.    private int detailId;
    7.    private String userAddr;
    8.    private String userTel;
    9.    private String userDesc;
    10.    private int userId;
    11. }

11.3.3 添加操作(事务)

测试代码

11.3.4 一对一关联查询

在查询用户的同时关联查询出与之对应的详情

实体

UserDetail

映射文件

连接查询

子查询 这里的detail查询是在DetailMapper.xml中,user查询是在UserMapper.xml中

<!--子查询,select查询对象,查询结束后返回的对象存储在 property的值对象中,这里也就是detail对象中
    column的值"user_id"是传递给select查询语句的参数值,select依靠这个参数值去做查询判断
-->
<!--这里的column的user_id和detail查询条件里的uid不同的原因, detail查询和user查询是不同的mapper文件,而detail的查询语句对应的函数参数值是uid,所以它应当使用#{uid},这里的column的user_id是把值作为detail查询函数的参数值传递过去了,所以两者是不一样.-->
<association property="detail" select="com.qfedu.dao.DetaukDAO.queryDetailByUid" column="user_id"></association

11.4 一对多关联

案例:班级(1)—学生(n)

11.4.1 创建数据表

  1. -- 创建班级信息表
  2. create table classes(
  3.   cid int primary key auto_increment,
  4.   cname varchar(30) not null unique,
  5.   cdesc varchar(100)
  6. );
  7. -- 创建学生信息表
  8. create table students(
  9.   sid char(5) primary key,
  10.   sname varchar(20) not null,
  11.   sage int not null,
  12.   scid int not null
  13. );

11.4.2 创建实体类

ClazzStudent

11.4.3 关联查询

当查询一个班级的时候, 要关联查询出这个班级下的所有学生

连接查询

连接查询映射配置

子查询

子查询映射配置

11.5 多对一关联

实例:学生(n)—班级(1)

当查询一个学生的时候,关联查询这个学生所在的班级信息

11.5.1 创建实体类

StudentClazz

11.5.2 关联查询

连接查询

连接查询映射配置

子查询

子查询映射配置

11.6 多对多关联

案例:学生(m)—课程(n)

11.6.1 创建数据表

  1. -- 学生信息表(如上)
  2. -- 课程信息表
  3. create table courses(
  4.   course_id int primary key auto_increment,
  5.   course_name varchar(50) not null
  6. );
  7. -- 选课信息表/成绩表(学号、课程号、成绩)
  8. create table grades(
  9.   sid char(5) not null,
  10.   cid int not null,
  11.   score int not null
  12. );

11.6.2 关联查询

查询学生时,同时查询学生选择的课程

StudentCourse

根据课程编号查询课程时,同时查询选择了这门课程的学生

StudentCourse

连接查询映射配置

子查询映射配置

十二、动态SQL

交友网:珍爱网、百合网 筛选心仪对象 性别 年龄 城市 身高

电商:淘宝、京东 筛选商品 羽毛球拍 品牌 价格

方江鹏 性别 女 select * from members where gender='女'

罗彪 性别 女 年龄 18-23 select * from members where gender='女' and age >=18 and age <=23

张三 年龄 城市 select * from members where age >=18 and age <=23 and city=''

用户的筛选条件不同,我们完成筛选执行的SQL也不一样;我们可以通过穷举来一一的完成不同条件的筛选,但是这种实现思路过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件查询。

12.1 什么是动态SQL?

根据查询条件动态完成SQL的拼接

12.2 动态SQL使用案例

案例:心仪对象搜索

12.2.1 创建数据表

  1. create table members(
  2.   member_id int primary key auto_increment,
  3.   member_nick varchar(20) not null unique, <!--昵称-->
  4.   member_gender char(2) not null,
  5.   member_age int not null,
  6.   member_city varchar(30) not null
  7. );

12.2.2 创建实体类

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. @ToString
  5. public class Member {
  6.    private int memberId;
  7.    private String memberNick;
  8.    private String memberGender;
  9.    private int memberAge;
  10.    private String memberCity;
  11. }

12.2.3 创建DAO接口

在DAO接口中定义一个多条件查询的方法

  1. public interface MemberDAO {
  2.    //在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
  3.    //优点:无需单独定义传递查询条件的类
  4.    //缺点:当向Map中存放参数时,key必须与动态sql保持一致()
  5.    //public List<Member> searchMember(HashMap<String,Object> params);
  6.    // 也可以定义专门用于存放查询条件的实体类存放参数
  7.    //优点:设置参数时无需关注属性名
  8.    //缺点:需要单独定义一个类来封装参数
  9.    public List<Member> searchMember(MemberSearchCondition params);
  10. }

12.3 if

  1. <resultMap id="memberMap" type="Member">
  2.    <id column="member_id" property="memberId"/>
  3.    <result column="member_nick" property="memberNick"/>
  4.    <result column="member_gender" property="memberGender"/>
  5.    <result column="member_age" property="memberAge"/>
  6.    <result column="member_city" property="memberCity"/>
  7. </resultMap>
  8. <select id="searchMember" resultMap="memberMap">
  9.   select member_id,member_nick,member_gender,member_age,member_city
  10.   from members
  11.   where 1=1
  12.    <if test="gender != null">  <!--gender 就是参数对象的属性/参数Map的key-->
  13.       and member_gender=#{gender}
  14.    </if>
  15.    <if test="minAge != null">
  16.       and member_age &gt;= #{minAge}   <!-- &gt; -->
  17.    </if>
  18.    <if test="maxAge != null">
  19.       and member_age &lt;= #{maxAge}  <!-- &lt; -->
  20.    </if>
  21.    <if test="city != null">
  22.       and member_city = #{city}
  23.    </if>
  24. </select>

测试

  1. @Test
  2. public void testSearchMember() {
  3.    HashMap<String,Object> params = new HashMap<String, Object>();
  4.    params.put("gender","女");
  5.    params.put("minAge",18);
  6.    //params.put("maxAge",23);
  7.    params.put("city","武汉");
  8.    //-----------------------------------------------------------------------
  9.    MemberSearchCondition params2 = new MemberSearchCondition();
  10.    params2.setGender("女");
  11.    //params2.setMinAge(21);
  12.    //params2.setMaxAge(30);
  13.    //params2.setCity("武汉");
  14.    //==========================================================================
  15.    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
  16.    List<Member> members = memberDAO.searchMember(params2);
  17.    for (Member m: members) {
  18.        System.out.println(m);
  19.   }
  20. }

12.4 where

  1. <select id="searchMember" resultMap="memberMap">
  2.   select member_id,member_nick,member_gender,member_age,member_city
  3.   from members
  4.    <where>
  5.        <if test="gender != null">  <!--gender 就是参数对象的属性/参数Map的key-->
  6.           and member_gender=#{gender}
  7.        </if>  
  8.        <if test="minAge != null">
  9.           and member_age &gt;= #{minAge}   <!-- &gt; -->
  10.        </if>
  11.        <if test="maxAge != null">
  12.           and member_age &lt;= #{maxAge}  <!-- &lt; -->
  13.        </if>
  14.        <if test="city != null">
  15.           and member_city = #{city}
  16.        </if>
  17.    </where>
  18.   order by member_age
  19. </select>
  20. <!--此where用法和下面trim用法一样,这里的where把SQL后缀语句直接添加在<where>标签后-->

12.5 trim

  1. <select id="searchMember" resultMap="memberMap">
  2.   select member_id,member_nick,member_gender,member_age,member_city
  3.   from members
  4.    <!-- suffix是后缀,在SQL语句后面添加语句
  5. prefix前缀override覆盖,如果第一个条件符合并且有and或者or,则删去and或者or
  6. -->
  7.    <trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
  8.        <if test="gender != null">  <!--gender 就是参数对象的属性/参数Map的key-->
  9.           and member_gender=#{gender}
  10.        </if>
  11.        <if test="minAge != null">
  12.           and member_age &gt;= #{minAge}   <!-- &gt; -->
  13.        </if>
  14.        <if test="maxAge != null">
  15.           and member_age &lt;= #{maxAge}  <!-- &lt; -->
  16.        </if>
  17.        <if test="city != null">
  18.           and member_city = #{city}
  19.        </if>
  20.    </trim>
  21. </select>

12.6 foreach

  1. public interface MemberDAO {
  2.    //查询指定城市的会员
  3.    public List<Member> searchMemberByCity(List<String> cities);
  4. }
  1. <select id="searchMemberByCity" resultMap="memberMap">
  2.   select member_id,member_nick,member_gender,member_age,member_city
  3.   from members where member_city in
  4.    <!--   指定集合类型           集合中元素别名       多个城市用分隔符","分开   前后加括号形成(武汉,宜昌)的格式-->
  5.    <foreach collection="list"   item="cityName"          separator=","       open="("    close=")">
  6.       #{cityName}
  7.    </foreach>
  8. </select>

测试

  1. @Test
  2. public void searchMemberByCity() {
  3.    List<String> cities = new ArrayList<String>();
  4.    cities.add("厦门");
  5.    cities.add("宜昌");
  6.    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
  7.    List<Member> members = memberDAO.searchMemberByCity(cities);
  8.    for (Member m: members) {
  9.        System.out.println(m);
  10.   }
  11. }

十三、模糊查询

案例:根据昵称查询会员信息(模糊匹配 like)

13.1 模糊查询实现

13.1.1 DAO

  1. public interface MemberDAO {
  2.    // 根据昵称查询用户信息——模糊查询
  3.    // 模糊查询需要使用${}取值,与sql进行拼接
  4.    // 在使用${}时,如果是一个对象,则写入对象属性名称即可
  5.    // 在使用${}时,即使只有一个参数也需要使用@Param注解声明参数的key(非String对象参数可以不用声明)
  6.    public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
  7. }

13.1.2 映射文件

  1. <!--如果参数时String类型,需要parameterType声明参数类型-->
  2. <select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
  3.   select member_id,member_nick,member_gender,member_age,member_city
  4.   from members
  5.   where member_nick like '%${keyWord}%'
  6. </select>

13.1.3 测试

  1. @Test
  2. public void testSearchMemberByNick(){
  3.    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
  4.    List<Member> members = memberDAO.searchMemberByNick("花");
  5.    for (Member m: members) {
  6.        System.out.println(m);
  7.   }
  8. }

13.2 #{}和${}的区别

  • ${key} 表示获取参数,先获取参数的值拼接到SQL语句中,再编译执行SQL语句;可能引起SQL注入问题

  • #{key} 表示获取参数,先完成SQL编译(预编译),预编译之后再将获取的参数设置到SQL与中 ,可以避免SQL注入问题

十四、MyBatis日志配置

MyBatis做为一个封装好的ORM框架,其运行过程我们没办法跟踪,为了让开发者了解MyBatis执行流程及每个执行步骤所完成的工作,MyBatis框架本身支持log4j日志框架,对运行的过程进行跟踪记录。我们只需对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。

14.1 添加日志框架依赖

  1. <!-- https://mvnrepository.com/artifact/log4j/log4j -->
  2. <dependency>
  3.    <groupId>log4j</groupId>
  4.    <artifactId>log4j</artifactId>
  5.    <version>1.2.17</version>
  6. </dependency>

14.2 添加日志配置文件

  • 在resources目录下创建名为 log4j.properties文件

  • log4j.properties文件配置日志输出的方式

# 声明日志的输出级别及输出方式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式  %t 表示线程名称  %5p 日志级别 %msg日志信息
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %msg \:%m%n

14.3 日志信息的级别

在使用日志框架输出日志信息的时候,会根据输出的日志信息的重要程度分为5个级别

级别说明
DEBUG输出调试信息
INFO输出提示信息
WARN输出警告信息
ERROR一般性错误信息
FATAL致命性错误信息

十五、配置数据库连接池-整合Druid

MyBatis做为一个ORM框架,在进行数据库操作时是需要和数据库连接连接的,MyBatis支持基于数据库连接池的连接创建方式。

当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为POOLED时,就可以使用MyBatis内置的连接池管理连接。

如果我们想要使用第三方的数据库连接池,则需进行自定义配置。

15.1 常见的连接池

  • DBCP

  • C3P0

  • Druid 性能也比较好,提供了比较便捷的监控系统

  • Hikari 性能最好

功能dbcpdruidc3p0HikariCP
是否支持PSCache
监控jmxjmx/log/httpjmx,logjmx
扩展性
sql拦截及解析支持
代码简单中等复杂简单
更新时间2015.8.62015.10.102015.12.092015.12.3
特点依赖于common-pool阿里开源,功能全面历史久远,代码逻辑复杂,且不易维护优化力度大,功能简单,起源于boneCP
连接池管理LinkedBlockingDeque数组threadlocal+CopyOnWriteArrayList

15.2 添加Druid依赖

  1. <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
  2. <dependency>
  3.    <groupId>com.alibaba</groupId>
  4.    <artifactId>druid</artifactId>
  5.    <version>1.2.5</version>
  6. </dependency>

15.3 创建Druid连接池工厂

  1. public class DruidDataSourceFactory extends PooledDataSourceFactory {
  2.    <!--实际上继承了一个成员变量DataSource,这里的构造方法是把父类的(或者继承来的)DataSource赋值为 DruidDataSource。从而供PooledDataSourceFactory使用,其实省略了一个方法是getDataSource(),这个方法也是继承来的。可以返回DataSource,此时DataSource通过底下的构造方法已经变为德鲁伊的DataSource,这样就可以实现使用Druid的数据源-->
  3.    public DruidDataSourceFactory() {
  4.        this.dataSource = new DruidDataSource();
  5.   }
  6. }

15.4 将DruidDataSourceFactory配置给MyBatis数据源

  1. <environments default="mysql">
  2.    <environment id="mysql">
  3.        <transactionManager type="JDBC"></transactionManager>
  4.        <!--   POOLED 使用MyBatis内置的连接池实现   -->
  5.        <!-- mybatis需要一个连接池工厂,这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
  6.        <dataSource type="com.qfedu.utils.DruidDataSourceFactory">
  7.            <property name="driverClass" value="${driver}"/>
  8.            <property name="jdbcUrl" value="${url}"/>
  9.            <property name="username" value="${username}"/>
  10.            <property name="password" value="${password}"/>
  11.        </dataSource>
  12.    </environment>
  13. </environments>

十六、MyBatis缓存

MyBatis是基于JDBC的封装,使数据库操作更加便捷;MyBatis除了对JDBC操作步骤进行封装之外也对其性能进行了优化:

  • 在MyBatis引入缓存机制,用于提升MyBatis的检索效率

  • 在MyBatis引入延迟加载机制,用于减少对数据库不必要的访问

16.1 缓存的工作原理

缓存,就是存储数据的内存

16.2 MyBatis缓存

MyBatis缓存分为一级缓存和二级缓存

16.2.1 一级缓存

一级缓存也叫做SqlSession级缓存,为每个SqlSession单独分配的缓存内存,无需手动开启可直接使用;多个SqlSession的缓存是不共享的。

特性:

1.如果多次查询使用的是同一个SqlSession对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存中存储的数据;

2.如果第一次查询完成之后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致;

3.当我们进行在查询时想要跳过缓存直接查询数据库,则可以通过sqlSession.clearCache();来清除当前SqlSession的缓存;

4.如果第一次查询之后第二次查询之前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库。

测试代码:

  1. @Test
  2. public void testQueryMemberById(){
  3.    SqlSession sqlSession1 = MyBatisUtil.getSqlSessionFactory().openSession();
  4.    SqlSession sqlSession2 = MyBatisUtil.getSqlSessionFactory().openSession();
  5.    MemberDAO memberDAO1 = sqlSession1.getMapper(MemberDAO.class);
  6.    Member member1 = memberDAO1.queryMemberById(1);
  7.    System.out.println(member1);
  8.    member1.setMemberAge(99);
  9.    sqlSession1.clearCache(); //清除缓存
  10.    System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  11.    MemberDAO memberDAO2 = sqlSession1.getMapper(MemberDAO.class);
  12.    Member member2 =memberDAO2.queryMemberById(1);
  13.    System.out.println(member2);
  14. }

16.2.2 两次查询与数据库数据不一致问题

16.2.3 二级缓存

二级缓存也称为SqlSessionFactory级缓存,通过同一个factory对象获取的Sqlsession可以共享二级缓存;在应用服务器中SqlSessionFactory是单例的,因此我们二级缓存可以实现全局共享。

特性:

1.二级缓存默认没有开启,需要在mybatis-config.xml中的settings标签开启

2.二级缓存只能缓存实现序列化接口的对象

  • 在mybatis-config.xml开启使用二级缓存

  1. <settings>
  2.    <setting name="cacheEnabled" value="true"/>
  3. </settings>
  • 在需要使用二级缓存的Mapper文件中配置cache标签使用功能二级缓存

<cache/>
  • 被缓存的实体类实现序列化接口

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. @ToString
  5. public class Member implements Serializable {
  6.    private int memberId;
  7.    private String memberNick;
  8.    private String memberGender;
  9.    private int memberAge;
  10.    private String memberCity;
  11. }
  • 测试

  1. @Test
  2. public void testQueryMemberById(){
  3.    SqlSessionFactory factory =MyBatisUtil.getSqlSessionFactory();
  4.    // 1.多个SqlSession对象必须来自于同一个SqlSessionFactory
  5.    SqlSession sqlSession1 = factory.openSession(true);
  6.    SqlSession sqlSession2 = factory.openSession(true);
  7.    System.out.println(sqlSession1 == sqlSession2);
  8.    MemberDAO memberDAO1 = sqlSession1.getMapper(MemberDAO.class);
  9.    Member member1 = memberDAO1.queryMemberById(1);
  10.    System.out.println(member1);
  11.    sqlSession1.commit();  
  12.    //2.第一次查询之后执行sqlSession1.commit(),会将当前sqlsession的查询结果缓存到二级缓存
  13.    System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  14.    MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
  15.    Member member2 =memberDAO2.queryMemberById(1);
  16.    System.out.println(member2);
  17. }

16.3 查询操作的缓存开关

  1. <!--使用二级缓存,可以使用useCache来控制是否查询缓存的操作-->
  2. <select id="queryMemberById" resultMap="memberMap" useCache="false">
  3.   select member_id,member_nick,member_gender,member_age,member_city
  4.   from members
  5.   where member_id=#{mid}
  6. </select>
  7. <!--update标签中有个flushCache=""属性 true表示更改完立即刷新缓存中的对象,false是不刷新-->

十七、延迟加载

延迟加载——如果在MyBatis开启了延迟加载,在执行了子查询(至少查询两次及以上)时,默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行;如果无需使用子查询结果,则子查询不会执行.

开启延迟加载:

  1. <resultMap id="classMap" type="Clazz">
  2.    <id column="cid" property="classId"/>
  3.    <result column="cname" property="className"/>
  4.    <result column="cdesc" property="classDesc"/>
  5.    <collection property="stus" select="com.qfedu.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
  6. </resultMap>
  7. <select id="queryClassByCid" resultMap="classMap">
  8.   select cid,cname,cdesc
  9.   from classes
  10.   where cid=#{cid}
  11. </select>

测试代码:

  1. @Test
  2. public void queryClassByCid() {
  3.    ClassDAO classDAO = MyBatisUtil.getMapper(ClassDAO.class);
  4.    Clazz clazz = classDAO.queryClassByCid(1);
  5.    System.out.println(clazz.getClassName());
  6.    System.out.println("-----------------------------------");
  7.    System.out.println(clazz.getStus());
  8. }

运行日志:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/216034
推荐阅读
相关标签
  

闽ICP备14008679号