首页 青云排行榜 知识中心 控制台
Java 数据库开发 >  MyBatis > 

Mybatis概述

MyBatis 是一个持久化框架(persistence framework),它简化了对数据库的访问,同时又保留了对 SQL 的控制。它在数据持久化的过程中,避免了大量的 JDBC 代码以及手动设置参数和获取结果的繁琐步骤。相比于 Hibernate 等全自动 ORM 框架,MyBatis 更轻量级且灵活,特别适用于需要手写复杂 SQL 的场景。接下来,我们将详细介绍 MyBatis 的各个方面。


1. MyBatis 简介

1.1 MyBatis 是什么?

MyBatis 是 Apache Software Foundation 旗下的一个项目,用于在 Java 应用中处理持久化层(Persistence Layer)。它通过 XML 或注解的方式将实体类和 SQL 语句进行映射,可以帮助开发者快速进行 CRUD(Create、Read、Update、Delete)操作。

1.2 MyBatis 的特点

轻量级:MyBatis 不需要像 Hibernate 那样的全局配置,配置简单灵活。

SQL 控制:MyBatis 允许开发者手写 SQL,直接与数据库交互,便于处理复杂的 SQL 查询。

可插拔性:MyBatis 可以与其他框架(如 Spring)无缝集成。

动态 SQL:支持动态 SQL 生成,以适应不同的查询条件。

缓存支持:支持一级缓存和二级缓存,提高性能。


2. MyBatis 的基本概念

2.1 SqlSessionFactory

作用:负责创建 SqlSession 实例。

配置:通过 XML 配置文件或 Java 配置类进行构建。

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.2 SqlSession

作用:用于执行 SQL 操作,如查询、插入、更新和删除。

生命周期:应为每次数据库操作创建一个新的 SqlSession,并在操作完成后关闭。

try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUserById(1);
    // 其他操作
    session.commit(); // 提交事务
}

2.3 Mapper 接口

作用:定义数据库操作的接口,MyBatis 自动生成实现类。

配置方式:可以通过 XML 文件或注解进行配置。

public interface UserMapper {
    User selectUserById(int id);
    void insertUser(User user);
    List<User> selectAllUsers();
}

2.4 实体类(POJO)

作用:与数据库表结构相对应的 Java 类。

要求:需要有与数据库字段相对应的属性,以及相应的 getter 和 setter 方法。

public class User {
    private int id;
    private String username;
    private String password;
    private String email;
    
    // getter 和 setter 方法
}

3. MyBatis 配置文件

MyBatis 的配置文件是整个框架的核心,它定义了数据库连接、事务管理、Mapper 映射等。

作用:配置 MyBatis 的全局属性,如数据源、事务管理等。

文件名:通常为 mybatis-config.xml。

3.1 环境配置

配置多个环境(development、test、production):

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="password"/>
        </dataSource>
    </environment>
    <environment id="production">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://production-db:3306/proddb?useSSL=false&serverTimezone=UTC"/>
            <property name="username" value="prod_user"/>
            <property name="password" value="prod_password"/>
        </dataSource>
    </environment>
</environments>

解析

default:指定默认环境。

transactionManager

   JDBC:使用 JDBC 提供的事务管理。

   MANAGED:应用服务器或 Spring 管理事务。

dataSource

   UNPOOLED:每次请求都会创建新连接,适用于简单应用。

   POOLED:使用连接池提高性能,适用于生产环境。

   JNDI:使用应用服务器的数据源。

3.2 Mapper 配置

使用XML配置Mapper

<mappers>
    <mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>

使用注解配置Mapper

<mappers>
    <mapper class="com.example.mapper.UserMapper"/>
</mappers>

自动扫描Mapper包

<mappers>
    <package name="com.example.mapper"/>
</mappers>

3.3 类型别名

类型别名用于简化XML中类的引用,可以为常用类指定简短别名。

<typeAliases>
    <typeAlias type="com.example.entity.User" alias="User"/>
</typeAliases>

可以使用包扫描自动为包下的类生成别名:

<typeAliases>
    <package name="com.example.entity"/>
</typeAliases>

3.4 插件配置

MyBatis 支持插件扩展,可以通过插件实现自定义功能,如日志、分页等。

<plugins>
    <plugin interceptor="com.example.plugin.ExamplePlugin">
        <property name="someProperty" value="100"/>
    </plugin>
</plugins>

3.5 全局属性配置

<settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useGeneratedKeys" value="true"/>
    <setting name="defaultExecutorType" value="SIMPLE"/>
    <setting name="defaultStatementTimeout" value="25000"/>
</settings>

常用全局属性

cacheEnabled:启用或禁用二级缓存。

lazyLoadingEnabled:启用或禁用延迟加载。

useGeneratedKeys:允许JDBC支持生成主键。

defaultExecutorType

   SIMPLE:每次执行都会打开和关闭Statement。

   REUSE:复用Statement。

   BATCH:批量执行Statement。

defaultStatementTimeout:设置超时时间(秒)。

3.6 自定义类型处理器

MyBatis 提供了默认类型处理器,也支持自定义处理器来处理特殊类型。

<typeHandlers>
    <typeHandler javaType="java.util.Date" jdbcType="TIMESTAMP" handler="com.example.handler.DateTypeHandler"/>
</typeHandlers>

自定义类型处理器示例:

package com.example.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;

public class DateTypeHandler extends BaseTypeHandler<Date> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        ps.setTimestamp(i, new Timestamp(parameter.getTime()));
    }
    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Timestamp timestamp = rs.getTimestamp(columnName);
        return timestamp != null ? new Date(timestamp.getTime()) : null;
    }
    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Timestamp timestamp = rs.getTimestamp(columnIndex);
        return timestamp != null ? new Date(timestamp.getTime()) : null;
    }
    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Timestamp timestamp = cs.getTimestamp(columnIndex);
        return timestamp != null ? new Date(timestamp.getTime()) : null;
    }
}

3.7 日志配置

MyBatis 支持多种日志框架,包括 SLF4J、Log4j、Commons Logging、JDK Logging。

使用 SLF4J

<settings>
    <setting name="logImpl" value="SLF4J"/>
</settings>

使用 Log4j

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

可以在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=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n


4. Mapper 文件

Mapper文件是MyBatis中的核心组件,它定义了SQL语句和实体类之间的映射关系。

4.1 Mapper 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.example.mapper.UserMapper">
    <!-- ResultMap 定义 -->
    <resultMap id="userResultMap" type="com.example.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="email" column="email"/>
    </resultMap>

    <!-- SQL 片段 -->
    <sql id="selectAllFields">
        id, username, password, email
    </sql>

    <!-- 查询操作 -->
    <select id="selectUserById" resultMap="userResultMap" parameterType="int">
        SELECT <include refid="selectAllFields"/>
        FROM users
        WHERE id = #{id}
    </select>

    <!-- 插入操作 -->
    <insert id="insertUser" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (username, password, email)
        VALUES (#{username}, #{password}, #{email})
    </insert>

    <!-- 更新操作 -->
    <update id="updateUser" parameterType="com.example.entity.User">
        UPDATE users
        SET username = #{username}, password = #{password}, email = #{email}
        WHERE id = #{id}
    </update>

    <!-- 删除操作 -->
    <delete id="deleteUser" parameterType="int">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

解析

namespace:定义 Mapper 的命名空间,通常与 Mapper 接口的全限定名相同。

resultMap:用于定义结果集与实体类属性的映射关系。

sql:用于定义可重用的 SQL 片段。

selectinsertupdate、delete:分别定义查询、插入、更新、删除操作。

4.2 ResultMap

ResultMap 用于将查询结果与实体类属性进行映射。

<resultMap id="userResultMap" type="com.example.entity.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <result property="email" column="email"/>
</resultMap>

id:映射主键字段。

result:映射非主键字段。

4.3 SQL片段

SQL片段可以提高SQL语句的重用性。

<sql id="selectAllFields">
    id, username, password, email
</sql>

使用 <include> 标签引入 SQL 片段:

<select id="selectUserById" resultMap="userResultMap" parameterType="int">
    SELECT <include refid="selectAllFields"/>
    FROM users
    WHERE id = #{id}
</select>

4.4 动态 SQL

MyBatis 支持动态 SQL,可以根据不同的条件动态生成 SQL 语句。

使用<if>标签

<select id="selectUser" resultMap="userResultMap" parameterType="map">
    SELECT <include refid="selectAllFields"/>
    FROM users
    WHERE 1=1
    <if test="username != null">
        AND username = #{username}
    </if>
    <if test="email != null">
        AND email = #{email}
    </if>
</select>

使用 <choose> 标签

<select id="selectUser" resultMap="userResultMap" parameterType="map">
    SELECT <include refid="selectAllFields"/>
    FROM users
    <where>
        <choose>
            <when test="username != null">
                username = #{username}
            </when>
            <when test="email != null">
                email = #{email}
            </when>
            <otherwise>
                id = #{id}
            </otherwise>
        </choose>
    </where>
</select>

使用 <foreach> 标签

<select id="selectUsersByIds" resultMap="userResultMap" parameterType="list">
    SELECT <include refid="selectAllFields"/>
    FROM users
    WHERE id IN
    <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

使用 <trim> 标签

<trim> 标签用于动态地移除多余的 SQL 关键字,如 AND、OR 等。

<select id="selectUser" resultMap="userResultMap" parameterType="map">
    SELECT <include refid="selectAllFields"/>
    FROM users
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </trim>
</select>

使用<set> 标签

<set> 标签用于动态生成 SET 子句,用于 UPDATE 语句中。

<update id="updateUser" parameterType="com.example.entity.User">
    UPDATE users
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="password != null">
            password = #{password},
        </if>
        <if test="email != null">
            email = #{email}
        </if>
    </set>
    WHERE id = #{id}
</update>

使用 <where> 标签

<where> 标签用于动态生成 WHERE 子句,会自动去除多余的 AND、OR 等关键字。

<select id="selectUser" resultMap="userResultMap" parameterType="map">
    SELECT <include refid="selectAllFields"/>
    FROM users
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </where>
</select>

5. Mapper 接口

Mapper 接口是 MyBatis 的核心组件之一,它定义了数据库操作的方法。

5.1 接口定义

package com.example.mapper;

import com.example.entity.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

    @Insert("INSERT INTO users (username, password, email) VALUES (#{username}, #{password}, #{email})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insertUser(User user);

    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectUserById(@Param("id") int id);

    @Select("SELECT * FROM users")
    List<User> selectAllUsers();

    @Update("UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")
    void updateUser(User user);

    @Delete("DELETE FROM users WHERE id = #{id}")
    void deleteUser(@Param("id") int id);
}

5.2 注解配置

@Insert:定义插入 SQL 语句。

@Select:定义查询 SQL 语句。

@Update:定义更新 SQL 语句。

@Delete:定义删除 SQL 语句。

@Options:用于配置附加选项,如自动生成主键。

@Param:用于指定参数名称,解决参数名不匹配的问题。

5.3 返回值类型

Mapper 接口方法的返回值类型可以是以下几种:

单一对象:查询返回单个结果。

集合:查询返回多个结果。

基本类型:返回单个字段值,如 int、String 等。

void:用于插入、更新、删除操作。

5.4 参数传递

Mapper 接口方法的参数可以是以下几种:

单个参数:

如基本类型、对象。

多个参数:可以使用 @Param 注解指定参数名称。

集合参数:如 List、Map 等。

6. MyBatis 与 Spring 整合

MyBatis 可以与 Spring 框架无缝整合,通过 Spring 管理 MyBatis 的 SqlSessionFactory 和事务。

6.1 Spring 配置文件

数据源配置

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

SqlSessionFactory 配置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath*:com/example/mapper/*.xml"/>
</bean>

Mapper 接口扫描

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

6.2 Spring Java 配置

使用Java配置替代XML配置:

package com.example.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class MyBatisConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/example/mapper/*.xml"));
        return sessionFactory.getObject();
    }
}

6.3 事务管理

配置事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

声明式事务管理

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.mapper.UserMapper;
import com.example.entity.User;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Transactional
    public void createUser(User user) {
        userMapper.insertUser(user);
    }
    @Transactional(readOnly = true)
    public User getUserById(int id) {
        return userMapper.selectUserById(id);
    }
}

7. MyBatis 进阶特性

7.1 缓存机制

MyBatis 提供了一级缓存和二级缓存机制,以提高性能。

一级缓存

   作用范围:SqlSession 级别。

   特性:默认开启,不同 SqlSession 之间不共享。

二级缓存

   作用范围:Mapper 级别。

   特性:需要显式配置,可在不同 SqlSession 之间共享。

 <cache/>

自定义缓存

   MyBatis 支持自定义缓存,可以通过实现 Cache 接口来自定义缓存行为。

package com.example.cache;
import org.apache.ibatis.cache.Cache;
import java.util.concurrent.locks.ReadWriteLock;

public class CustomCache implements Cache {
    private final String id;
    public CustomCache(String id) {
        this.id = id;
    }
    @Override
    public String getId() {
        return this.id;
    }
    @Override
    public void putObject(Object key, Object value) {
        // 自定义缓存实现
    }
    @Override
    public Object getObject(Object key) {
        // 自定义缓存实现
        return null;
    }
    @Override
    public Object removeObject(Object key) {
        // 自定义缓存实现
        return null;
    }
    @Override
    public void clear() {
        // 自定义缓存实现
    }
    @Override
    public int getSize() {
        // 自定义缓存实现
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}

配置自定义缓存:

<cache type="com.example.cache.CustomCache"/>

7.2 分页插件

MyBatis 不支持分页功能,但可以通过插件实现分页。

<plugins>
    <plugin interceptor="com.example.plugin.PaginationInterceptor">
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>

分页插件示例:

package com.example.plugin;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.util.Properties;

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PaginationInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截逻辑
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置属性
    }
}

7.3 拦截器

MyBatis 支持插件机制,可以通过拦截器实现自定义功能,如分页、日志记录等。

自定义拦截器

package com.example.interceptor;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截逻辑
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
        // 设置属性
    }
}

配置自定义拦截器:

<plugins>
    <plugin interceptor="com.example.interceptor.MyInterceptor">
        <property name="propertyName" value="propertyValue"/>
    </plugin>
</plugins>

7.4 日志拦截器

MyBatis 提供了日志拦截器,可以用于记录 SQL 语句执行的详细信息。

配置日志拦截器:

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

使用Log4j记录SQL日志:

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n

7.5 MyBatis Generator

MyBatis Generator 是一个代码生成工具,可以根据数据库表结构生成实体类、Mapper 接口和 Mapper XML 文件。

配置文件示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/testdb"
                        userId="root"
                        password="password"/>
        
        <javaModelGenerator targetPackage="com.example.entity" targetProject="src/main/java"/>
        <sqlMapGenerator targetPackage="com.example.mapper" targetProject="src/main/resources"/>
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"/>

        <table tableName="users" domainObjectName="User"/>
    </context>
</generatorConfiguration>

运行 MyBatis Generator

可以通过命令行或 Maven 插件运行 MyBatis Generator。

命令行运行

java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml -overwrite

Maven 插件运行

在 pom.xml 中添加插件配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.4.0</version>
            <executions>
                <execution>
                    <id>generate-sources</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <verbose>true</verbose>
                <overwrite>true</overwrite>
            </configuration>
        </plugin>
    </plugins>
</build>

执行 Maven 命令:

mvn mybatis-generator:generate

8. 总结

MyBatis 是一款强大的持久层框架,提供了灵活的 SQL 映射和强大的动态 SQL 功能,能够与 Spring 框架无缝整合,支持缓存、插件、拦截器等高级特性,是 Java 开发中常用的持久层解决方案之一。

通过本文档的介绍,您可以掌握 MyBatis 的基本使用方法和高级特性,能够在项目中灵活运用 MyBatis 进行数据库操作。

相关题目 技术能力测评
关于我们
公司简介
联系我们
联系我们
售前咨询: leizhongnan@eval100.com
售后服务: 0755-26415932
商务合作: support@eval100.com
友情链接
金蝶软件
快递100
关注我们
Copyright © 2023-2023 深圳慧题科技有限公司 粤ICP备2023109746号-1 粤公网安备44030002001082