框架 day33 Hibernate,组件映射,继承映射,抓取(检索)策略-优化,检索方式总结

组件(组成)映射

例1:

public class Person {

	private Integer pid;	//OID 值
	private String name;

	//第一种方案
	private String homeAddr;
	private String homeTel;
	private String companyAddr;
	private String companyTel;

↑一般项目都都采用此方案()

*通过面向对象角度,使用设计模式(组件|组合),将数据都抽取到一个对象中。将多个对象组合在一起,达到重复利用目的。

例2:抽取数据(地址)到一个对象中, 这个值对象既为组件,组件不支持共享引用

public class Address {
	//值对象 (vo 值对象/po 持久对象/bo 业务对象)

	private String addr;
	private String tel;
public class Person {

	private Integer pid;	//OID 值
	private String name;
	//第二种方案:组合
	private Address homeAddress;
	private Address companyAddress;

组件映射配置

<hibernate-mapping>
	<class name="com.itheima.b_component.Person" table="t_person">
		<id name="pid">
			<generator class="native"></generator>
		</id>
		<property name="name"></property>

		<component name="homeAddress" class="com.itheima.b_component.Address">
			<property name="addr" column="homeAddr"></property>
			<property name="tel" column="homeTel"></property>
		</component>
		<component name="companyAddress" class="com.itheima.b_component.Address">
			<property name="addr" column="companyAddr"></property>
			<property name="tel" column="companyTel"></property>
		</component>

	</class>

</hibernate-mapping>

注意:必须确定组合javabean类型  <component class="">

每一个对象属性必须在表中都存在独有列名。<property column="...">

继承映射

应用场景:

公司的员工,保存:小时工和正式员工

员工类

public class Employee {

	private Integer eid;
	private String name;

小时工

public class HourEmployee extends Employee {

	private int rate;	//小时薪水

正式员工

public class SalaryEmployee extends Employee {

	private int salary; //正式薪金

简单方式

public class Employee {

	private Integer eid;
	private String name;
	private String temp;//标志 1;小时工2;正式工
	private int money;  //金额

继承方式1:<sub-class>

*所有内容保存一张表,给表提供表示字段,每一个子类具有独有字段

缺点:存在null值。

配置

继承方式2:<joined-subclass> (掌握)

*将提供三种表,主表存放基本信息,两个从表存放独有字段内容。

表之间存在一对一关系

一对一:

方式1:主表的主键,与从表的外键(唯一),形成主外键关系

方式2:主表的主键,与从表都主键,形成主外键关系

继承方式3:<union-class>

*将生成3张表,彼此之间没有关系,但主键值逐渐递增,需要hibernate自动维护三张表之间的主键唯一

*缺点: 主键生成策略 increment,不能通过数据库自动生成。存在并发问题

每一次都执行select语句,从三张表中查询oid的最大值。

配置

抓取(检索)策略--优化

*开发中,使用都是默认值,最后进行优化,根据业务要求进行相应设置

3.1检索方式

*立即检索:在执行查询方法之后,立即执行select语句,进行查询。

//class 标签中lazy=false 立即检索
public void loadCustomerfalse(){
	Session session=sessionFacoty.openSession();
	Transaction tx=session.beginTransaction();
	//该行代码让hibernate执行select语句,查询数据库
	Customer c=(Customer)session.load(Customer.class, 1);
	c.getId();
	c.getAge();
	tx.commit();
	session.close();
}

*延迟检索:在执行查询方法之后,没有进行查询,底层生成代理对象,直到需要相应的数据,再进行查询。

//class 标签中lazy=true 延迟检索
public void loadCustomertrue(){
       Session session=sessionFacoty.openSession();
       Transaction tx=session.beginTransaction();
       Customer c=(Customer)session.load(Customer.class, 1);
       //该行代码让hibernate执行select语句,
       //查询数据库(需要用的时候查数据库)
      c.getId();
      c.getAge();
      tx.commit();
      session.close();
}

-相应的数据:除OID之外的值,关联数据。

-延迟加载在开发中,主要好处,延缓数据加载,在使用时才进行加载 (缩短数据在内存中时间)

-理解延迟检索中的代理

3.2检索类型

*类级别检索:当前对象所有属性值。例如:Customer自己数据

  Customer c=(Customer)session.load(Customer.class, 1);

  session的方法直接检索Customer对象,对Customer对象到底采用立即检索 还是延迟检索方式

通过class元素的lazy属性设定

*关联级别检索:当前对象关联对象数据。例如:Customer 关联 Order 数据

     Customer c=(Customer)session.load(Customer.class, 1);

     Set set=c.getOrders()//检索Order对象的set集合

在这个例子中

   session.load(Customer.class, 1):查询的主体表

     c.getOrders().size():查询客体表的集合大小

查询客体表是否发生,以何种方式发生(立即检索、延迟检索和迫切左外连接检索),就是关联级别检索

通过set元素lazy属性设定

3.3类级别

*类级别:查询当前类的所有内容,只查询一次。优化指的是查询时机优化,让空闲时间服务器做出其他处理。

*session.get(Customer.class ,oid) 通过OID立即检索(查询),如果数据不存在返回null。

*session.load(Customer.class , oid ) 默认通过OID延迟检索,如果数据不存在将抛异常。

类级别配置,只对load方法有效。

Customer.hbm.xml <class name="" table=""lazy="true|false">

true:默认值,延迟检索

false:立即检索。

*应用:

-如果程序加载一个对象的目的是为了访问它的属性, 可以采取立即检索.

-如果程序加载一个持久化对象的目的是仅仅为了获得它的引用, 可以采用延迟检索

3.4一对多和多对多的检索策略

*无论 <class>元素的 lazy 属性是 true 还是 false,

-Session 的 get() 方法及 Query 的 list() 方法在类级别总是使用立即检索策略

*若 <class> 元素的 lazy 属性为 true 或取默认值,

-Session 的 load() 方法不会执行查询数据表的 SELECT 语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:

>由 Hibernate 在运行时采用 javassist 工具动态生成

>Hibernate创建代理类实例时, 仅初始化其 OID 属性

>在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例

*对比

fetch="join",lazy无效,hibernate 将使用“迫切左外连接”,底层执行sql语句就是“左外连接”

只执行一条select,将当前对象及关联对象数据一次性查询出来。

fetch="select",默认值,执行多条select语句

lazy="false"立即执行,在执行get方法时,立即执行多条select语句。

lazy="true"延迟执行,在执行get方法时先查询客户Customer,直到使用Order数据时才查询Order

lazy="extra"极其懒惰,在执行get方法时先查询客户Customer(select t_customer),

如果需要order 订单数,将执行聚合函数只查询数量(select count()t_order),

如果需要详情再查询详情(select t_order))。

fetch="subselect",采用子查询

lazy取值与 fetch="select"相同。

注意:必须使用Query进行查询。

*延迟检索和增强延迟检索

-在延迟检索(lazy 属性值为 true) 集合属性时, Hibernate 在以下情况下初始化集合代理类实例

>应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains() 等方法

>通过 Hibernate.initialize() 静态方法显式初始化

-增强延迟检索(lazy 属性为 extra): 与 lazy=“true” 类似.

主要区别是增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机:

>当程序第一次访问 orders 属性的 iterator() 方法时, 会导致 orders 集合代理类实例的初始化

>当程序第一次访问 order 属性的 size(), contains() 和 isEmpty() 方法时, Hibernate 不会初始化 orders 集合类的实例, 仅通过特定的     select 语句查询必要的信息, 不会检索所有的 Order 对象

*用带子查询的 select 语句整批量初始化 orders 集合(fetch属性为“subselect”)

<set> 元素的 fetch 属性: 取值为“select”或“subselect”时, 决定初始化 orders 的查询语句的形式;

--若取值为”join”, 则决定 orders 集合被初始化的时机.默认值为 select

--当 fetch 属性为 “subselect” 时

>假定 Session 缓存中有 n 个 orders 集合代理类实例没有被初始化,

Hibernate 能够通过带子查询的 select 语句, 来批量初始化 n 个 orders 集合代理类实例

*迫切左外连接检索(fetch 属性值设为 “join”)

-当 fetch 属性为 “join” 时:

>检索 Customer 对象时, 会采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的 Order 对象

>lazy 属性将被忽略

>Query 的list() 方法会忽略映射文件中配置的迫切左外连接检索策略, 而依旧采用立即检索还是延迟加载策略由set集合的lazy属性决定

3.5多对一和一对一关联的检索策略

*共同特点拥有一方(<many-to-one> /<one-to-one>)

\

fetch="join" , lazy 无效,hibernate使用“迫切左外连接”,一次性查询订单和关联客户

fetch="select" 查询多条select语句

lazy="false"立即 ,执行两条select语句

lazy="proxy"代理,order关联客户Customer,客户是否延迟,取决客户类级别检查策略。

Customer.hbm.xml<class lazy="false"> 立即客户

Customer.hbm.xml<class lazy="true"> 延迟客户

lazy="no-proxy"不研究

*批量检索  从一的一端查询  查询所有的客户

-<set> 元素有一个 batch-size 属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量.

批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能.  默认值是1

注:query.list()属于hql检索,hql检索忽略关联级别的迫切左外连接检索,只与lazy属性有关.

配置

批量检索  从多的一端查询  查询所有的订单

-在Customer.hbm.xml文件中增加batch-size属性

*比较三种检索策略

检索方式总结

1、检索方式

1.使用OID获取

>session.get(User.class,1);立即加载如果不存在,就返回null

>session.load(User.class,2); 延迟加载 如果不存在,就抛异常

PS:使用以上两个方法进行查询,结果都为持久态对象,持久对象就在一级缓存中。

2.导航对象图检索方式

>customer.getOrderSet().size()

3.使用HQL

>session.createQuery("FROMEmployee e WHERE e.id>? ORDER BY e.id DESC").list();

4.原生的SQL方式

>session.createSQLQuery(Stringsql);

5.QBC 检索方式: 使用 QBC(QueryBy Criteria) API 来检索对象.

Query By Criteria提供 纯面向对象查询语句

2、 HQL 是 Hibernate最常用检索方式

*与SQL语法基本一致,不同的是HQL是面象对象的查询,查询的是对象和对象中的属性

*HQL的关键字不区分大小写,但类名和属性名区分大小写

支持 所有 SQL支持检索方式

步骤 :

1) 获得Session

2) 编写HQL

3) 通过 session.createQuery(hql) 创建Query对象

4) 为Query对象 设置条件参数

5) 执行查询 list() ---- 返回一个集合列表 、 uniqueResult();--- 返回一个查询结果

* Qurey 接口支持方法链编程风格  ,将上面所有步骤写入一句程序代码中

3、 编写测试用例,创建初始数据

创建4个Customer , 每个Customer 创建 10个 Order

4、 简单查询, hibernate 企业开发主流查询 HQL 和 QBC

* 查询所有数据

              //HQL
              Stringhql = "from Customer";
              Queryquery = session.createQuery(hql);
              List<Customer>list = query.list();
              System.out.println(list);

              //QBC
              Criteriacriteria = session.createCriteria(Customer.class);
              List<Customer>list2 = criteria.list();
              System.out.println(list2);

*HQL 和 QBC 都支持链式编程写法

            List<Customer>list3 = session.createQuery("from Customer").list();

5、 本地SQL 检索

*编写及其复杂的查询,企业内部大多使用 SQL语句

//内连接 写法一 : select *from A inner join B on A.id = B.A_id;
//内连接 写法二 (隐式): select* from A,B where A.id = B.A_id ;
Stringsql = "select * from customers
	,orders where customers.id =orders.customer_id and customers.name = ?";
SQLQuerysqlQuery = session.createSQLQuery(sql);
//设置参数
sqlQuery.setParameter(0,"mary");
Listlist = sqlQuery.list();
System.out.println(list);

*当返回很多列时,默认将每条记录保存在 Object[]中, 返回 List<Object[]>

*将返回结果 与实体类 绑定,将每条数据 转换实体类对象

String sql = "select orders.* from customers
	,orders where customers.id = orders.customer_id and customers.name = ?";
sqlQuery.addEntity(Order.class);

6、 编写HQL时,通过as关键字 为类 起别名

from Customer as c where c.name=:custname

原来写法: from Customer wherename=:custname

使用别名时 ,as可以省略 fromCustomer c where c.name=:custname

* 别名主要使用关联复杂查询时

7、 多态查询

hibernate 检索一个类 对应数据时, 将类所有子类(PO类) 对应数据表记录返回

session.createQuery("fromjava.lang.Object").list();

* 将Object 所有子类 对应数据表的 数据查询返回

from 关键字后面,如果PO类,省略包名, 如果不是PO类,必须写完整包名类名

8、 查询结果排序

//HQL
Stringhql = "from Customer order by name asc";
Listlist = session.createQuery(hql).list();
System.out.println(list);

//QBC
Listlist2 =session.createCriteria(Customer.class)
	.addOrder(org.hibernate.criterion.Order.asc("name")).list();
System.out.println(list2);

9、 分页查询

Query 接口和 Criteria 接口 都提供 setFirstResult 、setMaxResults 两个方法,用于分页查询

*  setFirstResult 起始记录索引,第一条数据 索引 0

*  setMaxResults 查询返回记录条数

案例:

              //分页查询,返回 25-34条订单
              //HQL
              Stringhql = "from Order";
              Queryquery = session.createQuery(hql);
              //设置分页参数
              query.setFirstResult(24);// 索引 是 起始记录 -1
              query.setMaxResults(10);   

10、 检索单一对象  query.uniqueResult() 、 criteria.uniqueResult()

* 该方法主要用于,只有1条数据结果

* 什么情况只有一条结果 : 用户登录、使用聚集函数 sum、count、avg、max、min

//查询mary的信息
Customercustomer = (Customer) session
	.createQuery("from Customer where name ='mary'").uniqueResult();
System.out.println(customer);

//使用聚集函数 -- 查询客户最大年龄
Integerage = (Integer) session
	.createQuery("select max(id) fromCustomer").uniqueResult();
System.out.println(age);

如果查询结果 只有一条记录或者 无记录,使用uniqueResult 是没问题的, 但是如果查询结果大于 一条记录,报错

org.hibernate.NonUniqueResultException:query did not return a unique result: 4

===============================================================================================================================

11、 带有参数条件的查询 (重点)

1) 单表条件查询

HQL写法:

Customer customer1 = (Customer)session.createQuery("from Customer where name = ?")
	.setParameter(0,"tom").uniqueResult();
Customer customer2 = (Customer)session.createQuery("from Customer where name =:cname")
	.setParameter("cname", "tom").uniqueResult();

QBC写法:

Customer customer3 = (Customer)session.createCriteria(Customer.class)
	.add(Restrictions.eq("name","tom")).uniqueResult();

*Restrictions 用来添加查询条件 ,面向对象条件查询

将参数绑定到一个持久化对象

Customercustomer = new Customer();
customer.setId(1);
Listlist2 = session.createQuery("from Order where customer =?")
	.setEntity(0, customer).list(); // 通过customer_id 查询

* 简化为

List list = session.createQuery("from Order where customer.id =?")
	.setParameter(0, 1).list(); 

* setEntity 关联对象 ,必须要有OID ,否则会报错

使用QBC 为参数绑定 持久化对象

Listlist3 =session.createCriteria(Order.class)
	.add(Restrictions.eq("customer",customer)).list(); // 通过customer_id 查询

2) 多表条件查询

hibernate  HQL 支持7种 连接写法

*(SQL标准)内连接 inner join  可以省略 inner,直接 join

*迫切内连接 inner joinfetch  ------ 不是SQL写法,是hibernate 提供

*隐式内连接 不写任何关键字,完成表关联

*(SQL标准)左外连接 left outer join ,可以省略 outer ,直接 left join

*迫切左外连接 left outerjoin fetch ----- 不是SQL语法

*(SQL标准)右外连接 right outer join

*(SQL标准)交叉连接 (笛卡尔积 )

问题: 区分内连接和迫切内连接,左外连接和迫切左外连接

*左外连接

List list =session.createQuery( "from Customer c left outer joinc.orders").list(); 

返回 List<Object[]>

每个数组两个元素 ,一个Customer 一个Order

*迫切左外连接

List list =session
	.createQuery("select distinct c from Customer c left outer joinfetch c.orders").list();

返回 List<Customer> 保存所有Customer对象,需要distinct 排重重复

问题:多表关联条件查询,隐式内连接 和 QBC方式

//隐式内连接 o.customer 当做Customer类数据表
List<Order>list = session
	.createQuery("from Order o where o.customer.name =?")
	.setParameter(0, "mary").list();

//QBC 连接查询,必须使用 criteria.createAlias()

Criteriacriteria = session.createCriteria(Order.class);
criteria.createAlias("customer","c"); // 为订单关联Customer属性起了别名
criteria.add(Restrictions.eq("c.name","mary"));
Listlist2 = criteria.list();

*  在 createAlias 默认使用 inner join 内连接

criteria.createAlias("customer","c", Criteria.LEFT_JOIN); 在关联时使用左外连接

12 、投影查询

查询结果仅包含实体的部分属性

*  查询Customer的所有 name,age 属性

HQL方式

session.createQuery("selectname,age from Customer"); 

返回 List<Object[]>

*将结果保存Customer对象中,提供name和age 构造方法

       session.createQuery("selectnew Customer(name,age) from Customer"); 

返回 List<Customer>

可以将查询结果 保存List 或者 Map集合

*select new list(name,age) from Customer

*select new map(name,age) from Customer

QBC方式 (开发中不用,非常麻烦)

       Listlist3 = session
                            .createCriteria(Customer.class)
                            .setProjection(
                                          Projections.projectionList()
                                                        .add(Property.forName("name"))
                                                        .add(Property.forName("age"))).list();
 

13、 分组统计查询

count  sum avg max min

       *Long count = (Long) session.createQuery("select count(*) fromOrder").uniqueResult();
       *List list2 = session.createQuery("select count(*) from Order group bycustomer").list();
 

14、 命名查询语句

在开发中 hql语句 写到代码中,不方便维护, 将HQL定义到配置文件中 ------------ 命名查询语句

在hbm映射文件 (也可以用注解配置)

       <!--这里可以定义命名查询 -->
       <!--       定义 HQL 语句 <queryname=""></query> -->
       <!--       定义 SQL 语句 <sql-queryname=""></sql-query> -->
       <queryname="findCustomerByName">
              <![CDATA[fromCustomer where name = ?]]>
       </query>

* 为hql语句 起了一个名字

程序代码

       Queryquery = session.getNamedQuery("findCustomerByName");
       query.setParameter(0,"tom");
       Customercustomer = (Customer) query.uniqueResult();

15、 离线Criteria对象 --- DetachedCriteria

*主要用于javaee分层开发,可以在web层封装查询条件,传递数据层 关联Session进行查询

       DetachedCriteriadetachedCriteria = DetachedCriteria.forClass(Customer.class);
       detachedCriteria.add(Restrictions.eq("name","kitty"));

       //传递数据层
       Sessionsession = HibernateUtils.openSession();
       Transactiontransaction = session.beginTransaction();

       //将离线查询对象 关联到Session
       Criteriacriteria = detachedCriteria.getExecutableCriteria(session);
       Customercustomer = (Customer) criteria.uniqueResult();

HQL和QBC比较: 两种查询功能类似, 简单查询建议编写HQL,对于复杂查询,多条件组合查询 建议编写 QBC方式

============================================================================================================================

时间: 2024-10-10 01:05:14

框架 day33 Hibernate,组件映射,继承映射,抓取(检索)策略-优化,检索方式总结的相关文章

Hibernate缓存、组件、继承映射

Hibernate缓存.组件.继承映射 三种状态: 临时状态:不受session管理,没有提交到数据库:没有执行sql之前,new对象的时候: 持久化状态:受session管理,提交到数据库:正在执行sql 游离状态:不受session管理,提交到数据库:session关闭后 Cache缓存:会先看看缓存里有没有,有就取出来,没有就到数据库取数据. Session的三个方法:flush.evict.clear 不同session不会共享数据. List与iterator的区别: List是直接到

【Hibernate步步为营】--继承映射详解

上篇文章讨论了多对多映射,在使用多对多映射时重点是使用<many-to-many>标签,并在标签的两端加入外键这样在生成关系时会创建两个关系之间的关系表,通过关系表来维护它们之间的关系,另外对于单向和双向的区别是在映射的哪一端添加标签的问题.在面向对象中很重要的一个特性就是继承,继承实现了代码的复用,而且Hibernate把基本上所有的对象模型进行了映射封装,其中就包括继承映射,接下来就详细讨论. 一.继承映射 继承是面向对象很重要的特性,它实现了代码的服用,在关系模型中同样也有继承关系,这种

【Hibernate步步为营】--继承映射具体解释

上篇文章讨论了多对多映射,在使用多对多映射时重点是使用<many-to-many>标签,并在标签的两端加入外键这样在生成关系时会创建两个关系之间的关系表,通过关系表来维护它们之间的关系,另外对于单向和双向的差别是在映射的哪一端加入标签的问题.在面向对象中非常重要的一个特性就是继承,继承实现了代码的复用,并且Hibernate把基本上全部的对象模型进行了映射封装,当中就包含继承映射,接下来就具体讨论. 一.继承映射 继承是面向对象非常重要的特性,它实现了代码的服用,在关系模型中相同也有继承关系,

Hibernate 原汁原味的四种抓取策略(转)

原文出处:http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html     尊重原作者,访问原创地址 最近在研究 Hibernate 的性能优化的时候碰到了"抓取策略", 由于以前没有详细的研究过, 所以到处找资料, 但是无论从一些讲 Hibernate 书籍,还是他人 Blog 中都没有找到详细 介绍 Hibernate 文档中所说的原汁原味的抓取策略, 综合懒加载等等特性混在了一起, 所 以在这自己在借鉴了他人的基

Android 7.0 以上 Charles 和 Fiddler 无法抓取 HTTPS 包的解决方式

Android 7.0 以上 Charles 和 Fiddler 无法抓取 HTTPS 包的解决方式 https://johnnyshieh.me/posts/android-7-capture-https-package/ 发表于 2019-05-06 | 分类于 Android| 0| 阅读次数 最近升级了 targetSdkVersion 到 28 后发现在 Android 7.0 以上机型 Charles 抓取 https 包时显示找不到证书,但是 Android 6.0 机型还是可以正

Hibernate中的继承映射

1.继承映射 继承映射分为两种情况:简单继承映射和继承映射. 在简单继承映射中,每个子类都要写一个映射文件. 在继承映射中,只使用一个映射文件.继承映射分为三种情况: 所有子类映射到一张表 需要使用鉴别器字段,不符合数据库设计原则 每个类映射到一张表 父类映射到一张表,插入一条子类信息,需要两条sql,父子表之间有外键约束 每个类映射到一张表,父类不映射表 必须使用uuid作为主键类型,所以bean中主键对应的属性不能是int类型 2.简单继承映射 <?xml version="1.0&q

hibernate detached分离查询 与 抓取策略注意事项

1.detached在抓取策略为 jion显式左外连接查询情况下 会产生笛卡儿积现象 DetachedCriteria dc = DetachedCriteria.forClass(Topic.class); dc.add(Restrictions.like("title", "%%")); Criteria criteria = dc.getExecutableCriteria(session); criteria.setFirstResult((currentp

Hibernate检索策略与检索方式

hibernate的Session在加载Java对象时,一般都会把鱼这个对象相关联的其他Java对象也都加载到缓存中,以方便程序的调用.但很多情况下,我们不需要加载太多无用的对象到缓存中,一来会占用大量的内存,二来会增加数据库的访问次数,使得程序的运行效率降低.为了合理的使用缓存,Hibernate提供了不同的检索策略来解决这些问题. 作用域 在说检索策略之前,先了解一下检索策略的作用域,它分为:类级别和关联级别.类级别的作用域在检索数据时,检索的数据只包含一个类对象,不涉及与该类关联的其他对象

[原创]java WEB学习笔记88:Hibernate学习之路-- -Hibernate检索策略(立即检索,延迟检索,迫切左外连接检索)

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------