通知 网站从因情语写改为晴雨,这个网站的模板也从calmlog_ex改为 whimurmur

HIBERNATE知识复习记录1-连接及常用方法

4332人浏览 / 0人评论 / | 作者:因情语写  | 分类: hibernate  | 标签: 框架  /  hibernate  | 

作者:因情语写

链接:https://www.proprogrammar.com/article/292

声明:请尊重原作者的劳动,如需转载请注明出处


    要去面试了,复习一下HIBERNATE的相关知识吧,原来边看视频边写的代码如下,已经分不清先后次序了,大致看一看吧。

  先看下总的配置文件hibernate.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="hibernate.connection.password">tiger</property>
        <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
        <property name="hibernate.connection.username">scott</property>
<!--    <property name="hibernate.default_schema">hibernate5</property>    --> 
       <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
        
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        
        <!-- 指定数据表的生成策略
            create: 每次操作都重新删除新建表
            update: 如果表结构有改变,将进行更新,但不会删除原来被更新的行与列
            create-drop: 每次操作时新建表,SESSION关闭时会删除表
            validate: 会检查表结构是否发生变化,若有变化会抛出异常,但不会修改表结构
        -->
        <property name="hbm2ddl.auto">update</property>
        
        <!-- 设置hibernate的事务隔离级别
            1: 读未提交
            2: 读已提交
            4: 可重复读
            8: 序列化
         -->
        <property name="hibernate.connection.isolation">2</property>
        
        <!-- 删除对象后使其OID置为null -->
        <property name="hibernate.use_identifier_rollback">true</property>
        
        <!-- 配置管理session的方式-->
        <property name="current_session_context_class">thread</property>
        
        <!-- 启用二级缓存  -->
        <property name="cache.use_second_level_cache">true</property>
        
        <!-- 配置使用的二级缓存的产品  -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
        
        <!-- 配置启用查询缓存 -->
        <property name="cache.use_query_cache">true</property>

        <!-- <property name="hibernate.c3p0.max_size">10</property>
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>
        <property name="hibernate.c3p0.idle_test_period">2000</property>
        <property name="hibernate.c3p0.timeout">2000</property>
        <property name="hibernate.c3p0.max_statements">10</property> -->
        
        <!-- invalid for mysql, valid for oracle -->
        <property name="hibernate.jdbc.fetch_size">100</property>
        <property name="hibernate.jdbc.batch_size">30</property>
        
        <!-- 指定关联的 .hbm.xml 文件 -->
        <!-- <mapping resource="hibernate/helloworld/News.hbm.xml" />
        <mapping resource="hibernate/helloworld/Worker.hbm.xml"/>
        <mapping resource="ntoone/Customer.hbm.xml"/>
        <mapping resource="ntoone/Order.hbm.xml"/> -->
        <!-- <mapping resource="ntoone/both/Customer.hbm.xml"/>
        <mapping resource="ntoone/both/Order.hbm.xml"/> -->
        <!-- <mapping resource="onetoone/foreign/Department.hbm.xml"/>
        <mapping resource="onetoone/foreign/Manager.hbm.xml"/>
        <mapping resource="onetoone/primary/Department.hbm.xml"/>
        <mapping resource="onetoone/primary/Manager.hbm.xml"/> -->
        <!-- <mapping resource="nton/Category.hbm.xml"/>
        <mapping resource="nton/Item.hbm.xml"/> 
        <mapping resource="unionclass/Person.hbm.xml"/>
        <mapping resource="strategy/Customer.hbm.xml"/>
        <mapping resource="strategy/Order.hbm.xml"/>-->
        <mapping resource="twolevelcache/Department.hbm.xml"/>
        <mapping resource="twolevelcache/Employee.hbm.xml"/>
        <!-- 二级缓存相关配置 -->
        <class-cache usage="read-write" class="twolevelcache.Employee"/>
        <class-cache usage="read-write" class="twolevelcache.Department"/>
        <collection-cache usage="read-write" collection="twolevelcache.Department.emps"/>
         
    </session-factory>
</hibernate-configuration>

    其中我觉得比较重要少见的是:

<!-- 删除对象后使其OID置为null -->
<property name="hibernate.use_identifier_rollback">true</property>
<!-- 设置hibernate的事务隔离级别
            1: 读未提交
            2: 读已提交
            4: 可重复读
            8: 序列化
-->
<property name="hibernate.connection.isolation">2</property>
<!-- 配置管理session的方式-->
<property name="current_session_context_class">thread</property>
<!-- 二级缓存相关配置 -->
<class-cache usage="read-write" class="twolevelcache.Employee"/>
<collection-cache usage="read-write" collection="twolevelcache.Department.emps"/>

    先看下hibernate包下面的内容。

  大致看一下吧,不去找什么资料解释了,权且知其然吧,也没有什么延伸扩展,文末会有相关代码的下载地址。

  先看第一个dao子包:

  HibernateTest.java

package hibernate.dao;

import java.sql.Connection;
import java.sql.SQLException;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import hibernate.util.HibernateUtils;

public class HibernateTest {
    private SessionFactory sessionFactory;
    
    private Session session;
    
    private Transaction transaction;
    
    @Before
    public void init()
    {
        System.out.println("init");

        // 1. 创建一个SessionFactory对象
        sessionFactory = null;
        Configuration configuration = new Configuration().configure();
        
        // before 4.0
//        sessionFactory = configuration.buildSessionFactory();
        
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
                                                                      .buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        // 2. 创建一个Session 对象
        session = sessionFactory.openSession();
        
        // 3. 开启事务
        transaction = session.beginTransaction();
        
    }
    
    @After
    public void destory()
    {
        System.out.println("destory");
        // 5. 提交事务
        transaction.commit();
        
        // 6. 关闭Session
        session.close();
        
        // 7. 关闭SesssionFactory
        sessionFactory.close();
    }
    
    @Test
    public void testManageSession()
    {
     // 这个session是通过getCurrentSession()方法创建的,所以是通过thread来管理的
        Session session = HibernateUtils.getInstance().getSession();
        System.out.println("-->" + session.hashCode());
        Transaction transaction = session.beginTransaction();
        
        DepartmentDao departmentDao = new DepartmentDao();
        Department dept = new Department();
        dept.setName("ATGUIGU");
        departmentDao.save(dept);
        // 若SESSION是由THREAD来管理的,则提交事务或回滚时SESSION已经关闭
        transaction.commit();
        System.out.println(session.isOpen());
    }
    
    @Test
    public void testBatch()
    {
     // 通过该方法可以使用原生的onnection对象来进行批处理操作
        session.doWork(new Work(){

            @Override
            public void execute(Connection connection) throws SQLException {
                // according to primitive jdbc api, most efficient, swiftest
                // see http://blog.csdn.net/huaguoming/article/details/8496398 for help
            }
            
        });
    }
}

      下面看一下hibernate映射文件。

  Department.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-7-29 2:43:46 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping package="hibernate.dao">
    <class name="Department" table="DEPARTMENT">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
     <!-- 字段名是emps,对应的表是EMPLOYEE表,inverse="true"说明由Employee对象来维持一对多的关系,即通过
        EMPLOYEE表的DEPT_ID即Employee类的deptId字段来联系DEPARTMENT,lazy="true"说明是懒加载,在获取
        Department对象的时候并不会同时获取关联表EMPLOYEE中相应的存储在emps中的内容,只有真正用到emps对象
        的时候才会查询数据库获取相应数据。
     -->
        <set name="emps" table="EMPLOYEE" inverse="true" lazy="true">
            <key><column name="DEPT_ID" /></key>
            <one-to-many class="Employee" />
        </set>
    </class>
</hibernate-mapping>

       其中生成id的方式设置成了native,也就是由相应数据库自己决定生成id的方式。

  其中还有一个一对多映射。

  Employee.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-7-29 2:43:46 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping package="hibernate.dao">
    <class name="Employee" table="EMPLOYEE">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="salary" type="float">
            <column name="SALARY" />
        </property>
        
        <property name="email" type="java.lang.String">
            <column name="EMAIL" />
        </property>
        
    <!-- 
      dept属性对应多个Employee对象,关于fetch几种方式的不同,可以见下面的文章
      http://blog.csdn.net/haolongabc/article/details/21629889
     -->
        <many-to-one name="dept" class="Department" fetch="join"><!-- join select subselect-->
            <column name="DEPT_ID" />
        </many-to-one>
    </class>
    
  <!-- 一种HQL检索方式,参见下面的文章
     https://www.cnblogs.com/linyueshan/p/5819801.html
     -->
    <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
</hibernate-mapping>

       相应的有一个多对一关系, 上面有相关解释。

  下面再看一下helloworld子包。

  HibernateTest.java

package hibernate.helloworld;

import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.sun.media.jfxmedia.control.VideoDataBuffer;

public class HibernateTest {
    private SessionFactory sessionFactory;
    
    private Session session;
    
    private Transaction transaction;
    
    @Before
    public void init()
    {
        System.out.println("init");

        // 1. 创建一个SessionFactory对象
        sessionFactory = null;
        Configuration configuration = new Configuration().configure();
        
        // before 4.0
//        sessionFactory = configuration.buildSessionFactory();
        
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
                                                                      .buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        // 2. 创建一个Session 对象
        session = sessionFactory.openSession();
        
        // 3. 开启事务
        transaction = session.beginTransaction();
        
    }
    
    @After
    public void destory()
    {
        System.out.println("destory");
        // 5. 提交事务
        transaction.commit();
        
        // 6. 关闭Session
        session.close();
        
        // 7. 关闭SesssionFactory
        sessionFactory.close();
    }
    
  /**
   * get方法
   */
    @Test
    public void testGet()
    {
        System.out.println("test");
        // 4. 执行保存操作
//        News news = new News("Java", "ATGUIGU", new Date(new java.util.Date().getTime()));
//        session.save(news);
        News new2 = (News)session.get(News.class, 1);
        
        System.out.println(new2);
        
        News new3 = (News)session.get(News.class, 1);
        System.out.println(new3);
    }
    
    /**
     * flush: 使数据表中的记录和session缓存中的对象的状态保持一致,
     * 为了保持一致,则是可能会发送对应的sql语句
     * 1. 调用transaction的commit()方法中:先调用session的
     *         flush方法,再提交事务。
     * 2. flush方法可能会发送sql,不会提交事务
     * 3. 注意:在未提交事务或显示的调用session.flush()方法之前,也有可
     *   能会进行flush操作。
     * 1)执行HQL或QBC查询,会先进行flush操作,以得到数据表的最新的记录
     * 2)若记录的ID是由底层数据库使用自增的方式生成的,则在调用save()方
     *   法后会立即发送insert()语句,因为save()方法后,必须保证对象的
     *   ID是存在的
     */
    @Test
    public void testSessionFlush()
    {
        News news = (News)session.get(News.class, 1);
        news.setAuthor("Oracle");
        // commit之前会调用flush()
        
        News news2 = (News) session.createCriteria(News.class).uniqueResult();
        System.out.println(news2);
    }
    
    @Test
    public void testSessionFlush2()
    {
        News news = new News("Java", "SUN", new Date(new java.util.Date().getTime()));
        session.save(news);
    }
    
    /**
     * refresh会强制发送SELECT语句,以使SESSION缓存中对象的状态和数据表中对
     * 应的记录保持一致!
   * 关于clear方法,可以查看下面的文章:
    * https://www.cnblogs.com/L-a-u-r-a/p/7049666.html 
     */
    @Test
    public void testRefresh()
    {
        News news = (News)session.get(News.class, 1);
        System.out.println(news);
        
//        session.refresh(news);
        session.clear();
        News news2 = (News)session.get(News.class, 1);
        System.out.println(news2);
    }
    
    /**
     * 对象的四种状态
     */
    /**
     * 1.save()方法
     * 1)使一个临时对象变为持久化对象
     * 2)为对象分配ID
     * 3)在flush缓存时会发送一条INSERT语句 
     * 4)在save方法之前的id是无效的
     * 5)持久化对象的ID是不能被修改的
     */
    @Test
    public void testSave()
    {
        News news = new News();
        news.setTitle("CC");
        news.setAuthor("cc");
        news.setDate(new Date(new java.util.Date().getTime()));
//        news.setId(100);
        
        System.out.println(news);
        
        session.save(news);
//        news.setId(200);
        
        System.out.println(news);
    }
    
    /**
     * persist():也会执行INSERT操作
     * 和save()的区别:
     * 在调用persist方法之前,若对象已经有id了,则不会执行insert,而相反的,会抛出一个
     * 异常
     */
    @Test
    public void testPersist()
    {
        News news = new News();
        news.setTitle("EE");
        news.setAuthor("dd");
        news.setDate(new Date(new java.util.Date().getTime()));
        news.setId(200);
        session.persist(news);
    }
    
    /**
     * 1. 执行 get 方法会立即加载对象,
     * 而执行 load 方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象。
     * get 是立即检索,load 是延迟检索
     * 2. 若数据库中没有对应的记录,Session也没有被关闭
     * get 返回null
     * load 若不使用该对象的任何属性,没问题;若需要初始化了,抛出异常
     * 3. load方法可能会抛出LazyInitializationException异常:在需要初始化代理对象之前已经关闭了session
     */
    @Test
    public void testLoad()
    {
        News news = (News)session.load(News.class, 10);
        System.out.println(news.getClass().getName());
        
        session.close();
        
        System.out.println(news);
    }
    
    @Test
    public void testGet()
    {
        News news = (News)session.get(News.class, 1);
        session.close();
        System.out.println(news);
    }
    
    /**
     * update:
     * 1. 若更新一个持久化对象,不需要显示的调用update方法,因为在调用
     * transaction的commit方法时,会先执行session的flush方法
     * 2. 更新一个游离对象,需要显示的调用session的update方法,可以把
     * 一个游离对象变为持久化对象。
   * hibernate对象的四种状态:
   * https://www.cnblogs.com/cat36/p/5553232.html
     * 需要注意的:
     * 1. 无论要更新的游离对象和数据表的记录是否一致,都会发送update语句
     *    如何能让update方法不再盲目的触发update语句呢?在.hbm.xml文件
     *    class节点设置一个属性,叫select-before-update为true,但通常不用
     *    设置该属性
     * 2. 若数据表中没有对应的记录,但还调用了update方法,会抛出异常
     * 3. 当update方法关联一个游离对象时,如果在session的缓存中已经存在相同的OID持久化对象,会抛出异常,因为在session缓存中不能有两
     *    个OID相同的对象
     */
    @Test
    public void testUpdate()
    {
        News news = (News)session.get(News.class, 1);

        transaction.commit();
        session.close();
//        news.setId(300);
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        
//        news.setAuthor("SUN");
        
        News news2 = (News)session.get(News.class, 1);
        session.update(news);
    }
    
    /**
     * 注意:
     * 1. 若OID不为null, 但数据表中没有相应记录,会抛出异常
     * 2. 了解:OID值等于id的unsaved-value属性值的对象,也认为是一个游离对象
     */
    @Test
    public void testSaveOrUpdate()
    {
        News news = new News("FF", "ff", new Date(1111111111111L));
        news.setId(11);
        session.saveOrUpdate(news);
    }
    
    /**
     * delete:执行删除操作。只要OID和数据表中的一条记录对应,就会准备执行delete操作,若OID在
     *                数据表中没有对应的记录,则抛出异常
     * 可以通过设置hibernate配置文件的hibernate.use_identifier_rollback属性,使执行删除后的对象的id为null
     */
    @Test
    public void testDelete()
    {
//        News news = new News();
//        news.setId(1);
        News news = (News) session.get(News.class, 2);
        session.delete(news);
        System.out.println(news);
    }
    
    /**
     * evict: 从session缓存中把指定的持久化对象移除
     */
    @Test
    public void testEvict()
    {
        News news1 = (News)session.get(News.class, 1);
        News news2 = (News)session.get(News.class, 2);
        
        news1.setTitle("AA");
        news2.setTitle("BB");
        
        session.evict(news1);
    }
    
  /**
  * 进行批处理
  */
    @Test
    public void testDoWork()
    {
        session.doWork(new Work() {
            
            @Override
            public void execute(Connection arg0) throws SQLException {
                System.out.println(arg0);
                // 调用存储过程
            }
        });
    }
    
  /**
  * 动态更新,参看下面的文章:
  * http://blog.csdn.net/tctctttccc/article/details/76785199
  */
    @Test
    public void testDynamicUpdate()
    {
        News news = (News) session.get(News.class, 1);
        news.setAuthor("ABCD");
    }
    
  /**
  * 主键id生成策略
  * http://ryxxlong.iteye.com/blog/612446
  */
    @Test
    public void testIdGenerator() throws InterruptedException
    {
        News news = new News("AA", "aa", new Date(22222222222222L));
        session.save(news);
        
        Thread.sleep(5000);
    }
    
  /**
  * 属性更新或插入,参见下面的文章:
  * http://blog.csdn.net/lengxingxing_/article/details/68925879
  */
    @Test
    public void testPropertyUpdate()
    {
        News news = (News) session.get(News.class, 1);
        news.setAuthor("AAA");
        news.setTitle("aaaaa");
        System.out.println(news.getDesc());
        System.out.println(news.getDate());
    }
    
  /**
  * 二进制大数据的处理
  */
    @Test
    public void testBlob() throws Exception
    {
//        News news = new News();
//        news.setAuthor("cc");
//        news.setContent("CONTENT");
//        news.setDate(new Date(11111111111L));
//        news.setDesc("DESC");
//        news.setTitle("CC");
//        
//        InputStream stream = new FileInputStream(this.getClass().getResource("/").getPath() + "/jvhua.jpg");
//        Blob image = Hibernate.getLobCreator(session)
//                        .createBlob(stream, stream.available());
//        news.setImage(image);
//        session.save(news);
        
        News news2 = (News)session.get(News.class, 1);
        Blob image2 = news2.getImage();
        InputStream inputStream = image2.getBinaryStream();
        System.out.println(inputStream.available());
    }
    
  /**
  * Component的相关使用:
  * http://blog.csdn.net/itzyjr/article/details/8505789
  * http://fehly.iteye.com/blog/636409
  */
    @Test
    public void testComponent()
    {
        Worker worker = new Worker();
        Pay pay = new Pay();
        
        pay.setMonthlyPay(1000);
        pay.setYearlyPay(50000);
        pay.setVocationWithPay(55);
        worker.setName("abcd");
        worker.setPay(pay);
//        session.save(worker);
        
        Worker worker2 = (Worker)session.get(Worker.class, 1);
        System.out.println("parent work name: " + worker2.getPay().getWorker().getName());
        
    }
}

       相应的映射文件:

  Work.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-7-22 11:36:52 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping package="hibernate.helloworld">
    <class name="Worker" table="WORKER">
        <id name="id" type="int">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <!-- 映射组成关系 -->
        <component name="pay" class="Pay">
            <parent name="worker"/>
            <!-- 指定组成关系的组件的属性 -->
            <property name="monthlyPay" column="MONTHLY_PAY"></property>
            <property name="yearlyPay" column="YEARLY_PAY"></property>
            <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property>
            
        </component>
    </class>
</hibernate-mapping>

       有一个component

  News.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-7-17 12:21:26 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping package="hibernate.helloworld">
  <!-- 动态插入,动态更新,更新前先选择 -->
    <class name="News" table="NEWS" dynamic-insert="true" dynamic-update="true" select-before-update="false">
        <id name="id" type="java.lang.Integer" unsaved-value="11">
            <column name="ID" />
            <!-- 指定主键的生成方式,native:使用数据库本地的方式
                increment
                hilo
                sequence
                identity
             -->
            <generator class="native" />
        </id>
    
    <!-- 唯一性,不可更新 -->
        <property name="title" type="java.lang.String"
            unique="true" update="false" index="news_index">
            <column name="TITLE" length="10" />
        </property>
        <property name="author" type="java.lang.String"
            index="news_index">
            <column name="AUTHOR" />
        </property>
        <property name="date" type="date">
            <column name="DATE" />
        </property>
        
        <!-- 映射派生属性 -->
        <property name="desc" formula="(SELECT concat(author, ': ', title) FROM NEWS N WHERE n.id = id)"></property>
        
    <!-- 大文本 -->
        <property name="content">
            <column name="CONTENT" sql-type="mediumtext"></column>
        </property>
        
    <!-- 二进制图片 -->
        <property name="image" type="blob">
            <column name="IMAGE" sql-type="mediumblob"></column>
        </property>
    </class>
</hibernate-mapping>

      有不少新东西,可以看一下

  内容太多了,就先讲到这里吧,其它的放到下一篇中继续说。


亲爱的读者:有时间可以点赞评论一下

点赞(1) 打赏

全部评论

还没有评论!
广告位-帮帮忙点下广告