Hibernate 基础知识

来源:互联网 发布:淘宝网店代运营 编辑:程序博客网 时间:2024/06/04 18:21

先看基本框架图:


整体流程 

1:通过configuration来读cfg.xml文件 
2:得到SessionFactory 工厂 
3:通过SessionFactory 工厂来创建Session实例 
4:通过Session打开事务 
5:通过session的api操作数据库 
6:事务提交 
7:关闭连接 

为什么要用: 
1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。   
2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作。   
3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。   
4. hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。 



两种取得session方法: 

openSession和getCurrentSession区别  
openSession:永远创建新的session 需手动close,事务提交不会自动close 
getCurrentSession:事务没提交之前。在上下文找,有可能返回旧的session,事务提交了,会得到新的session。事务提交,session自动close 
上下文,在配置文件指定: 
   <property name="current_session_context_class">thread</property> 
    jta | thread | managed | custom.Class  
     thread常用—》必须指明current_session_context_class,否则抛异常 

a.sessionFactory.openSession();

Java代码  收藏代码
  1. sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();  
  2.        Session session = sessionFactory.openSession();   //创建新的session对象  
  3.        session.beginTransaction();  
  4.        session.save(t);  
  5.        session.getTransaction().commit();  
  6.       session.close() ;                               //关闭session对象  
      

b.sessionFactory.getCurrentSession();

Java代码  收藏代码
  1. sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();  
  2.       Session session = sessionFactory.getCurrentSession();   //获取当前"上下文"中的session对象  
  3.       session.beginTransaction();  
  4.       session.save(t);  
  5.       Session session2 = sessionFactory.getCurrentSession();  
  6.    System.out.println(session == session2);  //true  
  7.       session.getTransaction().commit();            //事务提交自动关闭该session  
  8.       System.out.println(session == session2);  //false                                 


两种使用hibernate的方法

不通过spring加载hibernate.cfg.xml,而是通过自定义的类HibernateUtil.java创建sessionFactory对象 

Java代码  收藏代码
  1. 1.TeamDaoHibernate  
  2. package com.org.momo.dao.hibernateTemplate;  
  3.   
  4. import java.util.List;  
  5.   
  6. import org.hibernate.Query;  
  7. import org.hibernate.Session;  
  8. import org.hibernate.SessionFactory;  
  9. import org.hibernate.Transaction;  
  10.   
  11. import com.org.momo.bean.PageInfo;  
  12. import com.org.momo.bean.Team;  
  13. import com.org.momo.dao.TeamDao;  
  14. import com.org.momo.util.HibernateUtil;  
  15.   
  16. public class TeamDaoHibernate implements TeamDao {  
  17.     private SessionFactory sessionFactory;  
  18.       
  19.     public TeamDaoHibernatenotused() {  
  20.         sessionFactory = HibernateUtil.getSessionFactory();  
  21.     }  
  22.   
  23.     public void insert(Team team) throws Exception {  
  24.         Session session = sessionFactory.openSession();  
  25.           
  26.         Transaction transaction = session.beginTransaction();  
  27.         session.save(team);  
  28.         transaction.commit();  
  29.           
  30.         session.close() ;  
  31.     }  
  32.   
  33.     public void deleteById(Integer id) throws Exception {  
  34.         Session session = sessionFactory.openSession();  
  35.         Transaction transaction = session.beginTransaction();  
  36.           
  37.         String hql = "delete from Team where id=?";  
  38.         Query query = session.createQuery(hql);  
  39.         query.setInteger(0, id);  
  40.         query.executeUpdate();  
  41.           
  42.         transaction.commit();  
  43.         session.close();  
  44.     }  
  45.   
  46.     public void update(Team team) throws Exception {  
  47.         Session session = sessionFactory.openSession();  
  48.         Transaction transaction = session.beginTransaction();  
  49.           
  50.         session.merge(team);  
  51.           
  52.         transaction.commit();  
  53.         session.close();  
  54.     }  
  55.   
  56.     public List<Team> findAll() throws Exception {  
  57.         List<Team> teams = null;  
  58.           
  59.         Session session = sessionFactory.openSession();  
  60.           
  61.         Query query = session.createQuery("from Team");  
  62.         teams = query.list();  
  63.           
  64.         session.close();  
  65.           
  66.         return teams;  
  67.     }  
  68.   
  69.     public Team findById(Integer id) throws Exception {  
  70.         Team team = null;  
  71.           
  72.         Session session = sessionFactory.openSession();  
  73.         Query query = session.createQuery("from Team where id=?");  
  74.         query.setInteger(0, id);  
  75.         team = (Team)query.uniqueResult();  
  76.         session.close();  
  77.           
  78.         return team;  
  79.     }  
  80.   
  81.     public List<Team> findAllPage(PageInfo pageInfo) throws Exception {  
  82.         List<Team> teams = null;  
  83.           
  84.         Session session = sessionFactory.openSession();  
  85.           
  86.         Query queryTotal = session.createQuery("select count(id) from Team");  
  87.         int rowCount = ((Long)queryTotal.uniqueResult()).intValue() ;  //先转换为Long 在转换为int  
  88.         int pageTotal = rowCount/pageInfo.getPageRows();  
  89.         if(rowCount%pageInfo.getPageRows() > 0) {  
  90.             pageTotal++;  
  91.         }  
  92.         pageInfo.setPageTotal(pageTotal);  
  93.           
  94.         Query query = session.createQuery("from Team");  
  95.         query.setFirstResult(pageInfo.getPageRows()*(pageInfo.getCurrentPage()-1));  
  96.         query.setMaxResults(pageInfo.getPageRows());  
  97.         teams = query.list();  
  98.           
  99.         session.close();  
  100.           
  101.         return teams;  
  102.     }  
  103.   
  104. }  


2.HibernateUtil.java 
Java代码  收藏代码
  1.     package com.org.momo.util;  
  2.   
  3. import org.hibernate.SessionFactory;  
  4. import org.hibernate.cfg.AnnotationConfiguration;  
  5.   
  6.   
  7. public class HibernateUtil {  
  8.       private static final SessionFactory sessionFactory;  
  9.       static{  
  10.           try{  
  11.               //初始化hibernate.cfg.xml配置,建立数据库连接  
  12.               sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();  
  13.           }catch(Exception e){  
  14.               e.printStackTrace() ;  
  15.               throw new ExceptionInInitializerError(e) ;  
  16.           }  
  17.       }  
  18.         
  19.       public static SessionFactory getSessionFactory(){  
  20.           return sessionFactory ;  
  21.       }  
  22. }  


通过配置Spring的applicationContext.xml文件加载,spring对Hibernate的支持(HibernateTemplate) 


1.applicationContext.xml 
Java代码  收藏代码
  1.    <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans  
  3. xmlns="http://www.springframework.org/schema/beans"  
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  
  6.   
  7.     <bean id="teamService" class="com.org.momo.service.impl.TeamServiceImpl">  
  8.         <property name="teamDao" ref="teamDao"></property>  
  9.     </bean>  
  10.     <bean id="adminService" class="com.org.momo.service.impl.AdminServiceImpl">  
  11.         <property name="adminDao" ref="adminDao"></property>  
  12.     </bean>  
  13.     <bean id="logService" class="com.org.momo.service.impl.LogServiceImpl">  
  14.         <property name="logDao" ref="logDao"></property>  
  15.     </bean>  
  16.     <bean id="studentService" class="com.org.momo.service.impl.StudentServiceImpl">  
  17.         <property name="studentDao" ref="studentDao"></property>  
  18.     </bean>  
  19.       
  20.     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
  21.         <property name="configLocation" value="classpath:hibernate.cfg.xml"/>  
  22.     </bean>  
  23.     <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">  
  24.         <property name="sessionFactory" ref="sessionFactory"/>  
  25.     </bean>  
  26.       
  27.       
  28.     <bean id="teamDao" class="com.org.momo.dao.hibernateTemplate.TeamDaoHibernateTemplate">  
  29.         <property name="hibernateTemplate" ref="hibernateTemplate"/>  
  30.     </bean>  
  31.     <bean id="adminDao" class="com.org.momo.dao.hibernateTemplate.AdminDaoHibernateTemplate">  
  32.         <property name="hibernateTemplate" ref="hibernateTemplate"/>  
  33.     </bean>  
  34.     <bean id="logDao" class="com.org.momo.dao.hibernateTemplate.LogDaoHibernateTemplate">  
  35.         <property name="hibernateTemplate" ref="hibernateTemplate"/>  
  36.     </bean>  
  37.     <bean id="studentDao" class="com.org.momo.dao.hibernateTemplate.StudentDaoHibernateTemplate">  
  38.         <property name="hibernateTemplate" ref="hibernateTemplate"/>  
  39.     </bean>  
  40.       
  41.   
  42.     <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">  
  43.     </bean>  
  44. </beans>  


2.HibernateTemplate 

Java代码  收藏代码
  1.   package com.org.momo.dao.hibernateTemplate;  
  2.   
  3. import java.sql.SQLException;  
  4. import java.util.List;  
  5.   
  6. import org.hibernate.HibernateException;  
  7. import org.hibernate.Query;  
  8. import org.hibernate.Session;  
  9. import org.springframework.orm.hibernate3.HibernateCallback;  
  10. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
  11.   
  12. import com.org.momo.bean.PageInfo;  
  13. import com.org.momo.bean.Team;  
  14. import com.org.momo.dao.TeamDao;  
  15.   
  16. public class TeamDaoHibernateTemplate extends HibernateDaoSupport implements TeamDao {  
  17.   
  18.     public void deleteById(Integer id) throws Exception {  
  19.         Team team = this.getHibernateTemplate().load(Team.class, id) ;  
  20.         this.getHibernateTemplate().delete(team) ;  
  21.     }  
  22.   
  23.     public List<Team> findAll() throws Exception {  
  24.         return this.getHibernateTemplate().loadAll(Team.class);  
  25.     }  
  26.   
  27.     public Team findById(Integer id) throws Exception {  
  28.         List<Team> teams = this.getHibernateTemplate().find("from Team where id=?",id) ;  
  29.         if(!teams.isEmpty()){  
  30.             return teams.get(0) ;  
  31.         }else{  
  32.         return null;  
  33.         }  
  34.     }  
  35.   
  36.     public void insert(Team team) throws Exception {  
  37.         this.getHibernateTemplate().save(team) ;  
  38.     }  
  39.   
  40.     public void update(Team team) throws Exception {  
  41.         this.getHibernateTemplate().update(team);  
  42.     }  
  43.   
  44.     public List<Team> findAllPage(final PageInfo pageInfo) throws Exception {  
  45.         List<Team> teams = null;  
  46.           
  47.         int rowTotal = ((Long)this.getHibernateTemplate().find("select count(id) from Team").get(0)).intValue();  
  48.         int pageTotal = rowTotal/pageInfo.getPageRows();  
  49.         if(rowTotal%pageInfo.getPageRows() > 0) {  
  50.             pageTotal++;  
  51.         }  
  52.         pageInfo.setPageTotal(pageTotal);  
  53.           
  54.         teams = this.getHibernateTemplate().executeFind(new HibernateCallback(){  
  55.             public Object doInHibernate(Session session)  
  56.                     throws HibernateException, SQLException {  
  57.                 Query query = session.createQuery("from Team");  
  58.                 query.setFirstResult(pageInfo.getPageRows()*(pageInfo.getCurrentPage()-1));  
  59.                 query.setMaxResults(pageInfo.getPageRows());  
  60.                 return query.list();  
  61.             }  
  62.               
  63.         });  
  64.           
  65.         return teams;  
  66.     }  
  67.   
  68. }  


Hibernate的查询方式


HQL : 是面向对象的查询语言,同SQL有些相似是Hib中最常用的方式。

       查询设定各种查询条件。
       支持投影查询,检索出对象的部分属性。
       支持分页查询,允许使用having和group by
       提供内制的聚集函数,sum(),min(),max()
       能调用用户的自定义SQL
       支持子查询,嵌入式查询
       支持动态绑定参数
建议使用Query接口替换session的find方法。
   Query Q = session.createQuery("from customer as c where c.name = :customerName" + "and c.age = :customerAge");
   query.setString ("customerName" , "tom");
   query.setInteger("customerAge" , "21");
   list result = query.list();


QBC :  QBCAPI提供了另一种方式,主要是Criteria接口、Criterion接口和Expression类

   Criteria criteria = session.createCriteria(customer.class);
   Criterion criterion1 = Expression.like("name","t%");
   Criterion criterion2 = Expression.eq("age",new Integer(21));
   Critera = criteria.add(criterion1) ;
   Critera = criteria.add(criterion2) ;
   list result = criteria.list(); 
   或是: list result = session.createCriteria(Customer.class).add(Expression.eq("this.name","tom")).list(); 


SQL :  采用HQL和QBC检索时,Hib生成SQL语句适用所有数据库。

   Query query  = session.createSQLQuery("select {c.*} from customers c where c.name like : customername " + "and c.age = :customerage","c",customer.calss);
   query.setString("customername","tom");
   query.setInteger("customerage","21");
   list result = query.list();


Hibernate 的缓存机制

详细见这里。

Hibernate缓存分类

一级缓

又称为“Session的缓存”。Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。一级缓存中,持久化类的每个实例都具有唯一的OID。


二级缓存

关于二级缓存,可以看这里。

又称为“SessionFactory的缓存”。由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。

Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。

第二级缓存使用

适用于:  
1) 很少被修改的数据   
2) 不是很重要的数据,允许出现偶尔并发的数据   
3) 不会被并发访问的数据   
4) 常量数据   
不适用于:  
1) 经常被修改的数据   
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发   
3) 与其他应用共享的数据。


Session的延迟加载实现要解决两个问题

正常关闭连接和确保请求中访问的是同一个session。

Hibernate session就是java.sql.Connection的一层高级封装,一个session对应了一个Connection。http请求结束后正确的关闭session(过滤器实现了session的正常关闭);延迟加载必须保证是同一个session(session绑定在ThreadLocal)。

 

应用缓存


当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;

查不到,如果配置了二级缓存,那么从二级缓存中查;

如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存。

 

一级缓存与二级缓存的对比

 

一级缓存

二级缓存

存放数据的形式

相互关联的持久化对象

对象的散装数据

缓存的范围

事务范围,每个事务都拥有单独的一级缓存

进程范围或集群范围,缓存被同一个进程或集群范围内所有事务共享

并发访问策略

由于每个事务都拥有单独的一级缓存不会出现并发问题,因此无须提供并发访问策略

由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别

数据过期策略

处于一级缓存中的对象永远不会过期,除非应用程序显示清空或者清空特定对象

必须提供数据过期策略,如基于内存的缓存中对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间

物理介质

内存

内存和硬盘,对象的散装数据首先存放到基于内存的缓存中,当内存中对象的数目达到数据过期策略的maxElementsInMemory值,就会把其余的对象写入基于硬盘的缓存中

缓存软件实现

在Hibernate的Session的实现中包含

由第三方提供,Hibernate仅提供了缓存适配器,用于把特定的缓存插件集成到Hibernate中

启用缓存方式

只要通过Session接口来执行保存,更新,删除,加载,查询,Hibernate就会启用一级缓存,对于批量操作,如不希望启用一级缓存,直接通过JDBCAPI来执行

用户可以再单个类或类的单个集合的粒度上配置第二级缓存,如果类的实例被经常读,但很少被修改,就可以考虑使用二级缓存,只有为某个类或集合配置了二级缓存,Hibernate在运行时才会把它的实例加入到二级缓存中

用户管理缓存的方式

一级缓存的物理介质为内存,由于内存的容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目,Session的evit()方法可以显示的清空缓存中特定对象,但不推荐

二级缓存的物理介质可以使内存和硬盘,因此第二级缓存可以存放大容量的数据,数据过期策略的maxElementsInMemory属性可以控制内存中的对象数目,管理二级缓存主要包括两个方面:选择需要使用第二级缓存的持久化类,设置合适的并发访问策略;选择缓存适配器,设置合适的数据过期策略。SessionFactory的evit()方法也可以显示的清空缓存中特定对象,但不推荐

 

缓存管理方法

 一级缓存的管理


evit(Object obj)  将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。

clear()  将一级缓存中的所有持久化对象清除,释放其占用的内存资源。

contains(Object obj) 判断指定的对象是否存在于一级缓存中。

flush() 刷新一级缓存区的内容,使之与数据库数据保持同步。

 

 当调用session的save()方法。当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。 get()和load()。当session对象调用get()或load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。 使用HQL和QBC等从数据库中查询数据。

复制代码
public class Client{    public static void main(String[] args)    {        Session session = HibernateUtil.getSessionFactory().openSession();        Transaction tx = null;        try        {            /*开启一个事务*/            tx = session.beginTransaction();            /*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/            Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");            System.out.println("customer.getUsername is"+customer1.getUsername());            /*事务提交*/            tx.commit();                        System.out.println("-------------------------------------");                        /*开启一个新事务*/            tx = session.beginTransaction();            /*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/            Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");            System.out.println("customer2.getUsername is"+customer2.getUsername());            /*事务提交*/            tx.commit();                        System.out.println("-------------------------------------");                        /*比较两个get()方法获取的对象是否是同一个对象*/            System.out.println("customer1 == customer2 result is "+(customer1==customer2));        }        catch (Exception e)        {            if(tx!=null)            {                tx.rollback();            }        }        finally        {            session.close();        }    }}
复制代码
复制代码
结果Hibernate:     select        customer0_.id as id0_0_,        customer0_.username as username0_0_,        customer0_.balance as balance0_0_     from        customer customer0_     where        customer0_.id=?customer.getUsername islisi-------------------------------------customer2.getUsername islisi-------------------------------------customer1 == customer2 result is true
复制代码

输出结果中只包含了一条SELECT SQL语句,而且customer1 == customer2 result is true说明两个取出来的对象是同一个对象。其原理是:第一次调用get()方法, Hibernate先检索缓存中是否有该查找对象,发现没有,Hibernate发送SELECT语句到数据库中取出相应的对象,然后将该对象放入缓存中,以便下次使用,第二次调用get()方法,Hibernate先检索缓存中是否有该查找对象,发现正好有该查找对象,就从缓存中取出来,不再去数据库中检索。

 

二级缓存的管理:

evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源。

sessionFactory.evict(Customer.class, new Integer(1));  

evict(Class arg0)  将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源。

sessionFactory.evict(Customer.class);  

evictCollection(String arg0)  将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源。

sessionFactory.evictCollection("Customer.orders");  

 

常用的二级缓存插件

EHCache  org.hibernate.cache.EhCacheProvider

OSCache  org.hibernate.cache.OSCacheProvider

SwarmCahe  org.hibernate.cache.SwarmCacheProvider

JBossCache  org.hibernate.cache.TreeCacheProvider

复制代码
<!-- EHCache的配置,hibernate.cfg.xml --> <hibernate-configuration>    <session-factory>       <!-- 设置二级缓存插件EHCache的Provider类-->       <property name="hibernate.cache.provider_class">          org.hibernate.cache.EhCacheProvider       </property>       <!-- 启动"查询缓存" -->       <property name="hibernate.cache.use_query_cache">          true       </property>    </session-factory>  </hibernate-configuration>
复制代码
复制代码
<!-- ehcache.xml --><?xml version="1.0" encoding="UTF-8"?><ehcache>    <!--        缓存到硬盘的路径    -->    <diskStore path="d:/ehcache"></diskStore>    <!--        默认设置        maxElementsInMemory : 在內存中最大緩存的对象数量。        eternal : 缓存的对象是否永远不变。        timeToIdleSeconds :可以操作对象的时间。        timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。        overflowToDisk :内存满了,是否要缓存到硬盘。    -->    <defaultCache maxElementsInMemory="200" eternal="false"         timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>    <!--        指定缓存的对象。        下面出现的的属性覆盖上面出现的,没出现的继承上面的。    -->    <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"         timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache></ehcache>
复制代码
复制代码
<!-- *.hbm.xml --><?xml version="1.0" encoding='UTF-8'?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" ><hibernate-mapping>   <class>       <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->       <cache usage="read-write"/>       </class></hibernate-mapping>
复制代码

若存在一对多的关系,想要在在获取一方的时候将关联的多方缓存起来,需要在集合属性下添加<cache>子标签,这里需要将关联的对象的hbm文件中必须在存在<class>标签下也添加<cache>标签,不然Hibernate只会缓存OID。

复制代码
<hibernate-mapping>        <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">            <!-- 主键设置 -->            <id name="id" type="string">                <column name="id"></column>                <generator class="uuid"></generator>            </id>                        <!-- 属性设置 -->            <property name="username" column="username" type="string"></property>            <property name="balance" column="balance" type="integer"></property>                        <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">                <cache usage="read-only"/>                <key column="customer_id" ></key>                <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>            </set>                    </class>    </hibernate-mapping>
复制代码

 

 

0 0
原创粉丝点击