阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

JDBC-数据连接池的使用

180次阅读
没有评论

共计 17589 个字符,预计需要花费 44 分钟才能阅读完成。

据库连接池(connection pool)

   在昨天的练习中每一次练习都需要与数据库建立连接,完成时断开连接,然而当处理的数据量特别的时候,就很耗费时间、降低效率,今天我们学习使用连接池,将连接放在连接池中,需要使用的时候从中取出, 使用完毕放回池中并不是断开连接。

   数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
   数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
   为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
   数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

DBCP 连接池

     首先我们使用 DBCP 连接池(一个免费开源的连接池),我们需要先将 commons-dbcp-1.4.jar 文件放置当前工程下,并配置环境(添加到 Build Path)。下面通过一个程序了解如何使用 DBCP 连接池:

 

    我们在这里和之前一样需要创建一个“dbcp.properties”文件,将必要的参数放入其中,其内容如下,(此文件放在当前工程下),DBCP 连接池使用这个文件可以完成 mysql、Oracle 的连接池的建立,但是每次只能建立一个,另一个需要注释起来。

driverClassName = com.mysql.jdbc.Driver

url = jdbc:mysql://127.0.0.1:3306/company

username = root

password = 123456

 

initialSize = 5

maxActive = 50

maxIdle = 10

 

#driverClassName = oracle.jdbc.driver.OracleDriver

#url = jdbc:oracle:thin:@127.0.0.1:1521:orcl

#username = scott

#password = tiger

 

 

package com.atguigu.jdbc;

import Java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

public class DBCPTest {
	@Test
	public void test2() throws Exception {Properties properties = new Properties();
		properties.load(new FileInputStream("dbcp.properties"));
		DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
		System.out.println("inital:" + ((BasicDataSource)dataSource).getInitialSize());
		System.out.println("getMaxActive:" + ((BasicDataSource)dataSource).getMaxActive());
		System.out.println("getMaxIdle:" + ((BasicDataSource)dataSource).getMaxIdle());
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
		connection.close();}
}

 

C3P0 连接池

     下面我们学习一个功能更加强大的连接池,C3P0(仍然是一个免费开源的连接池),如上我们需要先将 commons-dbcp-1.4.jar 文件放置当前工程下,并配置环境(添加到 Build Path)。
这里通过一个程序了解如何使用 DBCP 连接池:
如同 DBCP 连接池,我们需要创建一个“c3p0-config.xml”文件,将必要的参数放入其中,其内容如下,(此文件放在当工程的 src 目录)

 

<c3p0-config> 

<named-config name=”mysql-config”> 

<property name=”driverClass”>com.mysql.jdbc.Driver</property>

<property name=”jdbcUrl”>jdbc:mysql://127.0.0.1:3306/school</property>

<property name=”user”>root</property>

<property name=”password”>123456</property>

<property name=”acquireIncrement”>5</property> 

<property name=”initialPoolSize”>5</property> 

<property name=”minPoolSize”>5</property> 

<property name=”maxPoolSize”>50</property> 

<property name=”maxStatements”>0</property> 

<property name=”maxStatementsPerConnection”>5</property> 

</named-config> 

<named-config name=”orcale-config”>

<property name=”driverClass”>oracle.jdbc.driver.OracleDriver</property>

<property name=”jdbcUrl”>jdbc:mysql://127.0.0.1:3306/school</property>

<property name=”user”>root</property>

<property name=”password”>123456</property>

</named-config>

</c3p0-config>

       DBCP 连接池使用这个文件可以完成 mysql、oracle 的连接池的建立,每次只能建立一个,但是另一个需要注释起来。因为我们是根据 <named-config name=”mysql-config”> 名建立连接,

 

package com.atguigu.jdbc;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.junit.Test;

import com.mchange.v2.c3p0.*;

public class C3P0Test {
	@Test
	public void test1() throws PropertyVetoException, SQLException {DataSource dataSource = new ComboPooledDataSource("mysql-config"); // 它会默认自动去读取文件
		System.out.println(dataSource);
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
		connection.close();// 把连接归还给连接池
		
		DataSources.destroy(dataSource);// 完全释放池中所有连接, 并销毁连接池!!
	}		
		
	@Test
	public void test2() throws PropertyVetoException, SQLException {DataSource dataSource = new ComboPooledDataSource("oracle-config"); // 它会默认自动去读取文件
		System.out.println(dataSource);
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
		connection.close();// 把连接归还给连接池
		
		DataSources.destroy(dataSource);// 完全释放池中所有连接, 并销毁连接池!!
	}	
}

 学习了连接池之后,JdbcUtil 工具类中的 getConnection 方法就可以应用,如下:

 

 

package com.atguigu.jdbc;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;

/**
 * 预备工作 : 
 * 	1) 把要访问的数据库相关的驱动程序复制到项目中, 就是 jar 包
 * 	2) 配置项目属性, 把 jar 包导入到本项目的 buildpath 中
 * @author Administrator
 *
 */
public class JdbcUtil {
	
	private static DataSource dataSource; // 声明静态属性对象引用.
	
	static {dataSource = new ComboPooledDataSource("mysql-config"); // 连接池对象只需要创建一次就可以了
	}
	
	public static Connection getConnection() throws SQLException {return dataSource.getConnection(); // 要想获取连接, 只需要从连接池中获取, 用完以后, 再归还回来
	}
	
	public static Connection getConnectionOld() throws IOException, ClassNotFoundException, SQLException {// 1) 读取配置文件
		Properties properties = new Properties();
		properties.load(new FileInputStream("jdbc.properties"));
		// 2) 获取配置文件中的必要的信息
		String driverClass = properties.getProperty("driverClass");
		String url = properties.getProperty("url");
		String user = properties.getProperty("user");
		String password = properties.getProperty("password");
		// 3) 注册驱动 , 加载驱动类
		Class.forName(driverClass);
		// 4) 通过驱动管理器获取连接 (需要 url, 用户, 密码)
		return DriverManager.getConnection(url, user, password);// 暗含  new Socket(host,port), 认证, 其他各种初始化操作
	}
	
	// 关闭连接
	public static void close(Connection connection) {close(connection, null);
	}
	
	public static void close(Connection connection, Statement statement) {close(connection, statement, null);
	}
	
	public static void close(Connection connection, Statement statement, ResultSet resultSet) {if (resultSet != null) {
			try {resultSet.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
		
		if (statement != null) {
			try {statement.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
		
		if (connection != null) {
			try {connection.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
	}
	
	// 销毁连接池
	public static void destroy() {
		try {DataSources.destroy(dataSource);
		} catch (SQLException e) {e.printStackTrace();
		}
	}	
}

 

DBUtils 工具类

将常用的操作数据库的 JDBC 的类和方法集合在一起,就是 DBUtils.JDBC。提供供我们使用的工具类 QueryRunner 来执行操作。
在使用之前我们仍然需要将 commons-dbutils-1.3.jar 添加到当前工程下,并添加到 path 路径。
package com.atguigu.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

public class QueryRunnerTest {

	// 使用我们自定义工具实现表的创建
	@Test
	public void test1() throws SQLException {QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		qr.update(connection, "create table test2(aa int, bb varchar(10))");
		JdbcUtil.close(connection);
	}	
	
	// 使用我们自定义工具向表中插入一条记录
	@Test
	public void test2() throws SQLException {QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		int rows = qr.update(connection, "insert into test2(aa, bb) values(?,?)", 10, "xxx");
		System.out.println(rows + "rows");
		JdbcUtil.close(connection);
	}
	
	// 使用 DBUtils.JDBC 接口中提供的方法对 departments 表进行查询,把结果集中的所有记录转换为 department 对象集合并存入 List 集合中,然后遍历输出对象
	@Test
	public void test3() throws SQLException {//query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) 
		String sql = "select * from departments where department_id > ?";
		QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		BeanListHandler<Department> rsh = new BeanListHandler<Department>(Department.class); // 把结果集中的所有记录转换为对象集合
		List<Department> list = qr.query(connection, sql, rsh, 20);
		for (Department department : list) {System.out.println(department);
		}
	}	
	
	// 使用 DBUtils.JDBC 接口中提供的方法对 departments 表进行查询,把结果集中的一条记录转换为 department 实体对象,然后输出对象
	@Test
	public void test4() throws SQLException {
		String sql = "select * from departments where department_id = ?";
		QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		BeanHandler<Department> rsh = new BeanHandler<Department>(Department.class); // 把结果集中的一条记录转换为实体对象
		Department objDepartment = qr.query(connection, sql, rsh, 20);
		System.out.println(objDepartment);
	}
	
	// 使用 DBUtils.JDBC 接口中提供的方法对 departments 表进行查询,将每一条记录存入集合中,然后遍历输出每一个数据
	@Test
	public void test5() throws SQLException {
		String sql = "select * from employees";
		QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		ArrayListHandler rsh = new ArrayListHandler();
		List<Object[]> list = qr.query(connection, sql, rsh);
		for (Object[] objects : list) {for (int i = 0; i < objects.length; i++) {System.out.print(objects[i] + "\t");
			}
			System.out.println();}
	}		
		
	// 使用 DBUtils.JDBC 接口中提供的方法对 departments 表进行查询,将查询到的一个数据输出	
	@Test
	public void test6 () throws SQLException {String sql = "select count(*) from world.country";
		QueryRunner qr = new QueryRunner();
		Connection connection = JdbcUtil.getConnection();
		ScalarHandler rsh = new ScalarHandler();
		Object singleValue = qr.query(connection, sql, rsh);
		System.out.println(singleValue);
	}	
	
		
	@Test
	public void test7() throws Exception {QueryRunner qr = new QueryRunner();
		List<Object> list = qr.query(JdbcUtil.getConnection(),  "select * from student", new ColumnListHandler(1));
		for (Object object : list) {System.out.println(object);
		}
	}

	//MapHandler 把第一行数据封装到 Map 集合中, 列名作为键, 对应值作为值
	@Test
	public void test8() throws Exception {QueryRunner qr = new QueryRunner();
		Map<String, Object> map = qr.query(JdbcUtil.getConnection(),  "select * from student", new MapHandler());
		Set<String> keys = map.keySet();
		for (String key : keys) {Object value = map.get(key);
			System.out.println(key + "--------" + value);
		}
	}	
		
	//MapListHandler 把一行数据封装到 Map 集合中, 并把所有行生成的 Map 再放入一个 List 集合
	@Test
	public void test9() throws Exception {QueryRunner qr = new QueryRunner();
		List<Map<String, Object>> list = qr.query(JdbcUtil.getConnection(),  "select * from student", new MapListHandler());
		for (Map<String, Object> map2 : list) {Set<String> keys = map2.keySet();
			for (String key : keys) {Object value = map2.get(key);
				System.out.println(key + "--------" + value);
			}
			System.out.println();}
	}	
}

到这里就可以统一整理一下自己定义的 JdbcUtil 工具类、CommonUtil 工具类,使自定义的工具类能达到 JDButi.JDBC 相同的功能,如下:

JdbcUtils.java

 

package com.atguigu.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;

public class JdbcUtil {
	
	private static DataSource dataSource;
	
	static {dataSource = new ComboPooledDataSource("config1"); // 它必须依赖文件 src/c3p0-config.xml
	}
	
	// 获取 c3p0 连接池的连接
	public static Connection getConnection() throws SQLException {return dataSource.getConnection();
	}
	
	public static void close(Connection connection) {close(connection, null);
	}
	
	public static void close(Connection connection, Statement statement) {close(connection, statement, null);
	}
	
	public static void close(Connection connection, Statement statement, ResultSet resultSet) {if (resultSet != null) {
			try {resultSet.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
		
		if (statement != null) {
			try {statement.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
		
		if (connection != null) {
			try {connection.close();
			} catch (Exception e) {e.printStackTrace();
			}
		}
	}
	
	public static void destroy() {
		try {DataSources.destroy(dataSource);
		} catch (SQLException e) {e.printStackTrace();
		}
	}
}

 

CommonUtil.java

package com.atguigu.jdbc;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class CommonUtil {
	
	/**
	 * 把结果集中的每一行都放入 Object 对象数组中, 再把所有的 Object 对象数组放入一个 List 集合中.
	 * @throws SQLException 
	 */
	public static List<Object[]> query(Connection connection, String sql, Object... values) throws SQLException {
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {preparedStatement = connection.prepareStatement(sql);
			fillArguments(preparedStatement, values);
			resultSet = preparedStatement.executeQuery();
			List<Object[]> list = new ArrayList<Object[]>();
			int cols = resultSet.getMetaData().getColumnCount();
			while (resultSet.next()) {Object[] dataRow = new Object[cols];
				for (int i = 0; i < dataRow.length; i++) {dataRow[i] = resultSet.getObject(i + 1);
				}
				list.add(dataRow);
			}
			return list;
		} finally {JdbcUtil.close(null, preparedStatement, resultSet);
		}
	}

	/**
	 * 把结果集中的第一行数据, 全放入一个对象数组中
	 * @throws SQLException 
	 */
	public static Object[] queryValueArray(Connection connection, String sql, Object... values) throws SQLException {
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {preparedStatement = connection.prepareStatement(sql);
			fillArguments(preparedStatement, values);
			resultSet = preparedStatement.executeQuery();
			if (resultSet.next()) {Object[] dataRow = new Object[resultSet.getMetaData().getColumnCount()];
				for (int i = 0; i < dataRow.length; i++) {dataRow[i] = resultSet.getObject(i + 1);
				}
				return dataRow;
			} else {return null;}
		} finally {JdbcUtil.close(null, preparedStatement, resultSet);
		}
	}
	
	/**
	 * 从结果集中获取第一行的第一列
	 * @throws SQLException 
	 */
	public static Object queryValue(Connection connection, String sql, Object... values) throws SQLException {
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {preparedStatement = connection.prepareStatement(sql);
			fillArguments(preparedStatement, values);
			resultSet = preparedStatement.executeQuery();
			if (resultSet.next()) {return resultSet.getObject(1);
			} else {return null;}
		} finally {JdbcUtil.close(null, preparedStatement, resultSet);
		}
	}
	
	/**
	 * 把结果集中第一行转换为对象返回
	 * @throws SQLException 
	 * @throws SecurityException 
	 * @throws NoSuchFieldException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	public static <T> T queryBean(Connection connection, String sql, Class<T> clazz, Object... values) throws SQLException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {preparedStatement = connection.prepareStatement(sql);
			
			fillArguments(preparedStatement, values);
			
			resultSet = preparedStatement.executeQuery();
			if (resultSet.next()) {T t = clazz.newInstance();
				ResultSetMetaData metaData = resultSet.getMetaData();
				int cols = metaData.getColumnCount();
				for (int i = 0; i < cols; i++) {String label = metaData.getColumnLabel(i + 1);
					Object value = resultSet.getObject(label);
					Field field = clazz.getDeclaredField(label);
					field.setAccessible(true);
					field.set(t, value);
				}
				return t;
			} else {return null;}
		} finally {JdbcUtil.close(null, preparedStatement, resultSet);
		}
	}

	/**
	 * 把结果集的所有记录都封装成对象, 并把所有对象放在一个 List 集合中
	 * @throws SQLException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws SecurityException 
	 * @throws NoSuchFieldException 
	 */
	public static <T> List<T> query(Connection connection, String sql, Class<T> clazz, Object... values) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {preparedStatement = connection.prepareStatement(sql);
			
			fillArguments(preparedStatement, values);
			
			resultSet = preparedStatement.executeQuery();
			List<T> list = new ArrayList<T>();
			ResultSetMetaData metaData = resultSet.getMetaData();
			int cols = metaData.getColumnCount();
			while (resultSet.next()) {T t = clazz.newInstance();
				for (int i = 0; i < cols; i++) {String label = metaData.getColumnLabel(i + 1);
					Object value = resultSet.getObject(label);
					if (value != null) {Field field = clazz.getDeclaredField(label);
						field.setAccessible(true);
						field.set(t, value);
					}
				}
				list.add(t);
			}
			return list;
		} finally {JdbcUtil.close(null, preparedStatement, resultSet);
		}
	}
	
	/**
	 * 通用更新操作
	 * @throws SQLException 
	 */
	public static int update(Connection connection, String sql, Object... values) throws SQLException {
		PreparedStatement preparedStatement = null;
		try {preparedStatement = connection.prepareStatement(sql);
			fillArguments(preparedStatement, values);
			return preparedStatement.executeUpdate();} finally {JdbcUtil.close(null, preparedStatement);
		}
	}
	
	public static void fillArguments(PreparedStatement preparedStatement, Object... values) throws SQLException {for (int i = 0; i < values.length; i++) {preparedStatement.setObject(i + 1, values[i]);
		}
	}	
}

BaseDAO

综合之前学习过的知识,在这里创建一个 BaseDAO<T> 类借助 DBUtils 工具类实现数据操作功能:

 

package com.atguigu.jdbc;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

public class BaseDAO<T>{

	protected Class<T> clazz; // T 泛型究竟是什么类型, 用类模板对象来描述
	protected QueryRunner qr = new QueryRunner(); // 用于执行通用查询和更新的工具类对象
	protected Connection connection; // 数据库连接
	protected String tableName; // 涉及到的表, 需要通过构造器初始化赋值
	
	public JdbcDAO(String tableName) {
		// 以下代码的执行者是子类对象, 所以 this.getClass 是获取子类��类模板对象
		Type type = this.getClass().getGenericSuperclass(); // JdbcDAO<Teacher>
		if (type instanceof ParameterizedType) {ParameterizedType parameterizedType = (ParameterizedType)type;//JdbcDAO<Teacher>
			Type[] types = parameterizedType.getActualTypeArguments();
			clazz = (Class<T>)types[0];
		} else {clazz = (Class<T>)Object.class;
		}
		// 获取一个连接供所有方法使用
		try {connection = JdbcUtil.getConnection();
		} catch (SQLException e) {e.printStackTrace();
		}
		this.tableName = tableName;
	}
	
	// 获得记录中具体的一个数据
	public Object getValue(String sql, Object... values) {
		try {return qr.query(connection, sql, new ScalarHandler(), values);
		} catch (SQLException e) {e.printStackTrace();
		}
		return null;
	}

	// 获得一行数据并封装成 javabean 对象
	public T get(String sql, Object... values) {
		try {return qr.query(connection, sql, new BeanHandler<T>(clazz), values);
		} catch (SQLException e) {e.printStackTrace();
		}
		return null;
	}

	// 获得多行记录,封装成 javabean 对象,保存在 list 集合中
	public List<T> getList(String sql, Object... values) {
		try {return qr.query(connection, sql, new BeanListHandler<T>(clazz), values);
		} catch (SQLException e) {e.printStackTrace();
		}
		return null;
	}
	
	
	// 获得所有记录,封装成 javabean 对象,保存在 list 集合中
	public List<T> getAll() {return getList("select * from" + tableName);
	}
	
	// 根据 id 获取某一条记录,并封装成 javabean 对象返回
	public T getById(int id) {return get("select * from" + tableName + "where id = ?", id);
	}
	
	// 根据 id 删除某一条记录,删除成功返回 ture,失败返回 false
	public boolean deleteById(int id) {int rows = update("delete from" + tableName + "where id = ?", id);
		if (rows > 0) {return true;}
		return false;
	}

	// 通用的更新操作
	public int update(String sql, Object... values) {
		try {return qr.update(connection, sql, values);
		} catch (SQLException e) {e.printStackTrace();
		}
		return 0;
	}
	
	// 关闭连接
	public void close() {JdbcUtil.close(connection);
	}
}

本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-10/136112.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计17589字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中