導航:首頁 > 源碼編譯 > ybatis源碼閱讀

ybatis源碼閱讀

發布時間:2022-07-22 03:57:41

Ⅰ 《深入淺出MyBatis技術原理與實戰》pdf下載在線閱讀全文,求百度網盤雲資源

《深入淺出MyBatis技術原理與實戰》網路網盤pdf最新全集下載:
鏈接:https://pan..com/s/1LxgP-ibmyXjclY23yXMN-Q

?pwd=gijo 提取碼:gijo
簡介:隨著大數據時代的到來,java持久層框架MyBatis已經成為越來越多企業的選擇。遺憾的是,時至今日國內依然沒有一本討論MyBatis的書,這增加了初學者的學習難度,初學者往往只能基於零星的案例來學習MyBatis,無法系統地掌握MyBatis,更不用說精通了。《深入淺出MyBatis技術原理與實戰》是筆者通過大量實踐和研究源碼後創作而成的,是國內第一本系統介紹MyBatis的著作本書分為3個部分,依次介紹了MyBatis的基礎應用、原理及插件開發、實踐應用,使讀者能夠由法入深、循序漸進地掌握MyBatis技術。首先,本書在官方API的基礎上完善了許多重要的論述和實例,並且給出了實操建議,幫助讀者正確掌握MyBatis。其次,本書詳細講述了MyBatis的內部運行原理,並全面討論了插件的開發。最後,本著學以致用的原則,筆者闡述了MyBatis-Spring項目和一些MyBatis開發常見的實例,使讀者能夠學得會,用得好。

Ⅱ 配置sqlsessiontemplate有什麼用

工作中,需要學習一下MyBatis sqlSession的產生過程,翻看了mybatis-spring的源碼,閱讀了一些mybatis的相關doc,對mybatis sqlSession有了一些認知和理解,這里簡單的總結和整理一下。

首先, 通過翻閱源碼,我們來整理一下mybatis進行持久化操作時重要的幾個類:
SqlSessionFactoryBuilder:build方法創建SqlSessionFactory實例。
SqlSessionFactory:創建SqlSession實例的工廠。

SqlSession:用於執行持久化操作的對象,類似於jdbc中的Connection。
SqlSessionTemplate:MyBatis提供的持久層訪問模板化的工具,線程安全,可通過構造參數或依賴注入SqlSessionFactory實例。

Hibernate是與MyBatis類似的orm框架,這里與Hibernate進行一下對比,Hibernate中對於connection的管理,是通過以下幾個重要的類:
SessionFactory:創建Session實例的工廠,類似於MyBatis中的SqlSessionFactory。
Session:用來執行持久化操作的對象,類似於jdbc中的Connection。

HibernateTemplate:Hibernate提供的持久層訪問模板化的工具,線程安全,可通過構造參數或依賴注入SessionFactory實例。

在日常的開發中,我們經常需要這樣對MyBatis和Spring進行集成,把sqlSessionFactory交給Spring管理,通常情況下,我們這樣配置:
?

1
2
3

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

通過上面的配置,Spring將自動創建一個SqlSessionFactory對象,其中使用到了org.mybatis.spring.SqlSessionFactoryBean,其 是MyBatis為Spring提供的用於創建SqlSessionFactory的類,將在Spring應用程序的上下文建議一下可共享的 MyBatis SqlSessionFactory實例,我們可以通過依賴注入將SqlSessionFactory傳遞給MyBatis的一些介面。

如果通過Spring進行事務的管理,我們需要增加Spring註解的事務管理機制,如下配置:
?

1
2
3
4
5

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<tx:annotation-driven/>

這樣,我們就可以使用Spring @Transactional註解,進行事務的控制,表明所注釋的方法應該在一個事務中運行。 Spring將在事務成功完成後提交事務,在事務發生錯誤時進行異常回滾,而且,Spring會將產生的MyBatis異常轉換成適當的 DataAccessExceptions,從而提供具體的異常信息。

下面,我們通過分析SqlSessionUtils中getSession的源碼,來詳細的了解一下sqlSession的產生過程,源碼如下:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, exceptionTranslator) {

notNull(sessionFactory, "No SqlSessionFactory specified");
notNull(executorType, "No ExecutorType specified");

SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);

if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new ("Cannot change the ExecutorType when there is an existing transaction");
}

holder.requested();

if (logger.isDebugEnabled()) {
logger.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
}

return holder.getSqlSession();
}

if (logger.isDebugEnabled()) {
logger.debug("Creating a new SqlSession");
}

SqlSession session = sessionFactory.openSession(executorType);

// Register session holder if synchronization is active (i.e. a Spring TX is active)
//
// Note: The DataSource used by the Environment should be synchronized with the
// transaction either through DataSourceTxMgr or another tx synchronization.
// Further assume that if an exception is thrown, whatever started the transaction will
// handle closing / rolling back the Connection associated with the SqlSession.
if (isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();

if (environment.getTransactionFactory() instanceof ) {
if (logger.isDebugEnabled()) {
logger.debug("Registering transaction synchronization for SqlSession [" + session + "]");
}

holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
bindResource(sessionFactory, holder);
registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.(true);
holder.requested();
} else {
if (getResource(environment.getDataSource()) == null) {
if (logger.isDebugEnabled()) {
logger.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
}
} else {
throw new (
"SqlSessionFactory must be using a in order to use Spring transaction synchronization");
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
}
}

return session;
}

上面的getSession方法,會從Spring的事務管理器中獲取一個SqlSession或創建一個新的SqlSession,將試圖從當前事務中得到一個SqlSession,然後,如果配置有事務管理器的工廠並且Spring 的事務管理器是活躍的,它將會鎖定當前事務的SqlSession,保證同步。主要是通過以下幾個步驟進行SqlSession的創建:
它會首先獲取SqlSessionHolder,SqlSessionHolder用於在中保持當前的SqlSession。
如果holder不為空,並且holder被事務鎖定,則可以通過holder.getSqlSession()方法,從當前事務中獲取sqlSession,即 Fetched SqlSession from current transaction。

如果不存在holder或沒有被事務鎖定,則會創建新的sqlSession,即 Creating a new SqlSession,通過sessionFactory.openSession()方法。

如果當前線程的事務是活躍的,將會為SqlSession注冊事務同步,即 Registering transaction synchronization for SqlSession。

Ⅲ mybatis工作原理及為什麼要用

一、mybatis的工作原理:

MyBatis 是支持普通 SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎所有的JDBC代碼和參數的手工設置以及結果集的檢索。

MyBatis使用簡單的 XML或註解用於配置和原始映射,將介面和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java對象)映射成資料庫中的記錄。

每個MyBatis應用程序主要都是使用SqlSessionFactory實例的,一個SqlSessionFactory實例可以通過SqlSessionFactoryBuilder獲得。用xml文件構建SqlSessionFactory實例是非常簡單的事情。

推薦在這個配置中使用類路徑資源,但可以使用任何Reader實例,包括用文件路徑或file://開頭的url創建的實例。MyBatis有一個實用類----Resources,它有很多方法,可以方便地從類路徑及其它位置載入資源。

二、使用mybatis的原因:因為mybatis具有許多的優點,具體如下:

1、簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。

2、靈活:mybatis不會對應用程序或者資料庫的現有設計強加任何影響。 sql寫在xml里,便於統一管理和優化。通過sql語句可以滿足操作資料庫的所有需求。

3、解除sql與程序代碼的耦合:通過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。

4、提供映射標簽,支持對象與資料庫的orm欄位關系映射。

5、提供對象關系映射標簽,支持對象關系組建維護。

6、提供xml標簽,支持編寫動態sql。

(3)ybatis源碼閱讀擴展閱讀:

mybatis的功能構架:

1、API介面層:提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫。介面層一接收到調用請求就會調用數據處理層來完成具體的數據處理。

2、數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次資料庫操作。

3、基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置載入和緩存處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件。為上層的數據處理層提供最基礎的支撐。

Ⅳ 如何在mybatis中調試查看生成的sql語句

mybatis的源碼中查看生成的sql語句,參考執行以下代碼即可。具體代碼如下:把裡面PooledDataSource類的log輸出部分,換成log.warn之後,重新打jar包,放到項目中,日誌級別改為info,如:log4j.rootLogger=info,stdout,Rlog4j.appender.std

Ⅳ 如何去閱讀並學習一些優秀的開源框架的源碼 來自

沒有目標的去看所謂的源碼,沒有任何的意義。
最適合看源碼的就是,你對完成一個功能感興趣,所以你想要了解他。
比如說Mybatis,你想了解他的代碼生成機制,自己先去思考,如果是你做,該怎麼做,再去看源碼。
再比如說Log4j.打日誌的時候是非同步的嗎?如果我需要把日誌打到另外一台機器上,我該怎麼做?
帶著這些問題去思考解決方案。
那麼天底下的源碼都是你的老師。
千萬別東一錘子西一錘子的去瞎看。

Ⅵ 怎麼學習mybatis框架的源碼

剛剛好我前段時間做了一個基於SpringMVC + Mybatis + Redis + Freemarker(JSP)的許可權控制Demo。地址看下面代碼:/** * 網路不讓輸入網址 * 地址為 */String url = "

Ⅶ 哪一個框架的源碼適合拿來閱讀學習

不要看框架源碼,最好先打好基礎,比如反射,代理等。因為很多框架的功能已經十分繁多,光是功能(英文)你都難以弄懂,更何況是參合了很多中間變數、邏輯功能的源碼,越看越難以理解。即使要學習框架,也要先從javadoc注釋,即API幫助文檔開始,了解數據流的流入和流出。用源碼反向猜測設計邏輯是吃力不討好的事,一沒完整注釋,二不是本人,三反射的應用最傷腦細胞,你壓根不知道在哪裡被插了一腳。如果框架適合用來學習,那麼改別人的代碼也就不是個事了。問題來了,為何程序員都不喜歡改別人的代碼bug?

Ⅷ 如何學習hibernate源碼

我來分享一下查看源碼的方法:

查看源碼的首要任務是要有一款上手的工具,這里用的是 IDEA。IDEA 的功能比較強大,包括 查看類結構圖,debug。這兩個是查看源碼的關鍵功能。

查看源碼可以靜態查看和動態查看,靜態查看的方法是查看類圖,還有 ALT + f7 查看方法在哪裡被調用或者類在哪裡被調用。在看 spring 源碼的時候就是用這種方法,不過這種方法對閱讀者的要求比較高,包括要了解這個方法的執行,設計模式的理解,以及框架是如何配置這個類的。第二種方法是 debug。debug 方法是後來才發現的一個重要的 查看源碼的方法,要點是掌握執行棧,就能掌握整個執行流程。比如這個是在debug hibernate 源碼的時候的截圖,可以看到這個執行棧非常深,從 spring-data-jpa 到 hibernate 中間經過好幾層的代理,主要完成一些適配,事務,攔截器等等操作,然後再到 hibernate 核心代碼,最後就是 jdbc 的 statement。方法棧中的每一個方法都是可以查看的,裡面的變數有時候是代理了好幾層,所以要 F7 進去才能看到真正的執行類。

上面是簡單的簡述 mybatis 的 cache 機制的源碼,真正想讓讀者明白的是,debug 如何查看源碼,查看源碼需要抓住一個主題,不然在閱讀龐大的框架的時候會找不著北。

所以,閱讀源碼需要掌握工具使用,debug, 查看類圖,查看方法在哪裡調用,軟知識是要掌握設計模式,對框架的概念有了解。

Ⅸ 初看Mybatis 源碼 SQL是怎麼執行的

一條sql語句到底是怎麼執行的?我們知道Mybatis其實是對JDBC的一個封裝。假如我執行
session.update("com.mybatis..AuthUserDao.updateAuthUserEmailByName", [email protected]);
語句,追蹤下來,Executor、 BaseStatementHandler等等。在 SimpleExecutor 中有如下代碼:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
1. 首先獲取相關配置信息,這個在初始化時,從配置文件中解析而來
2. 新建了一個handler
3. 做了執行statement之前的准備工作。看看準備了些什麼,跟蹤代碼,最後進入了DataSource類的doGetConnection方法,該方法做如下操作:
private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}

private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver)driverType.newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);

Ⅹ 怎麼沒有mybatis源碼解析相關的文檔

我們還記得是這樣配置sqlSessionFactory的:
[java] view plain
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:configuration.xml"></property>
<property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.tiantian.mybatis.model" />
</bean>
這里配置了一個mapperLocations屬性,它是一個表達式,sqlSessionFactory會根據這個表達式讀取包com.xxx.myts.mapper下面的所有xml格式文件,那麼具體是怎麼根據這個屬性來讀取配置文件的呢?
答案就在SqlSessionFactoryBean類中的buildSqlSessionFactory方法中:

[java] view plain
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}

try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}

if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
}

mybatis使用XMLMapperBuilder類的實例來解析mapper配置文件。
[java] view plain
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}

[java] view plain
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
接著系統調用xmlMapperBuilder的parse方法解析mapper。

[java] view plain
public void parse() {
//如果configuration對象還沒載入xml配置文件(避免重復載入,實際上是確認是否解析了mapper節點的屬性及內容,
//為解析它的子節點如cache、sql、select、resultMap、parameterMap等做准備),
//則從輸入流中解析mapper節點,然後再將resource的狀態置為已載入
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
//解析在configurationElement函數中處理resultMap時其extends屬性指向的父對象還沒被處理的<resultMap>節點
parsePendingResultMaps();
//解析在configurationElement函數中處理cache-ref時其指向的對象不存在的<cache>節點(如果cache-ref先於其指向的cache節點載入就會出現這種情況)
parsePendingChacheRefs();
//同上,如果cache沒載入的話處理statement時也會拋出異常
parsePendingStatements();
}

mybatis解析mapper的xml文件的過程已經很明顯了,接下來我們看看它是怎麼解析mapper的:
[java] view plain
private void configurationElement(XNode context) {
try {
//獲取mapper節點的namespace屬性
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//設置當前namespace
builderAssistant.setCurrentNamespace(namespace);
//解析mapper的<cache-ref>節點
cacheRefElement(context.evalNode("cache-ref"));
//解析mapper的<cache>節點
cacheElement(context.evalNode("cache"));
//解析mapper的<parameterMap>節點
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析mapper的<resultMap>節點
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析mapper的<sql>節點
sqlElement(context.evalNodes("/mapper/sql"));
//使用XMLStatementBuilder的對象解析mapper的<select>、<insert>、<update>、<delete>節點,
//myts會使用MappedStatement.Builder類build一個MappedStatement對象,
//所以myts中一個sql對應一個MappedStatement
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
configurationElement函數幾乎解析了mapper節點下所有子節點,至此myts解析了mapper中的所有節點,並將其加入到了Configuration對象中提供給sqlSessionFactory對象隨時使用。這里我們需要補充講一下myts是怎麼使用XMLStatementBuilder類的對象的parseStatementNode函數借用MapperBuilderAssistant類對象builderAssistant的addMappedStatement解析MappedStatement並將其關聯到Configuration類對象的:

[java] view plain
public void parseStatementNode() {
//ID屬性
String id = context.getStringAttribute("id");
//databaseId屬性
String databaseId = context.getStringAttribute("databaseId");

if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//fetchSize屬性
Integer fetchSize = context.getIntAttribute("fetchSize");
//timeout屬性
Integer timeout = context.getIntAttribute("timeout");
//parameterMap屬性
String parameterMap = context.getStringAttribute("parameterMap");
//parameterType屬性
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
//resultMap屬性
String resultMap = context.getStringAttribute("resultMap");
//resultType屬性
String resultType = context.getStringAttribute("resultType");
//lang屬性
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);

Class<?> resultTypeClass = resolveClass(resultType);
//resultSetType屬性
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//是否是<select>節點
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//flushCache屬性
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//useCache屬性
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
//resultOrdered屬性
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());

// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);

// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//resultSets屬性
String resultSets = context.getStringAttribute("resultSets");
//keyProperty屬性
String keyProperty = context.getStringAttribute("keyProperty");
//keyColumn屬性
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
//useGeneratedKeys屬性
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
由以上代碼可以看出myts使用XPath解析mapper的配置文件後將其中的resultMap、parameterMap、cache、statement等節點使用關聯的builder創建並將得到的對象關聯到configuration對象中,而這個configuration對象可以從sqlSession中獲取的,這就解釋了我們在使用sqlSession對資料庫進行操作時myts怎麼獲取到mapper並執行其中的sql語句的問題。

閱讀全文

與ybatis源碼閱讀相關的資料

熱點內容
nc編程軟體下載 瀏覽:382
如何限制手機app的使用 瀏覽:307
安卓華為手機怎麼恢復桌面圖標 瀏覽:956
我的世界電腦版伺服器地址在哪找 瀏覽:533
違抗了命令 瀏覽:256
安卓如何實現拖拽放置 瀏覽:91
凈資產收益率選股指標源碼 瀏覽:599
血壓力感測器計算公式單片機 瀏覽:466
全網介面vip影視解析源碼 瀏覽:916
如何破解伺服器遠程密碼錯誤 瀏覽:377
平安深圳app如何實名認證 瀏覽:500
linux網路監控軟體 瀏覽:889
內網伺服器如何上傳文件 瀏覽:140
程序員在你心中是什麼 瀏覽:1
蘋果手機怎麼找回app賬號 瀏覽:466
房屋中介網站源碼 瀏覽:29
命運格數演算法 瀏覽:852
ets3編程 瀏覽:730
怎麼製作音樂相冊的文件夾 瀏覽:566
寧夏重加密硅灰用法 瀏覽:231