最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中手動(dòng)開(kāi)啟數(shù)據(jù)庫(kù)事務(wù)的四種方式

 更新時(shí)間:2025年12月30日 16:43:20   作者:樂(lè)韻天城  
這篇文章給大家介紹SpringBoot中手動(dòng)開(kāi)啟數(shù)據(jù)庫(kù)事務(wù)的四種方式,每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

概要

某些情況下我們可能需要手動(dòng)開(kāi)啟事務(wù),比如由多個(gè)業(yè)務(wù)組合的功能,其中某一段業(yè)務(wù)報(bào)錯(cuò)我們需要進(jìn)行回滾操作,或者是使用數(shù)據(jù)庫(kù)事務(wù)實(shí)現(xiàn)分布式鎖。那么該如何開(kāi)啟事務(wù)呢。

開(kāi)啟事務(wù)

方式一:使用@Transactional注解,Spring會(huì)自動(dòng)幫我們管理事務(wù),包括開(kāi)啟事務(wù)、提交事務(wù)、回滾事務(wù)。
方式二:從數(shù)據(jù)源DataSource中獲取一個(gè)Connection,DataSource是自動(dòng)裝配的,SpringBoot默認(rèn)使用的是HikariDataSource。將Connection自動(dòng)提交設(shè)置為false,用此Connection執(zhí)行業(yè)務(wù)SQL,然后提交事務(wù)、回滾事務(wù)。
方式三:借助Spring中的事務(wù)管理器PlatformTransactionManager來(lái)開(kāi)啟事務(wù)。
方式四:使用TransactionTemplate(自動(dòng)注入即可),調(diào)用其execute方法來(lái)執(zhí)行業(yè)務(wù)邏輯。

PlatformTransactionManager

方式一是自動(dòng)管理事務(wù)。方式二雖然能手動(dòng)管理事務(wù),但實(shí)際操作起來(lái)不太優(yōu)雅。方式四本質(zhì)上還是方式三只不過(guò)把開(kāi)啟事務(wù)、提交事務(wù)、回滾事務(wù)做了封裝,通過(guò)lambda函數(shù)回調(diào)執(zhí)行我們的業(yè)務(wù),可以認(rèn)為還是自動(dòng)管理了事務(wù),這里重點(diǎn)介紹方式三。

先看一段代碼和運(yùn)行效果

    @Resource
    private UserMapper userMapper;
    @Resource
    private PlatformTransactionManager transactionManager;
    @GetMapping(value = "/transaction", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<String> transaction() throws InterruptedException {
        TransactionStatus transaction = null;
        try {
            //開(kāi)啟事務(wù)
            transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
            User user = userMapper.findById(1L);
            System.out.println("更新前:" + user);
            user.setAge(28);
            userMapper.updateById(user);
            //事物未提交前其他線程讀取數(shù)據(jù)
            Thread otherThread = new Thread(() -> {
                User newUser = userMapper.findById(1L);
                System.out.println("新線程獲取更新后的值:" + newUser);
            });
            otherThread.start();
            otherThread.join();
            User newUser = userMapper.findById(1L);
            System.out.println("更新后:" + newUser);
        } finally {
            if (transaction != null) {
                //提交事務(wù)
                transactionManager.commit(transaction);
                //其他線程讀取事務(wù)提交后的值
                Thread otherThread = new Thread(() -> {
                    User user = userMapper.findById(1L);
                    System.out.println("新線程獲取事務(wù)提交后的值:" + user);
                });
                otherThread.start();
                otherThread.join();
            }
        }
        return ResponseEntity.ok("transaction");
    }

這里使用mybatis來(lái)作為持久層框架,PlatformTransactionManager系統(tǒng)已經(jīng)自動(dòng)裝配,這里直接注入就可以使用。從運(yùn)行效果來(lái)看手動(dòng)開(kāi)啟的事務(wù)是生效的

上面的測(cè)試代碼是開(kāi)啟了一個(gè)新線程來(lái)觀察事務(wù)開(kāi)啟后的效果,由于是新線程必然和當(dāng)前線程是不會(huì)共享事務(wù)。但是這種寫(xiě)法需要額外的線程來(lái)操作,下面是用mybatis的SqlSessionFactory來(lái)開(kāi)啟一個(gè)新的SqlSession和當(dāng)前線程不共享事務(wù)。

    @Resource
    private UserMapper userMapper;
    @Resource
    private PlatformTransactionManager transactionManager;
    @Resource
    private SqlSessionFactory sqlSessionFactory;
    @GetMapping(value = "/transaction2", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<String> transaction2() {
        TransactionStatus transaction = null;
        DefaultSqlSession sqlSession = null;
        try {
            //開(kāi)啟事務(wù)
            transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
            User user = userMapper.findById(1L);
            System.out.println("更新前:" + user);
            user.setAge(28);
            userMapper.updateById(user);
            //重新開(kāi)啟一個(gè)連接
            Configuration configuration = sqlSessionFactory.getConfiguration();
            sqlSession = new DefaultSqlSession(
                    configuration,
                    configuration.newExecutor(
                            new JdbcTransaction(configuration.getEnvironment().getDataSource().getConnection()),
                            ExecutorType.SIMPLE),
                    true);
            User user2 = sqlSession.getMapper(UserMapper.class).findById(1L);
            System.out.println("新SqlSession獲取更新后的值:" + user2);
            User newUser = userMapper.findById(1L);
            System.out.println("更新后:" + newUser);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            if (transaction != null) {
                //提交事務(wù)
                transactionManager.commit(transaction);
                if (sqlSession != null) {
                    User newUser = sqlSession.getMapper(UserMapper.class).findById(1L);
                    System.out.println("新SqlSession獲取事務(wù)提交后的值:" + newUser);
                    sqlSession.close();
                }
            }
        }
        return ResponseEntity.ok("transaction2");
    }

從運(yùn)行結(jié)果來(lái)看,事務(wù)提交后新sqlSession獲取的age應(yīng)該為28,但仍然是18。這是因?yàn)橥粋€(gè)sqlSession執(zhí)行了相同的查詢sql語(yǔ)句時(shí),后續(xù)的查詢會(huì)從緩存中拿值,我們需要在相應(yīng)的mapper方法上加上@Options注解每次查詢前會(huì)清空緩然后走數(shù)據(jù)庫(kù)查詢。

    @Options(flushCache = Options.FlushCachePolicy.TRUE)
    @Select("select * from user where id = #{id}")
    User findById(Long id);

技術(shù)細(xì)節(jié)

PlatformTransactionManager是如何實(shí)現(xiàn)手動(dòng)管理事務(wù)的

PlatformTransactionManager的實(shí)現(xiàn)是JdbcTransactionManager,參考DataSourceTransactionManagerAutoConfiguration自動(dòng)裝配類。如果引入了其他事務(wù)框架,如spring-boot-starter-data-jpa,那么PlatformTransactionManager實(shí)現(xiàn)會(huì)是JpaTransactionManager,可以參考HibernateJpaAutoConfiguration自動(dòng)裝配類。不管是JdbcTransactionManager還是JpaTransactionManager在開(kāi)啟事務(wù)時(shí)做的相關(guān)操作都是類似的,都是從數(shù)據(jù)源中獲取到一個(gè)新的Connection后將其自動(dòng)提交設(shè)置為false。

當(dāng)我們?cè)诖a中執(zhí)行

transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());

getTransaction方法在其抽象類AbstractPlatformTransactionManager中,源碼如下

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {
		// 省略相關(guān)代碼。。。
		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
				//開(kāi)啟新的事務(wù)
				return startTransaction(def, transaction, false, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			// 省略相關(guān)代碼。。。
		}
	}
	private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
			boolean nested, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, nested, debugEnabled, suspendedResources);
		this.transactionExecutionListeners.forEach(listener -> listener.beforeBegin(status));
		try {
			//此處由實(shí)現(xiàn)類實(shí)現(xiàn)
			doBegin(transaction, definition);
		}
		catch (RuntimeException | Error ex) {
			this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, ex));
			throw ex;
		}
		prepareSynchronization(status, definition);
		this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, null));
		return status;
	}

doBegin是抽象方法,其實(shí)現(xiàn)在JdbcTransactionManager的父類DataSourceTransactionManager中實(shí)現(xiàn)。

protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;
		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				//從數(shù)據(jù)源中獲取一個(gè)新的連接
				Connection newCon = obtainDataSource().getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				if (definition.isReadOnly()) {
					checkDefaultReadOnly(newCon);
				}
				// 把新的數(shù)據(jù)庫(kù)連接綁定到ConnectionHolder中
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}
			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();
			// 省略部分代碼。。。
			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			// 正常情況下新獲取的連接都是自動(dòng)提交
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				//將數(shù)據(jù)庫(kù)連接改為手動(dòng)提交
				con.setAutoCommit(false);
			}
			// 省略部分代碼。。。
			// Bind the connection holder to the thread.
			if (txObject.isNewConnectionHolder()) {
				//給當(dāng)前線程的數(shù)據(jù)源綁定一個(gè)ConnectionHolder
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}
		catch (Throwable ex) {
			// 省略部分代碼。。。
		}
	}

源碼中可以看到新獲取的連接其自動(dòng)提交被設(shè)置為false這樣就能實(shí)現(xiàn)手動(dòng)提交事務(wù)了。且新的連接被TransactionSynchronizationManager(事務(wù)同步器)綁定到當(dāng)前線程中,事務(wù)同步器在綁定數(shù)據(jù)時(shí)是用ThreadLocal來(lái)實(shí)現(xiàn)的,方便后續(xù)線程能直接拿到綁定的數(shù)據(jù)庫(kù)連接。

當(dāng)使用mybatis的mapper接口或者sqlSession查詢以及更新數(shù)據(jù)時(shí),是如何共享事務(wù)的。

mapper接口會(huì)變成一個(gè)代理對(duì)象(是一個(gè)MapperFactoryBean屬于工廠Bean),sql的執(zhí)行是交給代理對(duì)象中封裝的sqlSession來(lái)完成操作。sqlSession在執(zhí)行sql語(yǔ)句時(shí)最終會(huì)交給Executor。

Executor中會(huì)有個(gè)事務(wù)字段transaction是一個(gè)接口。在Spring環(huán)境下它的實(shí)現(xiàn)是SpringManagedTransaction。Executor執(zhí)行sql語(yǔ)句時(shí)會(huì)從transaction中獲取一個(gè)數(shù)據(jù)連接。

  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }
  private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
    LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
        + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
  }

可以看到連接的獲取是通過(guò)工具類DataSourceUtils來(lái)操作完成的,這個(gè)是spring jdbc中所提供的工具類。

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
		}
		catch (IllegalStateException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
		}
	}
    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
        //這里能看到連接是從事務(wù)同步器中拿的
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(fetchConnection(dataSource));
            }
            return conHolder.getConnection();
        }
        // Else we either got no holder or an empty thread-bound holder here.
        // 省略相關(guān)代碼。。。
        return con;
    }

由于前面開(kāi)啟事務(wù)時(shí)已經(jīng)給當(dāng)前線程綁定了一個(gè)ConnectionHolder,這里就直接接能獲取到,這樣就實(shí)現(xiàn)了同一個(gè)線程中數(shù)據(jù)庫(kù)連接的共享。最后提交事務(wù)時(shí)是交給doCommit方法完成的。

	protected void doCommit(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		// 最開(kāi)始創(chuàng)建一個(gè)新的事務(wù)時(shí)txObject中已經(jīng)綁定了ConnectionHolder
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.commit();
		}
		catch (SQLException ex) {
			throw translateException("JDBC commit", ex);
		}
	}

事務(wù)提交之后從數(shù)據(jù)源中拿到的Connection自動(dòng)提交要恢復(fù)為true。JdbcTransactionManager的操作是在父類DataSourceTransactionManager的doCleanupAfterCompletion方法中完成的。

protected void doCleanupAfterCompletion(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		// Remove the connection holder from the thread, if exposed.
		if (txObject.isNewConnectionHolder()) {
			TransactionSynchronizationManager.unbindResource(obtainDataSource());
		}
		// Reset connection.
		Connection con = txObject.getConnectionHolder().getConnection();
		try {
			if (txObject.isMustRestoreAutoCommit()) {
			  // 恢復(fù)為自動(dòng)提交
				con.setAutoCommit(true);
			}
			DataSourceUtils.resetConnectionAfterTransaction(con,
					txObject.getPreviousIsolationLevel(),
					(txObject.isReadOnly() && !isDefaultReadOnly()));
		}
		catch (Throwable ex) {
			logger.debug("Could not reset JDBC Connection after transaction", ex);
		}
		// 省略部分代碼。。。
	}

總結(jié)

為何mybatis的sqlSession在執(zhí)行同一個(gè)查詢sql語(yǔ)句時(shí)后續(xù)會(huì)從緩存中拿值。前面說(shuō)到sql語(yǔ)句的執(zhí)行會(huì)交給Executor,其查詢方法如下。

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    // 如果設(shè)置了強(qiáng)制刷新緩存,每次執(zhí)行查詢時(shí)都會(huì)清空一遍緩存
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      // 如果從緩存中拿到了值就不從數(shù)據(jù)庫(kù)中查詢了
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

mybatis在掃描mapper接口時(shí)會(huì)默認(rèn)讓查詢語(yǔ)句的刷新緩存為都為false,這里其實(shí)就是mybatis的一級(jí)緩存,屬于會(huì)話級(jí)別。當(dāng)指定Options注解且其flushCache為true時(shí)會(huì)設(shè)置查詢語(yǔ)句要刷新緩存,如果是使用xml寫(xiě)sql語(yǔ)句,相應(yīng)的select標(biāo)簽上指定flushCache屬性為true。

到此這篇關(guān)于SpringBoot中如何手動(dòng)開(kāi)啟數(shù)據(jù)庫(kù)事務(wù)的文章就介紹到這了,更多相關(guān)SpringBoot手動(dòng)開(kāi)啟數(shù)據(jù)庫(kù)事務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java數(shù)組復(fù)制的四種方法效率對(duì)比

    java數(shù)組復(fù)制的四種方法效率對(duì)比

    這篇文章主要介紹了java數(shù)組復(fù)制的四種方法效率對(duì)比,文中有簡(jiǎn)單的代碼示例,以及效率的比較結(jié)果,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java運(yùn)用SWT插件編寫(xiě)桌面記事本應(yīng)用程序

    Java運(yùn)用SWT插件編寫(xiě)桌面記事本應(yīng)用程序

    這篇文章主要為大家介紹了一個(gè)Java項(xiàng)目實(shí)戰(zhàn),一步步教你實(shí)現(xiàn)記事本,步驟很詳細(xì),運(yùn)用SWT插件手把手編寫(xiě)記事本,感興趣的小伙伴們可以參考一下
    2016-01-01
  • mybatis-plus判斷參數(shù)是否為空并作為查詢條件方式

    mybatis-plus判斷參數(shù)是否為空并作為查詢條件方式

    這篇文章主要介紹了mybatis-plus判斷參數(shù)是否為空并作為查詢條件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2026-05-05
  • java 簡(jiǎn)單的計(jì)算器程序?qū)嵗a

    java 簡(jiǎn)單的計(jì)算器程序?qū)嵗a

    這篇文章主要介紹了java 簡(jiǎn)單的計(jì)算器程序?qū)嵗a的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Java 實(shí)現(xiàn)貪吃蛇游戲的示例

    Java 實(shí)現(xiàn)貪吃蛇游戲的示例

    這篇文章主要介紹了Java 如何實(shí)現(xiàn)貪吃蛇游戲,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-03-03
  • 一文帶你了解Java中的函數(shù)式編程

    一文帶你了解Java中的函數(shù)式編程

    函數(shù)式編程的理論基礎(chǔ)是阿隆佐·丘奇(Alonzo Church)于 1930 年代提出的 λ 演算(Lambda Calculus)。這篇文章主要為大家介紹了函數(shù)式編程的相關(guān)知識(shí),希望對(duì)大家有所幫助
    2023-04-04
  • java本機(jī)內(nèi)存分配Native?memory?allocation?mmap失敗問(wèn)題解決

    java本機(jī)內(nèi)存分配Native?memory?allocation?mmap失敗問(wèn)題解決

    這篇文章主要介紹了java本機(jī)內(nèi)存分配Native?memory?allocation?mmap失敗問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java中高效獲取IP地域信息方案全解析

    Java中高效獲取IP地域信息方案全解析

    在當(dāng)今互聯(lián)網(wǎng)應(yīng)用中,IP地域信息分析已成為許多業(yè)務(wù)場(chǎng)景的核心需求,本文將全面解析Java中獲取IP地域信息的各種方案,大家可以根據(jù)需要進(jìn)行選擇
    2025-08-08
  • JDK從8升級(jí)到21的問(wèn)題集(附案例代碼)

    JDK從8升級(jí)到21的問(wèn)題集(附案例代碼)

    JDK 8升級(jí)到JDK 21是一個(gè)重要的版本遷移,涉及語(yǔ)法、模塊化、API變更等多方面調(diào)整,這篇文章主要介紹了JDK從8升級(jí)到21問(wèn)題的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-09-09
  • Java實(shí)現(xiàn)簡(jiǎn)單登陸界面

    Java實(shí)現(xiàn)簡(jiǎn)單登陸界面

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單登陸界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評(píng)論

黄陵县| 开封县| 友谊县| 开远市| 崇仁县| 丰都县| 从江县| 象州县| 邵阳县| 西华县| 桐庐县| 莱阳市| 沙坪坝区| 喀喇沁旗| 五常市| 玛沁县| 清镇市| 东安县| 抚宁县| 拉孜县| 瑞金市| 康保县| 四川省| 汉沽区| 临桂县| 突泉县| 望城县| 沁源县| 三亚市| 油尖旺区| 翁源县| 永和县| 赤壁市| 班玛县| 南通市| 蒙自县| 新宾| 泰顺县| 宝鸡市| 吉木乃县| 修文县|