溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于Mybatis的配置文件的原理

發(fā)布時間:2021-07-13 17:32:54 來源:億速云 閱讀:106 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要介紹“基于Mybatis的配置文件的原理”,在日常操作中,相信很多人在基于Mybatis的配置文件的原理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”基于Mybatis的配置文件的原理”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

從下面這段代碼是我們在使用mybatis前的配置初始化過程

我們通過閱讀其源碼來逐步了解內(nèi)部實現(xiàn)原理。

// Mybatis 通過SqlSessionFactory獲取SqlSession, 然后才能通過SqlSession與數(shù)據(jù)庫進行交互
 private static SqlSessionFactory getSessionFactory() {
  SqlSessionFactory sessionFactory = null;
  String resource = "configuration.xml";
  try {
   sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
  } catch (IOException e) {
   e.printStackTrace();
  }
  return sessionFactory;
 }

我們進入到SqlSessionFactoryBuilder類里面

查看其源碼:

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.session; 
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
 
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; 
/**
 * Builds {@link SqlSession} instances.
 *
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {
 
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }
 
  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }
 
  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
 
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
 
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
 
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }
 
  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
 
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
 
}

在這個類中,支持多種構(gòu)造SqlSessionFactory的方法??梢灾粋魅雖ybatis配置文件,也可以同時傳入properties配置文件替代mybatis配置文件中的<properties>元素標(biāo)簽,另外也支持傳入環(huán)境參數(shù)envirmont參數(shù)。

我們跟隨著源碼繼續(xù)往下看:

 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

這里創(chuàng)建了一個XMLConfigBuilder類實例,通過他來對mybatis配置文件(一個xml配置文件)進行解析。

解析的代碼入口如下所示:

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
 
  private void parseConfiguration(XNode root) {
    try {
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

從這里看出,配置文件是以configuration為根節(jié)點,在根節(jié)點之下有多個子節(jié)點,它們分別為:settings、properties、typeAliases、plugins、objectFactory、objectWrapperFactory、environments、databaseIdProvider、typeHandlers、mappers。

MyBatis核心配置文件標(biāo)簽簡介

XML 映射配置文件

MyBatis的配置文件包含了影響MyBatis行為甚深的設(shè)置(settings)和屬性(properties)信息。文檔的頂層結(jié)果如下:

configuration配置

properties屬性

setting設(shè)置

typeAliases類型命名

typeHandlers類型處理器

objectFactory對象工廠

plugins插件

environments環(huán)境

environment環(huán)境變量

transactionManager事務(wù)管理器

dataSource數(shù)據(jù)源

databaseIdProyider數(shù)據(jù)庫廠商標(biāo)識

mappers映射

properties

屬性都是可外部配置且可動態(tài)替換的,既可以在典型的Java屬性文件中配置,亦可通過properties元素的子元素來傳遞。

例如:

<!-- 
  mybatis的核心配置文件
   1.數(shù)據(jù)庫的連接信息(連接池)
  -->
  <properties resource="jdbc.properties"></properties>

其中的屬性就可以在整個配置文件中使用來替換需要動態(tài)配置的屬性值。

比如:

  <!-- 默認(rèn)連接池 -->
  <dataSource type="POOLED">
       <property name="driver" value="${driverClass}"/>
       <property name="url" value="${url}"/>
       <property name="username" value="${userid}"/>
       <property name="password" value="${password}"/>
   </dataSource>

properties屬性:將數(shù)據(jù)庫連接參數(shù)單獨配置在jdbc.properties中,只需要在mybatis.xml文件中加載jdbc.properties的屬性值。 在mybatis.xml中就不需要對數(shù)據(jù)庫連接參數(shù)硬編碼(硬編碼是指將可變變量用一個固定值來代替的方法)。在properties 元素體內(nèi)定義的屬性首先被讀取。然后會讀取properties元素中resource或url加載屬性,它會覆蓋已讀取的同名屬性。

注意:如果在properties標(biāo)簽里面定義的屬性被${}所引用了,對#{}不管用。那么它不會讀取parameterType里面的參數(shù)值。比如properties里面定義了id屬性,值為40,在映射文件中引用該值,${id}那么我從parameterType里面?zhèn)髦禃r,不管我傳基本類型還是引用類型進去都不會覆蓋這個${id}值。始終都會讀取40.

屬性也可以被傳遞到SqlSessionBuilder.build()方法中。

例如:

SqlSessionFactoryBuilder源碼:

 public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
 
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
typeAliases

類型別名是為Java類型設(shè)置一個短的名字。它只和XML配置有關(guān),存在意義僅在于用來減少類完全限定名的冗余。

例如:

 <!-- 給類定義別名 -->
  <typeAliases>
   <typeAlias type="cn.et.lesson02.annotion.Food" alias="food"/>
   <typeAlias alias="Author" type="domain.blog.Author"/>
    <typeAlias alias="Blog" type="domain.blog.Blog"/> 
  </typeAliases>

當(dāng)這樣配置時,Blog可以用在任何使用domain.blog.Blog的地方。

也可以指定一個包名,MyBatis會在包名下面搜索需要的Java Bean

比如:

<typeAliases> <package name="domain.blog"/> </typeAliases>

每一個包domain.blog中的Java Bean,在沒有注解的情況下,會使用Bean的首字母小寫的非限定類名來作為它的別名。比如domain.blog.Author的別名為author;若有注解,則別名為其注解值。

看下面的例子:

@Alias("author") public class Author { ... }

mapper標(biāo)簽(映射配置):加載映射文件

<mappers>
   <!-- 
    通過resource加載單個映射文件
     加載單個類路徑下的映射文件 
      使用相對于類路徑的資源:
   -->
   <mapper resource="cn/et/lesson01/FoodMapper.xml"/>
   <!-- 
     使用類路徑加載單個映射文件
        <mapper url="cn.domarvel.dao.UserMapper"/>
         注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。并且還有一個前提是:使用的是mapper代理方法
    -->
    <!-- 
      自動批量加載指定包下的所有Mapper接口配置文件
        <package name="cn.domarvel.dao"/>
                     注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。并且還有一個前提是:使用的是mapper代理方法
     -->
  </mappers>

Mapper XML文件

MyBatis的真正強大在于他的映射語句,也是它的魔力所在,由于它的異常強大,映射器的XML文件就顯得相對簡單。如果拿它跟具有相同功能的JDBC代碼進行對比,你會立即發(fā)現(xiàn)省掉了將近95%的代碼。MyBatis就是針對SQL構(gòu)建的,并且比普通的方法做的更好。

mapper標(biāo)簽:映射文件的根節(jié)點,在根節(jié)點中支持九個元素。

namespace是用于綁定Dao接口的,當(dāng)你的namespace綁定接口后,你可以不用寫接口實現(xiàn)類,mybatis會通過該綁定自動幫你找到對應(yīng)要執(zhí)行的SQL語句

注意:接口中的方法與映射文件中的SQL語句的ID一一對應(yīng)

<mapper namespace="a">
</mapper>
<mapper namespace="cn.et.lesson02.xml.FoodMapper" >
</mapper>

SQL映射文件有很少的幾個頂級元素(按照它們應(yīng)該被定義的順序):

cache:給定命名空間的緩存配置

cache-ref:其命名空間緩存配置的引用。

resultMap:是最復(fù)雜也是最強大的元素,用來描述如何從數(shù)據(jù)庫結(jié)果集中來加載對象。

parameterMap:已廢棄,老式風(fēng)格的參數(shù)映射。內(nèi)聯(lián)參數(shù)是首選,這個元素可能在將來被移除,這里不會記錄。

sql:可被其他語句引用的可重用語句塊。

insert:映射插入語句

update:映射更新語句

delete:映射更新語句

select:映射查詢語句

select

查詢語句是MyBatis中最常用的元素之一,光能把數(shù)據(jù)存到數(shù)據(jù)庫中價值并不大,如果還能重新取出來才有用,多數(shù)應(yīng)用也都是查詢比修改要頻繁。對每個插入、更新或刪除操作,通常對應(yīng)多個查詢操作。這是MyBatis的基本原則之一,也是將焦點和努力放在查詢和結(jié)果映射的原因。簡單查詢的select元素時非常簡單的。

比如

<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>

這個語句被稱作selectPerson,接收一個int(或Integer)類型的參數(shù),并返回一個HashMap類型的對象,其中的鍵是列名,值便是結(jié)果行中的對應(yīng)值。

注意參數(shù)符號:#{id}

select元素有很多屬性允許你配置,來決定每條語句的作用細(xì)節(jié)。

基于Mybatis的配置文件的原理

SELECT多條件查詢

parameterType用于傳遞參數(shù)多參數(shù)可以使用傳入對象以及map的方式傳遞多個參數(shù)。

#{}表示傳遞的參數(shù)值 類同jdbc的? ${}表示直接將參數(shù)值替換類同'%值%'

比如:

<select id=“selectPerson” parameterType=“map”    resultType=“person”> 
 SELECT * FROM PERSON WHERE ID = #{id} and name like ‘%${name}%' 
</select>

Map中必須存在id和name的鍵值對

SELECT調(diào)用存儲過程

創(chuàng)建存儲過程prg_add(p1 in number,p2 in number,p3 out number)

Mybatis映射文件中使用select調(diào)用存儲過程

<select id=“prgAdd" statementType="CALLABLE">  <![CDATA[  
{call pro_hello (
 #{p1,mode=IN,jdbcType=NUMBER},
 #{p2,mode=IN,jdbcType=NUMBER},
 #{result,mode=OUT,jdbcType=NUMBER})}  
   ]]>  
 </select>

測試調(diào)用過程

Map<String, String> param = new HashMap<String, String>();  
param.put(“p1”, 1);  param.put(“p2”, 2);  
String returnValue = (String) session.selectOne(" prgAdd ", param);  
System.out.println("result=" + param.get("result"));  
System.out.println("returnValue=" + returnValue);
insert、update和delete

數(shù)據(jù)更變語句insert、update和delete的實現(xiàn)非常接近

<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20">
<update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> 
<delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
insert、update和delete參數(shù)

基于Mybatis的配置文件的原理

parameterType:入?yún)⒌娜薅惷蝾愋蛣e名

keyColumn:設(shè)置數(shù)據(jù)表自動生成的主鍵名。對特定數(shù)據(jù)庫(如PostgreSQL),若自動生成的主鍵不是第一個字段則必須設(shè)置

keyProperty:默認(rèn)值unset,用于設(shè)置getGeneratedKeys方法或selectKey子元素返回值將賦值到領(lǐng)域模型的哪個屬性中

useGeneratedKey:取值范圍true|false(默認(rèn)值)設(shè)置是否使用JDBC的getGenereatedKeys方法獲取主鍵并賦值到keyProperty設(shè)置的領(lǐng)域模型屬性中。

MySQL和SQLServer執(zhí)行auto-generated key field,因此當(dāng)數(shù)據(jù)庫設(shè)置好自增長主鍵后,可通過JDBC的getGeneratedKeys方法獲取。但像Oralce等不支持auto-generated key field的數(shù)據(jù)庫就不能用這種方法獲取主鍵了

flushCache:取值范圍true(默認(rèn)值)|false,設(shè)置執(zhí)行該操作后是否會清空二級緩存和本地緩存

timeout:默認(rèn)為unset(依賴jdbc驅(qū)動器的設(shè)置),設(shè)置執(zhí)行該操作的最大時限,超時將拋異常

databaseId:取值范圍oracle|mysql等,表示數(shù)據(jù)庫廠家,元素內(nèi)部可通過`<if test="_databaseId = 'oracle'">`來為特定數(shù)據(jù)庫指定不同的sql語句

selectKey

對于不支持自動生成類型的數(shù)據(jù)庫或可能不支持自動生成主鍵JDBC驅(qū)動來說,MyBatis有另外一種方法來生成主鍵。

這里有一個簡單的示例,它可以生成一個隨機ID(最好不要這么做,但這里展示了MyBatis處理問題的靈活性及其所關(guān)心的廣度):

<insert id="saveFood" >
  
	<!-- 
	 selectKey 在Mybatis中是為了解決Insert數(shù)據(jù)時不支持主鍵自動生成的問題,他可以很隨意的設(shè)置生成主鍵的方式。
	 		   SelectKey需要注意order屬性,像Mysql一類支持自動增長類型的數(shù)據(jù)庫中,
	 		   order需要設(shè)置為after才會取到正確的值。像Oracle這樣取序列的情況,需要設(shè)置為before,否則會報錯。
	 	keyProperty:selectKey 語句結(jié)果應(yīng)該被設(shè)置的目標(biāo)屬性。
	 	order:可設(shè)置為 BEFORE 或 AFTER,如果設(shè)置為 BEFORE,那么它會首先選擇主鍵,設(shè)置 keyProperty 然后執(zhí)行插入語句。
	 								如果設(shè)置為 AFTER,那么先執(zhí)行插入語句,然后是 執(zhí)行selectKey元素
		statementType:和前面的相 同,MyBatis 支持 STATEMENT ,PREPARED 和CALLABLE 語句的映射類型,
					       分別代表 PreparedStatement 和CallableStatement 類型。	
					       
		注意:selectKey操作會將操作查詢結(jié)果賦值到insert元素的parameterType的入?yún)嵗聦?yīng)的屬性中。并提供給insert語句使用
	 -->
 	<selectKey keyProperty="foodId" order="BEFORE" resultType="int" statementType="STATEMENT">
 		select FOOD_SEC.Nextval from dual
 	</selectKey>
	 insert into food values(#{foodId},#{foodName},#{price})
 </insert>

在上面的示例中,selectKey元素將會首先運行,food的id會被設(shè)置,然后插入語句會被調(diào)用,這給了你一個和數(shù)據(jù)庫中來處理自動生成的主鍵類似的行為,避免了使Java代碼變得復(fù)雜

SQL

這個元素可以被用來定義可重用的SQL代碼段,可以包含在其他語句中,它可以被靜態(tài)地(在加載參數(shù))參數(shù)化,不同的屬性值太高包含的實例變化。

比如:

<sql id="userColumns"> 
${alias}.id,${alias}.username,${alias}.password 
</sql>

這個SQL片段可以被包含在其他語句中,例如:

<select id="selectUsers" resultType="map"> select <include refid="userColumns">
<property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/>
</include> from some_table t1 cross join some_table t2 </select>

屬性值可以用于包含的refid屬性或者包含的字句里面的屬性值。

Result Map

resultMap 元素時MyBatis中最重要最強大的元素。它就是讓你遠(yuǎn)離90%的需要從結(jié)果集中取出數(shù)據(jù)的JDBC代碼的那個東西,而且在一些情形下允許你做一些JDBC不支持的事情。事實上,編寫相似于對復(fù)雜語句聯(lián)合映射這些等同的代碼,也許可以跨過上千行的代碼。ResultMap的設(shè)計就是簡單語句不需要明確的結(jié)果映射,而很多復(fù)雜語句確實需要描述它們的關(guān)系。

	<!-- 
 	resultMap  
		基本作用:建立SQL查詢結(jié)果字段與實體屬性的映射關(guān)系 
	 	屬性
	 		id屬性 ,resultMap標(biāo)簽的標(biāo)識。
	 		type屬性 ,返回值的全限定類名,或類型別名。
	 	autoMapping 屬性 ,值范圍true(默認(rèn)值)|false,設(shè)置是否啟動自動映射功能,
	 		自動映射功能就是自動查找與字段名小寫同名的屬性名,并調(diào)用setter方法。而設(shè)置為false后,
	 		則需要在resultMap內(nèi)明確注明映射關(guān)系才會調(diào)用對應(yīng)的setter方法。
 		
 		子元素:
 			id元素 ,用于設(shè)置主鍵字段與領(lǐng)域模型屬性的映射關(guān)系
			result元素 ,用于設(shè)置普通字段與領(lǐng)域模型屬性的映射關(guān)系
				property	需要映射到JavaBean 的屬性名稱。 	
 				
 				column	數(shù)據(jù)表的列名或者標(biāo)簽別名。
 				
 				javaType	一個完整的類名,或者是一個類型別名。如果你匹配的是一個JavaBean,
 							那MyBatis 通常會自行檢測到。然后,如果你是要映射到一個HashMap,
 							那你需要指定javaType 要達(dá)到的目的。
 						
 				jdbcType	數(shù)據(jù)表支持的類型列表。這個屬性只在insert,update 或delete 的時候針對允許空的列有用。
 							JDBC 需要這項,但MyBatis 不需要。如果你是直接針對JDBC 編碼,且有允許空的列,而你要指定這項。
 	 			
 	 			typeHandler	使用這個屬性可以覆寫類型處理器。這項值可以是一個完整的類名,也可以是一個類型別名。	
 	 			
 			association聯(lián)合
 						聯(lián)合元素用來處理“一對一”的關(guān)系。需要指定映射的Java實體類的屬性,屬性的javaType(通常MyBatis 自己會識別)。
 						對應(yīng)的數(shù)據(jù)庫表的列名稱。如果想覆寫的話返回結(jié)果的值,需要指定typeHandler。 
						不同情況需要告訴MyBatis 如何加載一個聯(lián)合。MyBatis 可以用兩種方式加載:
 	
 							select: 執(zhí)行一個其它映射的SQL 語句返回一個Java實體類型。較靈活;
							resultsMap: 使用一個嵌套的結(jié)果映射來處理通過join查詢結(jié)果集,映射成Java實體類型。
 	 
 	 
 	 -->
 	<resultMap type="grade" id="gradeMap" autoMapping="true" >
 		<!-- 
 			因為 autoMapping="false"(默認(rèn)是true) 所以要明確注明映射關(guān)系  如果為true則會自動映射   不需要寫下面這行代碼
 			<result column="gid" property="gid"/>
 			還有下面這種方法
 				需要gid為主鍵的情況下使用
 			
 			列和屬性的關(guān)系  主鍵使用id 非主鍵使用result
 			<id column="gid" property="gid"/>
 		-->
 		<!-- 將查詢的結(jié)果gname賦給gname1 -->
 		<id column="gid" property="gid"/>
 		<result column="gname" property="gname"/>
 	</resultMap>

到此,關(guān)于“基于Mybatis的配置文件的原理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI