zoukankan      html  css  js  c++  java
  • JDBC

    jdbc

    1、概念:

    Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的

    即Java语言操作数据库。

    image-20200515010136874

    2、入门步骤:

    前提导入驱动jar包image-20200515090214058

    1、注册驱动

    2、获取数据库连接对象 Connection

    3定义sql

    4、获取执行sql的语句对象 Statement

    5、执行sql语句,接受返回结果

    6、处理结果

    7、释放资源

    public class JdbcDemo {
      public static void main(String[] args) {
          try {
              //1、注册驱动
              Class.forName("com.mysql.jdbc.Driver");
              //2获取数据库连接对象
              Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db2", "root", "root");
              //3定义sql语句
              String sql = "update stu set name = 'li' where id = 1";
              //4获取执行sql的对象 statement
              Statement statement = connection.createStatement();
              //5执行sql
              int count = statement.executeUpdate(sql);
              //6处理结果
              System.out.println(count);
              //7释放资源
              statement.close();
              connection.close();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }

    3、详解各个对象

    a、DriverManager--驱动管理对象

    功能:

    1注册驱动

    static void registerDriver(Driver driver) 注册与给定的驱动程序 DriverManager 。

    写代码使用:Class.forName("com.mysql.jdbc.Driver");

    通过查看源代码发现:在com.mysql.jdbc.Driver类中存在静态代码块

    try
    {
    DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
    throw new RuntimeException("Can't register driver!");
    }

    }

    2获取数据库连接

    方法:static Connection getConnection(String url, String user, String password )

    参数

    url - 形式为 jdbc:subprotocol:subname的数据库网址

    语法:jdbc:mysql://ip地址(域名):端口号/数据库名称

    例子:jdbc:mysql://127.0.0.1:3306/db2

    user - 正在连接的数据库用户 password - 用户密码

    b、Connection--数据库连接对象

    1获取执行sql的对象

    Statement createStatement() 创建一个 Statement对象,用于将SQL语句发送到数据库。

    PreparedStatement prepareStatement(String sql) 创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。

    2、管理事务:

    开启事务:setAutoCommit(boolean autoCommit) --调用该方法设置参数为false,即开启事务。

    提交事务:commit()

    回滚事务:rollback()

    c、Statement--执行sql的对象

    1执行sql

    1oolean execute(String sql) :可以执行任意的sql 了解

    2、int executeUpdate(String sql) :可以执行DML(insertupdatedelete)语句、DDL(createalterdrop)语句

    返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则成功,反之,则失败。

    3、ResultSet executeQuery(String sql) :执行DQL(select)语句

    d、ResultSet--结果集对象

    1、next() 游标向下移动一行

    2、getxxx(参数):获取数据

    xxx:代表数据类型 如:int getInt(),String getString()

    1、int --代表列的编号,从1开始 如:getString(1)

    while (rs.next()){
      System.out.println(rs.getInt(1)+","+rs.getString(2)+","+rs.getString(3));
    }

    2、String--代表列名。如:getString("name")

    while (rs.next()){    System.out.println(rs.getInt("id")+rs.getString("name")+rs.getString("phone_num"));
    public class JdbcDemo01 {
      public static void main(String[] args) {
          Connection connection = null;
          Statement statement = null;
          try {
              //注册驱动
              Class.forName("com.mysql.jdbc.Driver");
              //获取数据库连接对象
              connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db2", "root", "root");
              //定义sql语句
              String sql = "select * from person";
              //获取执行sql的statement对象
              statement = connection.createStatement();
              //执行sql语句
              ResultSet rs = statement.executeQuery(sql);
              //处理结果集
              while (rs.next()){
                  Person person = new Person();
                  person.setId(rs.getInt("id"));
                  person.setName(rs.getString("name"));
                  person.setAge(rs.getInt("age"));
                  System.out.println(person);
              }
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }catch (SQLException e){
              e.printStackTrace();
          }
      }
    }

    Jdbc工具类——练习

    public class JdbcUtils {
      private static String url;
      private static String user;
      private static String password;
      private static String driver;
      static {
          //1、创建properties集合类
          Properties properties = new Properties();
          try {
              //2、加载文件
              //获取src路径下的文件的方式--ClassLoader 类加载器
              ClassLoader classLoader = JdbcUtils.class.getClassLoader();

    //           InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");//返回用于读取指定资源的输入流。
    //           properties.load(resourceAsStream);

              URL resourceUrl = classLoader.getResource("jdbc.properties");
              String urlPath = resourceUrl.getPath();
              System.out.println(urlPath);
              properties.load(new FileReader(urlPath));
              //3、获取配置的数据,赋值
              JdbcUtils.url = properties.getProperty("url");
              user = properties.getProperty("user");
              password = properties.getProperty("password");
              driver = properties.getProperty("driver");
              //4、加载驱动
              Class.forName(driver);
          } catch (IOException e) {
              e.printStackTrace();
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
      }
      /***
      * @Author 俺家笨丫头
      * @Description: 获取数据库连接
      * @Date: 2020/5/15 23:52
      * @Param []
      * @throws Exception
      * @return java.sql.Connection
      */
      public static Connection getConnection() throws SQLException {
          return DriverManager.getConnection(url,user,password);
      }
      /**
      * @Author 俺家笨丫头
      * @Description: 释放资源
      * @Date: 2020/5/15 23:52
      * @Param [statement, connection]
      * @throws Exception
      * @return void
      */
      public static void close(Statement statement, Connection connection){
          if (statement != null){
              try {
                  statement.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (connection != null){
              try {
                  connection.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
      public static void close(ResultSet rs, Statement statement, Connection connection){
          if (rs != null){
              try {
                  rs.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (statement != null){
              try {
                  statement.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (connection != null){
              try {
                  connection.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
    }
    public class JdbcDemo02 {
      public static void main(String[] args) {
          List<Person> personList = findAll();
          System.out.println(personList);
          System.out.println(personList.size());
      }

      private static List<Person> findAll() {
          ArrayList<Person> list = new ArrayList<>();
          String sql = "select * from person";
          Connection connection = null;
          try {
              connection = JdbcUtils.getConnection();
              Statement statement = connection.createStatement();
              ResultSet rs = statement.executeQuery(sql);
              while (rs.next()){
                  Person person = new Person();
                  person.setId(rs.getInt("id"));
                  person.setName(rs.getString("name"));
                  person.setAge(rs.getInt("age"));
                  list.add(person);
              }
          } catch (SQLException e) {
              e.printStackTrace();
          }
          return list;
      }
    }

    登录小练习(有bug)sql注入问题

    public class Demo03 {
      public static void main(String[] args) {
          Scanner scanner = new Scanner(System.in);
          System.out.println("请输入用户名字:");
          String name = scanner.next();
          System.out.println("请输入密码:");
          String password = scanner.next();
          boolean b = logIn(name, password);
          if(b){
              System.out.println("登录成功");
          }else{
              System.out.println("用户名或密码错误");
          }
      }

      private static boolean logIn(String name, String password) {
          Connection connection = null;
          Statement statement = null;
          ResultSet rs = null;
          if (name == null || password == null){
              return false;
          }
          String sql = "select * from users where name = '" +name+
                  "' and password = '" + password +
                  "'";
          System.out.println("sql:" + sql);
          try {
              connection = JdbcUtils.getConnection();
              connection.setAutoCommit(true);
              statement = connection.createStatement();
              rs = statement.executeQuery(sql);
              return rs.next();
          } catch (SQLException e) {
              e.printStackTrace();
          }finally {
              JdbcUtils.close(rs,statement,connection);
          }
          return false;
      }
    }

    e、PreparedStatement--执行sql的对象

    • 表示预编译的SQL语句的对象。

      SQL语句已预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。

      1、SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

      1、用户随便输入密码:a' or 'a' = 'a'

      2、sql:select * from users where name = 'huhu' and password = 'a' or 'a' = 'a'

      2、解决sql注入问题:使用PreparedStatement对象来解决

      3、预编译的sql:参数使用?作为占位符

      4、步骤:

      1、导入驱动架包mysql-connector-java-5.1.40-bin.jar

      2、注册驱动

      3、获取数据库连接对象

      4、定义sql

      注意sql的参数使用?作为占位符。如 :select * from users where name = ? and password = ?;

      5、获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)

      6、给?赋值

      方法:setXXX(参数1,参数2)

      参数1--?的位置编号 从1开始

      参数2--?的值

      7、执行sql,接受返回结果

      8、处理结果

      9、释放资源

    public class Demo04 {
      public static void main(String[] args) {
          Scanner scanner = new Scanner(System.in);
          System.out.println("请输入用户名字:");
          String name = scanner.next();
          System.out.println("请输入密码:");
          String password = scanner.next();
          boolean b = logIn(name, password);
          if(b){
              System.out.println("登录成功");
          }else{
              System.out.println("用户名或密码错误");
          }
      }

      private static boolean logIn(String name, String password) {
          Connection connection = null;
          PreparedStatement preparedStatement = null;
          ResultSet rs = null;
          if (name == null || password == null){
              return false;
          }
          try {
              connection = JdbcUtils.getConnection();
              connection.setAutoCommit(true);
              String sql = "select * from users where name = ? and password = ?";
              preparedStatement = connection.prepareStatement(sql);
              preparedStatement.setString(1,name);
              preparedStatement.setString(2,password);
              rs = preparedStatement.executeQuery();
              return rs.next();
          } catch (SQLException e) {
              e.printStackTrace();
          }finally {
              JdbcUtils.close(rs,preparedStatement,connection);
          }
          return false;
      }
    }

    4、JDBC控制事务:

    1、事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

    2、操作:开启事务、提交事务、回滚事务。

    3、使用connection对象来管理事务

    开启事务:setAutoCommit(boolean autoCommit) --调用该方法设置参数为false,即开启事务。

    提交事务:commit()

    回滚事务:rollback()

    默认的话为自动提交, 每执行一个update ,delete或者insert的时候都会自动提交到数据库,无法回滚事务。 设置connection.setautocommit(false);只有程序调用connection.commit()的时候才会将先前执行的语句一起提交到数据库,这样就实现了数据库的事务。

    true:sql命令的提交(commit)由驱动程序负责 false:sql命令的提交由应用程序负责,程序必须调用commit或者rollback方法

    public class Demo05 {
      public static void main(String[] args) {
          Connection connection = null;
          PreparedStatement preparedStatement = null;
          PreparedStatement preparedStatement2 = null;

          try {
              //获取数据库连接
              connection = JdbcUtils.getConnection();
              //开启事务
              connection.setAutoCommit(false);
              //sql语句
              String sql1 = "update person a set a.age = a.age - ? where a.id = ?";
              String sql2 = "update person a set a.age = a.age + ? where a.id = ?";
              //获取执行sql的对象
              preparedStatement = connection.prepareStatement(sql1);
              preparedStatement2 = connection.prepareStatement(sql1);
              //设置参数
              preparedStatement.setInt(1,5);
              preparedStatement.setInt(2,1);

              preparedStatement2.setInt(1,5);
              preparedStatement2.setInt(2,5);
              //执行sql
              preparedStatement.executeUpdate();
              int i = 3/0;//制造异常
              preparedStatement2.executeUpdate();
              //提交事务
              connection.commit();
          } catch (Exception e) {
              if (connection != null){
                  try {
                      connection.rollback();//回滚事务
                  } catch (SQLException e1) {
                      e1.printStackTrace();
                  }
              }
              e.printStackTrace();
          }finally {
              JdbcUtils.close(preparedStatement,connection);
              JdbcUtils.close(preparedStatement2,connection);
          }
      }
    }

    5、数据库连接池

    A、概念:其实就是一个容器(集合),存放数据库连接的容器。

    当系统初始化好之后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从 容器中获取连接对象,用户访问完后,会将连接对象归还给容器。

    B、优点:1、节约资源 2、用户访问高效

    C、实现

    1、标准接口:DataSource javax.sql包下

    方法:

    a、获取连接:getConnection()

    b、归还连接:如果连接对象Connection是从连接池中获取的,那么调用connection.close()方法,则不会再关闭连接了。而是归还链接。

    2、一般我们不去实现它,由数据库厂商来实现

    1、C3P0:数据库连接池技术

    2、Druid:数据库连接池实现技术,有阿里巴巴提供的

    D、C3P0数据库连接池技术

    步骤:

    1、导入jar包:c3p0-0.9.5.5.jar mchange-commons-java-0.2.19.jar 和数据库的驱动架包

    2、定义配置文件:

    名称:c3p0.properties.xml 或者 c3p0-config.xml

    路径:直接将配置文件放在src目录下即可

    3、创建核心对象 数据库连接池对象 ComboPooledDataSource

    4、获取连接:getConnection

    c3p0-config.xml的配置

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
      <!-- 使用默认的配置读取连接池对象 -->
      <default-config>
        <!-- 连接参数 -->
        <!-- 数据库驱动名 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!-- 数据库的url -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db2</property>
        <property name="user">root</property>
        <property name="password">root</property>
         
        <!-- 连接池参数 -->
        <!--初始化时申请获取5个连接,取值应在minPoolSize与maxPoolSize之间。-->
        <property name="initialPoolSize">5</property>
        <!-- 连接池里允许申请的最大的连接数量 -->
        <property name="maxPoolSize">10</property>
        <!-- 最小的连接数量 -->
        <property name="minPoolSize">4</property>
        <!--超时时间 (等待3秒才会报错)-->
          <property name="checkoutTimeout">3000</property>
      </default-config>
     
      <named-config name="c3p0mysql">
        <!-- 连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db2</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <!-- 连接池参数 -->
        <property name="initialPoolSize">4</property>
        <property name="maxPoolSize">8</property>
        <!-- 最小的连接数量 -->
        <property name="minPoolSize">3</property>
        <!--超时时间-->
          <property name="checkoutTimeout">1000</property>
      </named-config>
     
      <named-config name="c3p0oracle">
        <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
        <property name="jdbcUrl">jdbc:oracle:thin:@地址:端口:ORCL</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。-->
        <property name="initialPoolSize">3</property>
        <!--连接池中保留的最大连接数。-->
        <property name="maxPoolSize">6</property>
        <!--连接池中保留的最小连接数 -->
        <property name="minPoolSize">2</property>
        <!--超时时间-->
          <property name="checkoutTimeout">1000</property>
      </named-config>
    </c3p0-config>
    public class C3p0Demo01 {
      public static void main(String[] args) {
          //1创建数据库连接池对象
          //DataSource dataSource = new ComboPooledDataSource();//默认<default-config>
          DataSource dataSource = new ComboPooledDataSource("c3p0mysql");//使用指定的配置
          //2获取连接对象
          try {
              Connection connection = dataSource.getConnection();

              //3打印
              System.out.println(connection);
          } catch (SQLException e) {
              e.printStackTrace();
          }
      }
    }
    public class C3p0Demo02 {
      public static void main(String[] args) throws SQLException {
          //1创建数据库连接对象
          DataSource dataSource = new ComboPooledDataSource();//使用默认<default-config>
          //2获取连接
          for (int i = 1; i <= 11; i++) {
              Connection connection = dataSource.getConnection();
              System.out.println(i+":"+connection);

              if (i==5){
                  connection.close();//归还连接到连接池
              }
          }
      }
    }

    E、Druid:数据库连接池实现技术,有阿里巴巴提供的

    1步骤:

    1、导入jar包:druid-1.1.21.jar 和数据库的驱动架包

    2、定义配置文件:

    是properties形式的

    可以是任意名称,可以放在任意目录下

    3、创建核心对象 数据库连接池对象 DruidDataSourceFactory

    4、获取连接:getConnection

    2定义工具类

    1定义一个JDBCUtils

    2提供静态代码块加载配置文件,初始化连接池对象

    3提供方法

    1获取连接方法:通过数据库连接池获取连接

    2释放资源

    3获取连接池的方法

    JDBCUtils工具类

    public class JdbcUtils {
      //1定义成员变量
      private static DataSource dataSource;
      static {
          InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
          try {
              Properties properties = new Properties();
              properties.load(inputStream);
              dataSource = DruidDataSourceFactory.createDataSource(properties);
          } catch (IOException e) {
              e.printStackTrace();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      public static Connection getConnetion() throws SQLException {
          return dataSource.getConnection();
      }
      public static void close(Statement statement,Connection connection){
          if (statement != null){
              try {
                  statement.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (connection !=null){
              try {
                  connection.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }

      public static void close(ResultSet resultSet, Statement statement, Connection connection){
          if (resultSet != null){
              try {
                  resultSet.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (statement != null){
              try {
                  statement.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
          if (connection !=null){
              try {
                  connection.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }

      public static DataSource getDataSource(){
          return dataSource;
      }
    }

    druid.properties文件的配置

    # druid.properties文件的配置
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/db2
    username=root
    password=root
    # 初始化连接数量
    initialSize=5
    # 最大连接数
    maxActive=10
    # 最大超时时间
    maxWait=3000

    小练习

    public class DruidDemo02 {
      public static void main(String[] args) {
          Connection connetion = null;
          PreparedStatement statement = null;
          try {
              connetion = JdbcUtils.getConnetion();
              connetion.setAutoCommit(false);
              String sql = "insert into person values(null,?,?)";
              statement = connetion.prepareStatement(sql);
              statement.setString(1,"李毅");
              statement.setInt(2,26);
              int count = statement.executeUpdate();
              System.out.println(count);
              connetion.commit();
              connetion.close();
          } catch (SQLException e) {
              try {
                  connetion.rollback();
              } catch (SQLException e1) {
                  e1.printStackTrace();
              }
              e.printStackTrace();
          }finally {
              JdbcUtils.close(statement,connetion);
          }

      }
    }

    6、Spring JDBC

    Spring 框架对jdbc的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发

    步骤:

    1、导入架包

    2、创建JDBCTemplate对象。依赖于数据源DataSource

    JdbcTemplate template = new JdbcTemplate(ds);

    3、调用JdbcTemplate的方法来完成CRUD的操作

    update():执行DML语句。增删改语句。

    queryForMap():查询结果将结果集封装为map集合 //注意这个方法的查询结果集长度只能是1

    queryForList():查询结果将结果集封装为list集合

    query():查询结果,将结果封装为JavaBean对象//查询返回对象类型的集合

    new BeanPropertyRowMapper<类型>(类型.class)

    queryForObject():查询结果,将结果封装为对象//一般用来查询聚合函数的

    public class JdbcTemplateDemo01 {
      public static void main(String[] args) {
          //1导jar包
          //2创建JdbcTemplate对象
          JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
          //3调用方法
          String sql = "insert into person values(?,?,?)";
          int count = template.update(sql, null, "千年老妖", 1000);
          System.out.println(count);
      }
    }

    image-20200517110006877

    public class JdbcTemplateDemo02 {
      private JdbcTemplate template = new JdbcTemplate(JdbcUtils.getDataSource());
      @Test
      public void test1(){
          Person person = new Person();
          ArrayList<Person> list = new ArrayList<>();
          String sql = "update person set age = ? where id = ?";
          int update = template.update(sql, 26, 1);
          System.out.println(update);
      }
      @Test
      public void test2(){
          String sql = "insert into person values (?,?,?)";
          int update = template.update(sql, null,"刘飞",28);
          System.out.println(update);
      }
      @Test
      public void test3(){
          String sql = "delete from person where id = ?";
          int update = template.update(sql,4);
          System.out.println(update);
      }
      @Test
      public void test4(){
          String sql = "select * from person where id = ?";
          //注意这个方法的查询结果集长度只能是1
          Map<String, Object> stringObjectMap = template.queryForMap(sql, 1);
          Set<Map.Entry<String, Object>> entrySet = stringObjectMap.entrySet();
          /*for (Map.Entry<String, Object> entry : entrySet) {
              System.out.println(entry.getKey()+entry.getValue());
          }*/
          Iterator<Map.Entry<String, Object>> iterator = entrySet.iterator();
          while (iterator.hasNext()){
              Map.Entry<String, Object> entry = iterator.next();
              System.out.println(entry.getKey()+entry.getValue());
          }
      }
      @Test
      public void test5(){
          String sql = "select * from person where id = ? or id = ?";
          List<Map<String, Object>> maps = template.queryForList(sql,1,2);
          //System.out.println(maps);
          for (Map<String, Object> map : maps) {
              System.out.println(map);
          }
      }

      @Test
      public void test6(){
          String sql = "select * from person where id = ? or id = ?";

          /*List<Person> list = template.query(sql, new RowMapper<Person>() {
              @Override
              public Person mapRow(ResultSet resultSet, int i) throws SQLException {
                  int id = resultSet.getInt("id");
                  String name = resultSet.getString("name");
                  int age = resultSet.getInt("age");
                  Person person = new Person(id,name,age);
                  return person;
              }
          },1,3);
          for (Person person : list) {
              System.out.println(person);
          }*/

          List<Person> list = template.query(sql, new BeanPropertyRowMapper<Person>(Person.class), 1, 3);
          System.out.println(list);
      }
      @Test
      public void test7(){
          String sql = "select count(1) from person where id = ? or id = ?";
          Integer integer = template.queryForObject(sql, Integer.class,1,5);
          System.out.println(integer);
      }
    }

     

  • 相关阅读:
    区块链技术基础
    理解数字货币
    MySQL-插入更新 ON DUPLICATE KEY UPDATE
    Flume-Failover Sink Processor 故障转移与 Load balancing Sink 负载均衡
    Flume-Replicating Channel Selector 单数据源多出口
    Flume-几种拓扑结构
    Flume-事务与传输流程
    Java-JPDA 概述
    Navicat Premium
    Flume-Taildir Source 监控目录下多个文件的追加
  • 原文地址:https://www.cnblogs.com/lxy522/p/12904791.html
Copyright © 2011-2022 走看看