Mybatis学习

Mybatis学习,第1张

Mybatis 一、简介 1.1、什么是Mybatis

Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。Mybatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。


<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.2version>
dependency>

1.2、持久化

数据持久化就是将程序的数据持久状态和瞬时状态转化的过程

数据库和io文件(持久化)

1.3、持久层

Dao层、Service层、controller层

  1. 完成持久化工作的代码块
  2. 层界限十分明显
1.4、为什么需要mybatis

方便

传统的jdbc代码太复杂。

帮程序员将数据存入数据库,容易上手。

二、第一个mybatis程序

搭建环境–》导入mybatis–》 编写代码–》测试

2.1、搭建数据库 2.2、创建一个模块

1、在resource文件下创建mybatis-config.xml导入头文件


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
configuration>

2、创建SqlSessionFactory(编写工具类)

package com.huang.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //获取sqlSessionFactory对象
            String url="mybatis-config.xml";
            InputStream io = Resources.getResourceAsStream(url);
            sqlSessionFactory=new SqlSessionFactoryBuilder().build(io);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    //创建sqlsession实例
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

3、编写Sql请求

实体类
package com.huang.pojo;

public class User {
    private int uid;
    private String u_name;
    private String u_password;

    public User() {
    }

    public User(int uid, String u_name, String u_password) {
        this.uid = uid;
        this.u_name = u_name;
        this.u_password = u_password;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getU_name() {
        return u_name;
    }

    public void setU_name(String u_name) {
        this.u_name = u_name;
    }

    public String getU_password() {
        return u_password;
    }

    public void setU_password(String u_password) {
        this.u_password = u_password;
    }
}

Dao接口
package com.huang.dao;

import com.huang.pojo.User;

import java.util.List;

public interface UserDao {
    List<User> getAllUser();
}

接口实现类(被mapper.xml替代)

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.huang.dao.UserDao">

    <select id="getAllUser" resultType="com.huang.pojo.User">
        select * from mybatis.user
    select>
mapper>
测试
package com.huang.dao;

import com.huang.pojo.User;
import com.huang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserTest {
    @Test
    public void test01(){
        //获得sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一getMapper:
//        UserDao mapper = sqlSession.getMapper(UserDao.class);
//        List allUser = mapper.getAllUser();

        //方式二:不建议使用
        List<User> allUser = sqlSession.selectList("com.huang.dao.UserDao.getAllUser");
        for (User user:allUser){
            System.out.println(user.toString());
        }
        sqlSession.close();
    }
}

报错:org.apache.ibatis.binding.BindingException: Type interface com.huang.dao.UserDao is not known to the MapperRegistry.

原因: 未在mybatis-config.xml中配置mapper映射

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

报错二:java.lang.ExceptionInInitializerError
at com.huang.dao.UserTest.test01(UserTest.java:14)

原因:out处文件找不到对应文件,文件未导出。

<build>
        <resources>
            <resource>
                <directory>src/main/resourcesdirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>truefiltering>
            resource>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>truefiltering>
            resource>
        resources>
    build>

报错三:Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。

原因:xml文件encoding 设置utf-8无法被读取

解决:

<properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>

三、CRUD

增删改需要进行实物提交

sqlSession。commit();

注意:

写sql的标签不要写错(delete、update、insert)

模糊查询拼接后注入避免用户写入时一些问题

四、配置解析 4.1、核心配置文件(mybatis-config.xml)

4.2、环境变量(environment)

mybatis的事务管理:

1、jdbc(默认)

2、managed

连接池(unpooled|pooled|jnd)

4.3、属性(properties)

可以通过properties来实现引用配置文件


        DOCTYPE configuration
                PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    
    
       <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
       properties>
    
    
<environments default="test">

    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        dataSource>
    environment>

    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        dataSource>
    environment>
environments>

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

configuration>


driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456

配置文件读取优先级:.properties优先级高于配置文件中的properties

4.3、别名(typeAliases)

别名是为了java类型设置一个短的名字。

减少类完全限定名的冗余。

第一种方式:

    <typeAliases>
        <typeAlias type="com.huang.pojo.User" alias="user">typeAlias>
    typeAliases>

--------------------------------------------------------------------------------
  <select id="getAllUser" resultType="user">
        select * from user
    select>

第二种:

    <typeAliases>
        <package name="com.huang.pojo"/>
    typeAliases>
--------------------------------------------------------------------------------
------resultType的首字母小写
  <select id="getAllUser" resultType="user">
        select * from user
   select>

建议使用第二种

第二种如果用自己DIY可以再实体类上加上注解

import org.apache.ibatis.type.Alias;

@Alias("hello")
public class User {
    private int uid;
    private String u_name;
    private String u_password;
}
--------------------------
      <select id="getAllUser" resultType="hello">
        select * from user
   </select>

4.4、设置

4.5、其他设置

4.6、映射器(注册绑定我的mapper文件)

可能遇到的问题:

采用class和包扫描都必须保证接口和mapper配置文件名称一致,并且在同一个包下面。

五、作用域和生命周期

生命周期和作用域非常重要,错误的生命周期会导致非常严重的并发问题!!!

SqlSessionFactoryBuilder(局部变量):

1、一旦创建了SqlSessionFactoryBuilder,就不在需要他。

SqlSessionFactory(全局作用域):

1、就是数据连接池

2、一旦创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者创建一个新的实例。

3、最简单就是使用单例模式或者静态单例模式

SqlSession:

1、连接到连接池的一个请求! --> 关闭

2、用完后需要赶紧关闭,否则资源被占用!

六、解决属性名和字段名不一致问题 6.1、结果集映射ResultMap
    
    <resultMap id="userMapper" type="User">
        <result column="uid" property="uid">result>
        <result column="u_name" property="u_name">result>
        <result column="u_password" property="pwd">result>
    resultMap>


    <select id="getAllUser" resultMap="userMapper">
        select * from user
    select>

将数据库中的字段和实体类属性名对应

cloumn是数据库字段,property是实体类属性名

七、日志 7.1、日志工厂(设置Setting)

控制台输出日志:

 
	


7.2、log4j

1、导包

<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>

2、log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/huang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG


3、配置xml

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

如何使用:

static Logger logger=Logger.getLogger(UserTest.class);


		logger.info("进入info");
        logger.debug("进入debug");
        logger.error("进入error");

8、分页 8.1、limit分页
 //接口
	List<User> getLimitUsers(Map<String,Integer> map);

//mapper
    <select id="getLimitUsers" resultMap="userMapper" parameterType="map">
        select * from user limit #{startPage},#{pageSize}
    </select>
//Test
     @Test
    public void getLimitUsers(){
        SqlSession session=MybatisUtils.getSqlSession();
        UserDao mapper=session.getMapper(UserDao.class);
        HashMap<String,Integer> map=new HashMap<String, Integer>();
        map.put("startPage",0);
        map.put("pageSize",2);
        List<User> limitUsers = mapper.getLimitUsers(map);
        for (User user:limitUsers){
            System.out.println(user.toString());
        }
    }

8.2、RowBounds分页(java来实现查询到的所有数据进行管理)
List<User> getUsersByRowBounds(); //接口

//mapper
<select id="getUsersByRowBounds" resultMap="userMapper">
        select * from user
    select>


    @Test
    public void getUserByRowBounds(){
        SqlSession session=MybatisUtils.getSqlSession();
        RowBounds rowBounds = new RowBounds(1, 2);
        List<User> userList = session.selectList("com.huang.dao.UserDao.getUsersByRowBounds",null,rowBounds);
        //rowbounds


        for (User user:userList){
            System.out.println(user.toString());
        }
        session.close();

    }

8.3、分页插件

9、注解开发 9.1、面向接口编程
    @Select("select * from user")
    List<User> getAllUser();

<mappers>
    <mapper class="com.huang.dao.UserDao"/>
mappers>

    @Test
    public  void test06(){
        SqlSession session=MybatisUtils.getSqlSession();
        UserDao mapper = session.getMapper(UserDao.class);
        List<User> allUser = mapper.getAllUser();
        for (User u:allUser){
            System.out.println(u.toString());
        }
    }

10、mybatis原理

11、使用注解开发(CRUD)

1、开启事务自动提交:

 //重载方法填写true时将自动提交事务
        return sqlSessionFactory.openSession(true);

2、填写接口

@Insert("insert into user (u_name,u_password) values (#{u_name},#{pwd})")
    void  insertUser(User user);

可以再数据类型前面加@Param(“dsad”)来最定义传值   和#{}里的值对应

3、测试

   @Test
    public  void test06(){
        SqlSession session=MybatisUtils.getSqlSession();
        UserDao mapper = session.getMapper(UserDao.class);
        User user=new User();
        user.setUid(0);
        user.setU_name("sss");
        user.setU_password("123");
        mapper.insertUser(user);
        session.close();
    }

重点#{} 和 &KaTeX parse error: Expected 'EOF', got '#' at position 7: {}的区别:#̲{}不可以sql注入,{}可以进行sql拼接

12、Lombok

1、下载插件

2、导包

<dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.10version>
dependency>

3、使用(在类上添加注解即可)

@Data
@ToString
public class User implements  Cloneable{
    private int uid;
    private String u_name;
    private String pwd;
    //java设计模式:原型模式
    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            Object user =super.clone();
            return user;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

13、多对一

多对一实体类:

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private int uid;
    private String u_name;
    private String pwd;
    private Teacher teacher;
    //深度克隆
    @Override
    protected User clone() throws CloneNotSupportedException {
        try {
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(bis);
            return  (User)objectInputStream.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}



@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Teacher  implements Serializable {
    private int tid;
    private String tname;
    private String tage;
}




1、子查询
    List<User> getUser();

    <resultMap id="userMapper" type="User">
            <result column="uid" property="uid">result>
            <result column="u_name" property="u_name">result>
            <result column="u_password" property="pwd">result>
            <association property="teacher" column="tid" javaType="Teacher" 	             select="getTeacher"/>
    resultMap>



    <select id="getUser" resultMap="userMapper">
        select * from user
    select>


    <select id="getTeacher" resultType="com.huang.pojo.Teacher">
         select * from teacher where tid=2
    select>


2、联表查询
    List<User> getUser2();

    <select id="getUser2" resultMap="userMapper2">
        select u.uid as uid,u.u_name as uname,t.tname as tname
        from  user u, teacher t
        where u.tid=t.tid
    select>


    <resultMap id="userMapper2" type="User">
        <result property="uid" column="uid" />
        <result property="u_name" column="uname"/>
        <association property="teacher" javaType="Teacher">
            <result property="tname" column="tname"/>
        association>
    resultMap>

14、一对多

一对多实体类:

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private int uid;
    private String u_name;
    private String pwd;
    private int tid;
    //深度克隆
    @Override
    protected User clone() throws CloneNotSupportedException {
        try {
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(bis);
            return  (User)objectInputStream.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}


@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Teacher  implements Serializable {
    private int tid;
    private String tname;
    private String tage;
    private List<User> users;
}


1、处理结果(嵌套结果)
List<Teacher> getTeacher(@Param("tid") int tid);

    <select id="getTeacher" resultMap="teacherMapper1">
        select u.uid as uid,u.u_name as uname,t.tname as tname,t.tid as tid
        from  user u, teacher t
        where u.tid=t.tid and t.tid=#{tid}
    select>

    <resultMap id="teacherMapper1" type="Teacher">
        <result property="tid" column="tid" />
        <result property="tname" column="tname"/>
        <collection property="users" ofType="User">
            <result property="uid" column="uid"/>
            <result property="u_name" column="uname"/>
            <result property="tid" column="tid" />
        collection>
    resultMap>

2、处理查询(嵌套查询语句)
List<Teacher> getTeacher2(@Param("tid") int tid);

    <select id="getTeacher2" resultMap="teacherMapper2">
        select * from teacher where tid=#{tid}
    select>
    <resultMap id="teacherMapper2" type="Teacher">
        <result property="tid" column="tid"/>
        <collection property="users" javaType="ArrayList" ofType="User" select="getUserByTid" column="tid"/>
    resultMap>
    <select id="getUserByTid" resultType="User">
        select * from user where tid=#{tid}
    select>

小结:

1、关联 associate 【多对一】

2、结合 collection【一对多】

3、javaType :用来实体类中返回属性的类型 例如集合为ArrayList ,以及其他引用类型: String …

4、ofType :用来约束泛型中的类型

面试高频:

mysql 引擎

innodb 底层原理

索引

索引优化

15、动态SQl

什么是动态sql就是指,根据不同的条件生成不同的sql语句。

1、if
    <select id="selectBlog" parameterType="map" resultType="Blog">
        select * from blog where author='hello'
        <if test="title != null">
        and title = #{title}
        if>
    select>

2、choose=switch
    <select id="selectBlogChoose" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <choose>
                <when test="author !=null and views != null">
                    author = #{author} and views=#{views}
                when>
                <when test="views != null and views ==null">
                    and views=#{views}
                when>
            choose>
        where>

    select>

3、trim
<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">
    
    

4、where(第一个分支不要加and 其他需要)
    <select id="selectBlog" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <if test="title != null">
                and title = #{title}
            if>
        where>

    select>

5、set(最后一个条件不用加逗号)
    <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title != null">
                title = #{title} ,
            if>
            <if test="author != null">
                author = #{author}
            if>
            where  id =#{id}
        set>
    update>

6、forEach(list内容都不满足的情况下查询所有)
    <select id="selectBlog2" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id=#{id}
            foreach>
        where>
    select>

7、SQL片段(不要套where)
    <sql id="select-t-a">
        <if test="title != null">
            title = #{title}
        if>
        <if test="author != null">
            and author = #{author}
        if>
    sql>
    <select id="selectBlog" parameterType="map" resultType="Blog">
        select * from blog
        <where>
            <include refid="select-t-a">include>
        where>
    select>

14、缓存 1、简介

把频繁查询不经常修改的数据进行存储,提高查询效率

2、一级缓存又称本地缓存(SQlSession,只在一次会话中存在close后就消失)

默认开启的(再一次session会话中 查询相同对象是同一个地址缓存)

    @org.junit.Test
    public void Test2(){
        SqlSession session= MybatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map map=new HashMap();
//        map.put("title","java");
        map.put("author","hello123321");
        //手动清楚缓存session.clearCache
        ArrayList<Blog> blogs = mapper.selectBlog(map);
        for (Blog b:blogs){
            System.out.println(b.toString());
        }
        ArrayList<Blog> blogs2 = mapper.selectBlog(map);
        System.out.println(blogs2==blogs);
        session.close();
    }

影响缓存的点:

1、写入 *** 作会刷新缓存,查询后的2次数据指定地址不同 返回false

2、手动开启缓存清理session.clearCache

3、查询不同的数据

3、二级缓存

开启二级缓存后,首先还是把查询的数据放在一级缓存中,当一级缓存关闭后才会把数据放入二级缓存

1、开启二级缓存

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

        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>

2、对应mapper.xml中配置缓存

<cache size="512" eviction="FiFo" flushInterval="60000" readOnly="true"/>


//个人定制是否是用缓存
在标签中加入   useCache="false"


3、测试

        @org.junit.Test
        public void Test2(){
            SqlSession session= MybatisUtils.getSqlSession();
            SqlSession session2= MybatisUtils.getSqlSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            BlogMapper mapper2 = session2.getMapper(BlogMapper.class);
            Map map=new HashMap();
    //        map.put("title","java");
            map.put("author","hello123321");
            ArrayList<Blog> blogs = mapper.selectBlog(map);
            for (Blog b:blogs){
                System.out.println(b.toString());
            }
            session.close();
            ArrayList<Blog> blogs1 = mapper2.selectBlog(map);
            System.out.println(blogs==blogs1);
            session2.close();
        }

15、缓存原理

16、自定义缓存ehCache

1、resource 中添加ehcache.xml文件自定义缓存模式


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    
    
    
    
    <diskStore path="java.io.tmpdir" />
    
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">

        <persistence strategy="localTempSwap" />
    defaultCache>

    
    <cache name="GoodsType"
           eternal="false"
           timeToIdleSeconds="2400"
           timeToLiveSeconds="2400"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU">
    cache>
ehcache>


<dependency>
            <groupId>org.mybatis.cachesgroupId>
            <artifactId>mybatis-ehcacheartifactId>
            <version>1.2.1version>
dependency>



DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.huang.dao.BlogMapper">
    <cache type="org.mybatis.caches.ehcache.EhcacheCache" />

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/872185.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存