MyBatis 基础知识一解析

不点 阅读:237 2021-03-31 14:14:24 评论:0

第一:MyBatis 框架概述

         mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
         采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

1.1 JDBC 编程

示例代码:

package com.zzg.orm.jdbc; 
 
import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
 
public class JDBCTest { 
 
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		Connection connection = null; 
		PreparedStatement preparedStatement = null; 
		ResultSet resultSet = null; 
		try { 
			// 加载数据库驱动 
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			// 通过驱动管理类获取数据库链接 
			connection = DriverManager.getConnection( 
					"jdbc:mysql://localhost:3306/myblog?useSSL=false&&serverTimezone=UTC&&allowPublicKeyRetrieval=true", 
					"root", "123456"); 
			// 定义 sql 语句 ?表示占位符 
			String sql = "select * from blog_tag where id = ?"; 
			// 获取预处理 statement 
			preparedStatement = connection.prepareStatement(sql); 
			// 设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值 
			preparedStatement.setInt(1, 1); 
			// 向数据库发出 sql 执行查询,查询出结果集 
			resultSet = preparedStatement.executeQuery(); 
			// 遍历查询结果集 
			while (resultSet.next()) { 
				System.out.println(resultSet.getString("id") + "," + resultSet.getString("name")); 
			} 
		} catch (Exception e) { 
			e.printStackTrace(); 
		} finally { 
			// 释放资源 
			if (resultSet != null) { 
				try { 
					resultSet.close(); 
				} catch (SQLException e) { 
					e.printStackTrace(); 
				} 
			} 
			if (preparedStatement != null) { 
				try { 
					preparedStatement.close(); 
				} catch (SQLException e) { 
					e.printStackTrace(); 
				} 
			} 
			if (connection != null) { 
				try { 
					connection.close(); 
				} catch (SQLException e) { 
					// TODO Auto-generated catch block 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
 
} 

JDBC 问题分析:

1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
2、Sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java
代码。
3、使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能
多也可能少,修改 sql 还要修改代码,系统不易维护。
4、对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记
录封装成 pojo 对象解析比较方便。

 

第二:MyBatis 快速入门

2.1 MyBatis 依赖的jar如下:

<!-- mybatis 开发框架 --> 
		<dependency> 
			<groupId>org.mybatis</groupId> 
			<artifactId>mybatis</artifactId> 
			<version>3.4.6</version> 
		</dependency>

核心业务代码:

package com.zzg.jdbc.mybatis.entity; 
 
import java.io.Serializable; 
import java.util.Date; 
 
@SuppressWarnings("serial") 
public class User implements Serializable { 
	private Integer id; 
	private String username; 
	private Date birthday; 
	private String sex; 
	private String address; 
 
	public Integer getId() { 
		return id; 
	} 
 
	public void setId(Integer id) { 
		this.id = id; 
	} 
 
	public String getUsername() { 
		return username; 
	} 
 
	public void setUsername(String username) { 
		this.username = username; 
	} 
 
	public Date getBirthday() { 
		return birthday; 
	} 
 
	public void setBirthday(Date birthday) { 
		this.birthday = birthday; 
	} 
 
	public String getSex() { 
		return sex; 
	} 
 
	public void setSex(String sex) { 
		this.sex = sex; 
	} 
 
	public String getAddress() { 
		return address; 
	} 
 
	public void setAddress(String address) { 
		this.address = address; 
	} 
 
	@Override 
	public String toString() { 
		return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address=" 
				+ address + "]"; 
	} 
 
} 
 
 
package com.zzg.jdbc.mybatis.dao; 
 
import java.util.List; 
 
import com.zzg.jdbc.mybatis.entity.User; 
 
public interface IUserDao { 
	/** 
	* 查询所有用户 
	* @return 
	*/ 
	List<User> findAll(); 
} 
 
 
<?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.zzg.jdbc.mybatis.dao.IUserDao"> 
	<!-- 配置查询所有操作 --> 
	<select id="findAll" resultType="com.zzg.jdbc.mybatis.entity.User"> 
		select * from user 
	</select> 
</mapper> 
 

编写持久层接口映射文件IUserDao .xml 注意事项:

要求:
创建位置:
必须和持久层接口在相同的包中。
名称:必须以持久层接口名称命名文件名,扩展名是.xml

编写SqlMapConfig.xml 配置文件 和测试代码:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE configuration  
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
<configuration> 
	<!-- 配置 mybatis 的环境 --> 
	<environments default="mysql"> 
		<!-- 配置 mysql 的环境 --> 
		<environment id="mysql"> 
			<!-- 配置事务的类型 --> 
			<transactionManager type="JDBC"></transactionManager> 
			<!-- 配置连接数据库的信息:用的是数据源(连接池) --> 
			<dataSource type="POOLED"> 
				<property name="driver" value="com.mysql.cj.jdbc.Driver" /> 
				<property name="url" 
					value="jdbc:mysql://localhost:3306/myblog?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true" /> 
				<property name="username" value="root" /> 
				<property name="password" value="123456" /> 
			</dataSource> 
		</environment> 
	</environments> 
	<!-- 告知 mybatis 映射配置的位置 --> 
	<mappers> 
		<mapper resource="com/zzg/jdbc/mybatis/dao/IUserDao.xml" /> 
	</mappers> 
</configuration>
package com.zzg.jdbc.mybatis.test; 
 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.List; 
 
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 com.zzg.jdbc.mybatis.dao.IUserDao; 
import com.zzg.jdbc.mybatis.entity.User; 
 
public class MybatisTest { 
 
	public static void main(String[] args) throws IOException { 
		//1.读取配置文件 
		InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); 
		//2.创建 SqlSessionFactory 的构建者对象 
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 
		//3.使用构建者创建工厂对象 SqlSessionFactory 
		SqlSessionFactory factory = builder.build(in); 
		//4.使用 SqlSessionFactory 生产 SqlSession 对象 
		SqlSession session = factory.openSession(); 
		//5.使用 SqlSession 创建 dao 接口的代理对象 
		IUserDao userDao = session.getMapper(IUserDao.class); 
		//6.使用代理对象执行查询所有方法 
		List<User> users = userDao.findAll(); 
		for(User user : users) { 
		System.out.println(user); 
		} 
		//7.释放资源 
		session.close(); 
		in.close(); 
 
	} 
 
} 

第三:MyBatis 快速入门(注解版本)

3.1 在持久层接口层添加相关注解

package com.zzg.jdbc.mybatis.dao; 
 
import java.util.List; 
 
import org.apache.ibatis.annotations.Select; 
 
import com.zzg.jdbc.mybatis.entity.User; 
 
public interface IUserDao { 
	/** 
	* 查询所有用户 
	* @return 
	*/ 
	@Select("select * from user") 
	List<User> findAll(); 
} 

3.2 修改SqlMapConfig.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE configuration  
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
<configuration> 
	<!-- 配置 mybatis 的环境 --> 
	<environments default="mysql"> 
		<!-- 配置 mysql 的环境 --> 
		<environment id="mysql"> 
			<!-- 配置事务的类型 --> 
			<transactionManager type="JDBC"></transactionManager> 
			<!-- 配置连接数据库的信息:用的是数据源(连接池) --> 
			<dataSource type="POOLED"> 
				<property name="driver" value="com.mysql.cj.jdbc.Driver" /> 
				<property name="url" 
					value="jdbc:mysql://localhost:3306/myblog?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true" /> 
				<property name="username" value="root" /> 
				<property name="password" value="123456" /> 
			</dataSource> 
		</environment> 
	</environments> 
	<!-- 告知 mybatis 映射配置的位置 --> 
	<mappers> 
		<!-- 基础版本  --> 
		<!-- <mapper resource="com/zzg/jdbc/mybatis/dao/IUserDao.xml" /> --> 
		<!-- 注解版本 --> 
		<mapper class="com.zzg.jdbc.mybatis.dao.IUserDao"/> 
	</mappers> 
</configuration>

注意:在使用基于注解的 Mybatis 配置时,请移除 xml 的映射配置(IUserDao.xml)。 否则会提示如下错误信息:

Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:  
### Error building SqlSession. 
### The error may exist in com/zzg/jdbc/mybatis/dao/IUserDao.xml 
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.zzg.jdbc.mybatis.dao.IUserDao.findAll 
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) 
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80) 
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64) 
	at com.zzg.jdbc.mybatis.test.MybatisAnnotation.main(MybatisAnnotation.java:24) 
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.zzg.jdbc.mybatis.dao.IUserDao.findAll 
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121) 
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:99) 
	at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78) 
	... 2 more 
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.zzg.jdbc.mybatis.dao.IUserDao.findAll 
	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:872) 
	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:844) 
	at org.apache.ibatis.session.Configuration.addMappedStatement(Configuration.java:668) 
	at org.apache.ibatis.builder.MapperBuilderAssistant.addMappedStatement(MapperBuilderAssistant.java:302) 
	at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parseStatement(MapperAnnotationBuilder.java:351) 
	at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse(MapperAnnotationBuilder.java:134) 
	at org.apache.ibatis.binding.MapperRegistry.addMapper(MapperRegistry.java:72) 
	at org.apache.ibatis.session.Configuration.addMapper(Configuration.java:741) 
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:381) 
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119) 
	... 4 more 

第四:Spring 集成MyBatis 快速入门

核心依赖jar 包:

<!-- mybatis 开发框架 --> 
		<dependency> 
			<groupId>org.mybatis</groupId> 
			<artifactId>mybatis</artifactId> 
			<version>3.4.6</version> 
		</dependency> 
		<!-- mybatis 与spring 集成 --> 
		<dependency>s 
			<groupId>org.mybatis</groupId> 
			<artifactId>mybatis-spring</artifactId> 
			<version>1.3.2</version> 
		</dependency>

核心业务代码:

package com.zzg.sm.entity; 
 
import java.io.Serializable; 
import java.util.Date; 
 
@SuppressWarnings("serial") 
public class Book implements Serializable { 
 
	/** 
	 * 编号 
	 */ 
	private int id; 
	/** 
	 * 书名 
	 */ 
	private String title; 
	/** 
	 * 价格 
	 */ 
	private double price; 
	/** 
	 * 出版日期 
	 */ 
	private Date publishDate; 
 
	public Book(int id, String title, double price, Date publishDate) { 
		this.id = id; 
		this.title = title; 
		this.price = price; 
		this.publishDate = publishDate; 
	} 
 
	public Book() { 
	} 
 
	public int getId() { 
		return id; 
	} 
 
	public void setId(int id) { 
		this.id = id; 
	} 
 
	public String getTitle() { 
		return title; 
	} 
 
	public void setTitle(String title) { 
		this.title = title; 
	} 
 
	public double getPrice() { 
		return price; 
	} 
 
	public void setPrice(double price) { 
		this.price = price; 
	} 
 
	public Date getPublishDate() { 
		return publishDate; 
	} 
 
	public void setPublishDate(Date publishDate) { 
		this.publishDate = publishDate; 
	} 
 
} 
 
 
package com.zzg.sm.dao; 
 
import java.util.List; 
 
import org.apache.ibatis.annotations.Param; 
 
import com.zzg.sm.entity.Book; 
 
public interface BookDao { 
	/** 
	 * 获得所有图书 
	 */ 
	public List<Book> getAllBooks(); 
 
	/** 
	 * 根据图书编号获得图书对象 
	 */ 
	public Book getBookById(@Param("id") int id); 
 
	/** 
	 * 添加图书 
	 */ 
	public int add(Book entity); 
 
	/** 
	 * 根据图书编号删除图书 
	 */ 
	public int delete(int id); 
 
	/** 
	 * 更新图书 
	 */ 
	public int update(Book entity); 
 
} 
 
 
<?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.zzg.sm.dao.BookDao"> 
	<!--id应该是接口中的方法,结果类型如没有配置别名则应该使用全名称 --> 
	<!--获得所有图书 --> 
	<select id="getAllBooks" resultType="Book"> 
		select 
		id,title,price,publishDate from books 
	</select> 
	<!--获得图书对象通过编号 --> 
	<select id="getBookById" resultType="Book"> 
		select 
		id,title,price,publishDate from books where id=#{id} 
	</select> 
	<!-- 增加 --> 
	<insert id="add"> 
		insert into books(title,price,publishDate) 
		values(#{title},#{price},#{publishDate}) 
	</insert> 
	<!-- 删除 --> 
	<delete id="delete"> 
		delete from books where id=#{id} 
	</delete> 
	<!-- 更新 --> 
	<update id="update" parameterType="Book"> 
		update books 
		<set> 
			<if test="title != null and title != ''"> 
				title=#{title}, 
			</if> 
			<if test="price != null"> 
				price=#{price}, 
			</if> 
			<if test="publishDate != null"> 
				publishDate=#{publishDate}, 
			</if> 
		</set> 
		where id=#{id} 
	</update> 
</mapper> 
 
package com.zzg.sm.service; 
 
import java.util.List; 
 
import org.apache.ibatis.annotations.Param; 
 
import com.zzg.sm.entity.Book; 
 
public interface BookService { 
	/** 
	 * 获得所有图书 
	 */ 
	public List<Book> getAllBooks(); 
 
	/** 
	 * 根据图书编号获得图书对象 
	 */ 
	public Book getBookById(@Param("id") int id); 
 
	/** 
	 * 添加图书 
	 */ 
	public int add(Book entity); 
 
	/** 
	 * 根据图书编号删除图书 
	 */ 
	public int delete(int id); 
 
	/** 
	 * 更新图书 
	 */ 
	public int update(Book entity); 
} 
 
 
package com.zzg.sm.service.impl; 
 
import java.util.List; 
 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
 
import com.zzg.sm.dao.BookDao; 
import com.zzg.sm.entity.Book; 
import com.zzg.sm.service.BookService; 
 
@Service 
public class BookServiceImpl implements BookService { 
	@Autowired 
	private BookDao dao; 
 
	@Override 
	public List<Book> getAllBooks() { 
		// TODO Auto-generated method stub 
		return dao.getAllBooks(); 
	} 
 
	@Override 
	public Book getBookById(int id) { 
		// TODO Auto-generated method stub 
		return dao.getBookById(id); 
	} 
 
	@Override 
	public int add(Book entity) { 
		// TODO Auto-generated method stub 
		return dao.add(entity); 
	} 
 
	@Override 
	public int delete(int id) { 
		// TODO Auto-generated method stub 
		return dao.delete(id); 
	} 
 
	@Override 
	public int update(Book entity) { 
		// TODO Auto-generated method stub 
		return dao.update(entity); 
	} 
 
} 

编写sm.xml 配置文件 和测试代码:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:aop="http://www.springframework.org/schema/aop"  
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> 
 
	<!-- dbcp 数据库连接池 --> 
 	<bean id="dataSource" 
		class="org.apache.commons.dbcp2.BasicDataSource"> 
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> 
		<property name="url" value="jdbc:mysql://localhost:3306/myblog?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true"></property> 
		<property name="username" value="root"></property> 
		<property name="password" value="123456"></property> 
	</bean> 
     
    <!--3 会话工厂bean sqlSessionFactoryBean --> 
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 
        <!-- 数据源 --> 
        <property name="dataSource" ref="dataSource"></property> 
        <!-- 别名 --> 
        <property name="typeAliasesPackage" value="com.zzg.sm.entity"></property> 
        <!-- sql映射文件路径 --> 
        <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"></property> 
    </bean> 
     
    <!--4 自动扫描对象关系映射 --> 
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 
        <!--指定会话工厂,如果当前上下文中只定义了一个则该属性可省去 --> 
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> 
        <!-- 指定要自动扫描接口的基础包,实现接口 --> 
        <property name="basePackage" value="com.zzg.sm.dao"></property> 
    </bean> 
     
    <!--5 声明式事务管理 --> 
    <!--定义事物管理器,由spring管理事务 --> 
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource" ref="dataSource"></property> 
    </bean> 
    <!--支持注解驱动的事务管理,指定事务管理器 --> 
    <tx:annotation-driven transaction-manager="transactionManager"/> 
 
    <!--6 容器自动扫描IOC组件  --> 
    <context:component-scan base-package="com.zzg.sm"></context:component-scan> 
     
    <!--7 aspectj支持自动代理实现AOP功能 --> 
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 
</beans>
package com.zzg.sm; 
 
import java.util.Date; 
import java.util.List; 
 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
import com.zzg.sm.entity.Book; 
import com.zzg.sm.service.BookService; 
 
public class SMTest { 
	 
	public static void main(String[] args) { 
		 ApplicationContext ctx=new ClassPathXmlApplicationContext("sm.xml"); 
		 BookService  bookservice=ctx.getBean(BookService.class); 
		  
		 // 执行插入 
//		 Book insert = new Book(); 
//		 insert.setPrice(99.9f); 
//		 insert.setPublishDate(new Date()); 
//		 insert.setTitle("Python 编程思想"); 
//		 bookservice.add(insert); 
		  
		 // 执行修改 
		 Book update = new Book(); 
		 update.setId(1); 
		 update.setTitle("java编程思想--修改"); 
		 bookservice.update(update); 
		  
		 // 执行查询 
		 List<Book> list = bookservice.getAllBooks(); 
		 list.stream().forEach(item ->{ 
			 System.out.println(item.getId()); 
			 System.out.println(item.getTitle()); 
		 }); 
		  
		 Book object = bookservice.getBookById(1); 
		 if(object != null) { 
			 System.out.println(object.getId()); 
			 System.out.println(object.getTitle()); 
		 } 
		  
	} 
} 

MyBatis使用步骤总结

  • 1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
  • 2)创建SqlSessionFactory
  • 3)通过SqlSessionFactory创建SqlSession对象
  • 4)通过SqlSession操作数据库 CRUD
  • 5)调用session.commit()提交事务
  • 6)调用session.close()关闭会话
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
排行榜
KIKK导航

KIKK导航

关注我们