posts - 78, comments - 34, trackbacks - 0, articles - 1
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          Today is JDBC高級(jí)部分,課程的主要內(nèi)容有連接池與事務(wù)。這都是在應(yīng)用開發(fā)中比較常用,比較重要的。

          一、使用配置文件優(yōu)化JDBCUtil,使用工廠模式隔離DAO層:

          昨天有說過將對(duì)數(shù)據(jù)庫(kù)獲取連接和釋放的操作單封裝到一個(gè)類中,如:

          import java.sql.*;
          import cn.itcast.cc.exception.JDBCUtilException;
          
          /**
           * JDBC工具類
           * 
           * @author Administrator
           * 
           */
          public class JDBCUtil {
          	// 連接數(shù)據(jù)庫(kù)時(shí)所需要的參數(shù)
          	private static String url = "jdbc:mysql://localhost:3306/jdbc";
          	private static String username = "root";
          	private static String password = "root";
          	// 類被加載時(shí)就加載驅(qū)動(dòng)
          	static {
          		try {
          			Class.forName("com.mysql.jdbc.Driver");
          		} catch (ClassNotFoundException e) {
          			e.printStackTrace();
          		}
          	}
          
          	// 獲取連接
          	public static Connection getConnection() throws JDBCUtilException {
          		try {
          			return DriverManager.getConnection(url, username, password);
          		} catch (SQLException e) {
          			throw new JDBCUtilException(e);
          		}
          	}
          
          	// 釋放連接等資源,下面是一種健康的釋放方式
          	public static void release(ResultSet rs, Statement sta, Connection conn)
          			throws JDBCUtilException {
          		if (rs != null) {
          			try {
          				rs.close();
          			} catch (SQLException e) {
          				throw new JDBCUtilException(e);
          			}
          			rs = null;
          		}
          
          		if (sta != null) {
          			try {
          				sta.close();
          			} catch (SQLException e) {
          				throw new JDBCUtilException(e);
          			}
          			sta = null;
          		}
          
          		if (conn != null) {
          			try {
          				conn.close();
          			} catch (SQLException e) {
          				throw new JDBCUtilException(e);
          			}
          			conn = null;
          		}
          	}
          }

          從上面我們看到其中裝載數(shù)據(jù)庫(kù)驅(qū)動(dòng)和獲取數(shù)據(jù)庫(kù)連接的代碼:

          Class.forName("com.mysql.jdbc.Driver");

          DriverManager.getConnection(url, username, password);

          我們使用了固定的url,它是一個(gè)成員。在以后的開發(fā)中,如果要使用其他的數(shù)據(jù)庫(kù)或者數(shù)據(jù)庫(kù)的用戶名密碼被改變了,我們還需要手動(dòng)的修改上邊的代碼,重新編譯。這是一種不良的設(shè)計(jì)方法,為了使我們的代碼與數(shù)據(jù)庫(kù)分離,我們可以將這些參數(shù)配置到配置文件中。這樣在需要使用時(shí)去讀取配置文件中的對(duì)應(yīng)值即可。以后換數(shù)據(jù)庫(kù)或更改了用戶名,直接修改一下配置文件即可。

          昨天有提到過DAO層,DAO層專門用于處理對(duì)數(shù)據(jù)庫(kù)的CURD操作等,比如,添加用戶、查找用戶、修改用戶、刪除用戶,我們可以把這些操作封裝到一個(gè)類中(UserDao.java),它所處位置就是DAO層。比如,用于處理用戶注冊(cè)的Servlet,在這個(gè)Servlet中直接實(shí)例化一個(gè)UserDao對(duì)象。然后調(diào)用userDaoObj.add(userbean);方法即可實(shí)現(xiàn)用戶的注冊(cè)。

          如果我想換一種數(shù)據(jù)存儲(chǔ)方式,比如配置文件,我只將用戶信息保存在配置文件中。這樣我就需要修改UserDao.java,但有時(shí)又需要UserDao.java這個(gè)功能類怎么辦?我只能重新創(chuàng)建一個(gè)專門處理配置文件數(shù)據(jù)的類(UserDaoPro.java)。這樣我又需要修改Servlet中的代碼,將實(shí)例化UserDao的代碼修改為實(shí)例化UserDaoPro對(duì)象。那UserDaoPro與UserDao的接口是不是相同的呢?如果不相同麻煩就大了,JAVA是提倡使用接口編程的,就是為了實(shí)現(xiàn)接口的統(tǒng)一化。

          可見,上面的這種實(shí)現(xiàn)方式是存在問題的。即使接口統(tǒng)一,我們還需要修改Servlet中的代碼。此時(shí)工廠模式派上了用場(chǎng),還記得工廠模式吧!是這樣的:

          1.定義一個(gè)UserDao接口,統(tǒng)一對(duì)用戶操作的接口。

          2.定義一個(gè)UserDaoFactory的工廠類,專門生產(chǎn)UserDao對(duì)象。

          3. UserDaoFactory工廠從配置文件中讀取實(shí)現(xiàn)了UserDao接口的類名稱。使用此類生產(chǎn)產(chǎn)品——UserDao對(duì)象。

          4.在Servlet中使用UserDaoFactory工廠來創(chuàng)建需要的UserDao對(duì)象。

          這樣就實(shí)現(xiàn)了DAO層與Servlet的完全分離!完美!

          二、數(shù)據(jù)庫(kù)連接池:

          每當(dāng)有一個(gè)新的連接發(fā)生時(shí),數(shù)據(jù)庫(kù)就需要?jiǎng)?chuàng)建一個(gè)Connection對(duì)象來處理連接。訪問結(jié)束后,Connection對(duì)象被釋放。數(shù)據(jù)庫(kù)創(chuàng)建一個(gè)Connection對(duì)象,是十分耗時(shí)且消耗較大的服務(wù)器資源。試想,如果有500個(gè)用戶同時(shí)訪問數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)同時(shí)創(chuàng)建500個(gè)Connection將會(huì)是什么樣子!因此,連接池這一技術(shù)誕生了。

          連接池,在服務(wù)器加載WEB應(yīng)用后。WEB應(yīng)用會(huì)自動(dòng)創(chuàng)建一個(gè)連接池,池中包含多個(gè)Connection對(duì)象。每當(dāng)有新的連接請(qǐng)求時(shí),便從這個(gè)池中拿出一個(gè)Connection對(duì)象用于處理連接,使用完成后,便還回給這個(gè)池子。下面代碼為連接池的實(shí)現(xiàn)原理:

          import java.io.*;
          import java.lang.reflect.*;
          import java.sql.*;
          import java.util.*;
          import javax.sql.DataSource;
          
          /**
           * 自己編寫的簡(jiǎn)單連接池類,單例模式實(shí)現(xiàn)。
           * 
           * @author Administrator
           * 
           */
          public class JDBCUtil implements DataSource {
          	private static LinkedList<Connection> conns = new LinkedList<Connection>();
          	private static JDBCUtil myjdbcutil = new JDBCUtil();
          
          	private JDBCUtil() {
          		// 取配置文件
          		InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream(
          				"cn/itcast/cc/db/myjdbc.properties");
          		// 裝載配置文件
          		Properties pro = new Properties();
          		try {
          			pro.load(in);
          		} catch (IOException e) {
          			e.printStackTrace();
          		}
          		// 取出配置項(xiàng)
          		String driver = pro.getProperty("driverClassName");
          		String url = pro.getProperty("url");
          		String username = pro.getProperty("username");
          		String password = pro.getProperty("password");
          		int initialSize = Integer.parseInt(pro.getProperty("initialSize"));
          		// 加載驅(qū)動(dòng),填充連接池
          		try {
          			// 常用的數(shù)據(jù)庫(kù),驅(qū)動(dòng)類被加載時(shí),都會(huì)自已加載驅(qū)動(dòng)。
          			Class.forName(driver);
          			for (int i = 0; i < initialSize; i++) {
          				conns.add(DriverManager.getConnection(url, username, password));
          			}
          		} catch (Exception e) {
          			e.printStackTrace();
          		}
          	}
          
          	// 返回單例的實(shí)例
          	public static JDBCUtil getInstance() {
          		return myjdbcutil;
          	}
          
          	public Connection getConnection() throws SQLException {
          		if (conns.size() > 0) {
          			final Connection conn = conns.pop();
          			// 此處需要使用動(dòng)態(tài)代理技術(shù),因?yàn)榉祷氐腃onnection對(duì)象在關(guān)閉時(shí)需要被收回到LinkedList中。
          			// 動(dòng)態(tài)代理,增強(qiáng)Connection.colse()方法,將它回收到LinkedList中。但不銷毀!
          			return (Connection) Proxy.newProxyInstance(JDBCUtil.class
          					.getClassLoader(), conn.getClass().getInterfaces(),
          					new InvocationHandler() {
          
          						public Object invoke(Object proxy, Method method,
          								Object[] args) throws Throwable {
          							// 如果是close方法,則將conn回收到List中。
          							if (method.getName().equals("close")) {
          								conns.push(conn);
          								return null;
          							}
          							return method.invoke(conn, args);
          						}
          					});
          		}
          		throw new RuntimeException("服務(wù)器忙,請(qǐng)稍后連接!");
          	}
          }

          有很多服務(wù)器為用戶提供了DataSource(數(shù)據(jù)源)的實(shí)現(xiàn)。DataSource中包含了連接池的實(shí)現(xiàn)。也有一些第三方組織單獨(dú)提供了連接池的實(shí)現(xiàn):DBCP、C3P0。所以,在以后的開發(fā)中,我們可以直接使用這些資源。

          著名的DBCP比較常用,使用DBCP必須引入Commons-dbcp.jar和Commons-pool.jar。Tomcat中的連接池也是使用DBCP實(shí)現(xiàn)的。DBCP的使用非常簡(jiǎn)單:

          import java.io.InputStream;
          import java.sql.*;
          import java.util.Properties;
          import javax.sql.DataSource;
          import org.apache.commons.dbcp.BasicDataSourceFactory;
          
          public class DBCPUtil {
          	private static DataSource ds = null;
          	static {
          		try {
          			// 取配置文件
          			InputStream in = JDBCUtil.class.getClassLoader()
          					.getResourceAsStream(
          							"cn/itcast/cc/db/dbcpconfig.properties");
          			// 裝載配置文件
          			Properties pro = new Properties();
          			pro.load(in);
          			// 創(chuàng)建數(shù)據(jù)源
          			ds = BasicDataSourceFactory.createDataSource(pro);
          		} catch (Exception e) {
          			e.printStackTrace();
          		}
          	}
          
          	// 獲取連接
          	public static Connection getConnection() throws SQLException {
          		return ds.getConnection();
          	}
          }

           

          其中的配置文件“dbcpconfig.properties”:

          #連接設(shè)置

          #驅(qū)動(dòng)器

          driverClassName=com.mysql.jdbc.Driver

          #數(shù)據(jù)庫(kù)連接URL

          url=jdbc:mysql://localhost:3306/jdbc

          #數(shù)據(jù)庫(kù)用戶名

          username=root

          #數(shù)據(jù)庫(kù)密碼

          password=root

          #初始連接池?cái)?shù)量

          initialSize=10

          #最大連接數(shù)量

          maxActive=50

          #最大空閑連接

          maxIdle=20

          #最小空閑連接

          minIdle=5

          #超時(shí)等待時(shí)間以毫秒為單位 6000毫秒/1000等于60秒

          maxWait=60000

          #JDBC驅(qū)動(dòng)建立連接時(shí)附帶的連接屬性屬性的格式必須為這樣:[屬性名=property;]

          #注意:"user" 與 "password" 兩個(gè)屬性會(huì)被明確地傳遞,因此這里不需要包含他們。

          connectionProperties=useUnicode=true;characterEncoding=UTF8

          #指定由連接池所創(chuàng)建的連接的自動(dòng)提交(auto-commit)狀態(tài)。

          defaultAutoCommit=true

          #driver default 指定由連接池所創(chuàng)建的連接的只讀(read-only)狀態(tài)。

          #如果沒有設(shè)置該值,則“setReadOnly”方法將不被調(diào)用。(某些驅(qū)動(dòng)并不支持只讀模式,如:Informix)

          defaultReadOnly=

          #driver default 指定由連接池所創(chuàng)建的連接的事務(wù)級(jí)別(TransactionIsolation)。

          #可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE

          defaultTransactionIsolation=READ_UNCOMMITTED

          上面是直接使用Apache提供的DBCP實(shí)現(xiàn)的連接池,接下來看一下使用JNDI技術(shù)在Tomcat中配置連接池,在Tomcat服務(wù)的conf\context.xml文件中添加:

          <Context>

          <Resource name="jdbc/datasource" auth="Container"

          type="javax.sql.DataSource" username="root" password="root"

          driverClassName="com.mysql.jdbc.Driver"

          url="jdbc:mysql://localhost:3306/jdbc"

          maxActive="8" maxIdle="4"/>

          </Context>

          Name: 對(duì)象在JNDI容器中的名稱。

          Auth:所使用的容器。

          Type:對(duì)象類型。

          Username:數(shù)據(jù)庫(kù)用戶名。

          Password:數(shù)據(jù)庫(kù)密碼。

          driverClassName:數(shù)據(jù)庫(kù)驅(qū)動(dòng)類。

          url:連接數(shù)據(jù)庫(kù)的URL。

          maxActive:最大連接數(shù)。

          maxIdle:最大空閑連接數(shù)。

          注意其中的數(shù)據(jù)庫(kù)驅(qū)動(dòng),對(duì)應(yīng)的jar包必須放置到Tomcat目錄下的lib目錄中。這樣服務(wù)器才能找得到。

          在WEB應(yīng)用中獲取數(shù)據(jù)庫(kù)連接的代碼:

          // 初始化JNDI環(huán)境

          Context initCtx = new InitialContext();

          // 獲取JNDI容器

          Context envCtx = (Context) initCtx.lookup("java:comp/env");

          // 獲取數(shù)據(jù)源

          DataSource dataSource = (DataSource)envCtx.lookup("jdbc/datasource");

          // 獲取數(shù)據(jù)庫(kù)連接

          dataSource.getConnection();

          JNDI是一個(gè)對(duì)象容器,服務(wù)器根據(jù)配置文件提供的信息。創(chuàng)建這些對(duì)象,并按照指定的名稱保存在容器中。WEB應(yīng)用,無論在何處都可以根據(jù)名稱獲取在JNDI容器中的對(duì)象。

          三、數(shù)據(jù)庫(kù)事務(wù)管理:

          什么是事務(wù)?老方有這樣的面試題。事務(wù)指邏輯上的一組操作,這組操作要不全部生效,要不全部失效。在控制臺(tái)中操作事務(wù)的命令:

          start transaction 開啟事務(wù)

          Rollback 回滾事務(wù)

          Commit 提交事務(wù)

          set transction isolation level 設(shè)置事務(wù)隔離級(jí)別

          select @@tx_isolation 查詢當(dāng)前事務(wù)隔離級(jí)別

          事務(wù)的特性(ACID),這在面試中是比較常見的。老方一再?gòu)?qiáng)調(diào),一定要記下來!OK,讓我們看一看。

          事務(wù)的原子性(Automicity),它更像是事務(wù)的定義。就是邏輯上的一組操作,不可分割,要么全部生效,要么全部失效。

          事務(wù)的一致性(Consistency),老方的PPT有中說:事務(wù)必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變換到另外一個(gè)一致性狀態(tài)。比如,銀行轉(zhuǎn)賬,A-B。A賬戶中減少100元,B帳戶中增加一百元。這看起來比較好理解,但讓我有點(diǎn)疑問的是,如果從數(shù)據(jù)庫(kù)中刪除一個(gè)用戶呢?難道不用事務(wù)處理嗎?所以我對(duì)這個(gè)一致性的理解除了老方說的,還有只要事務(wù)處理符合正確的邏輯,就是一致性。

          隔離性(Isolation),如果多個(gè)用戶同時(shí)訪問數(shù)據(jù)庫(kù),同時(shí)訪問一張表。這樣會(huì)造成一些訪問錯(cuò)誤,所以有了隔離性這一定義。將多個(gè)并發(fā)事務(wù)之間要相互隔離。

          持久性(Durability),事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是永久性的,無論其他操作或數(shù)據(jù)庫(kù)故障不會(huì)對(duì)它有任何影響。

          在JDBC中使用事務(wù)的語(yǔ)句:

          // 開啟事務(wù)

          conn.setAutoCommit(false);

          // 回滾事務(wù)

          conn.rollback();

          // 提交事務(wù)

          conn.commit();

          // ...

          // 設(shè)置保存點(diǎn)

          sp = conn.setSavepoint();

          // 回滾到指定保存點(diǎn)

          conn.rollback(sp);

          方老師使用了一個(gè)經(jīng)典的案例,來講解事務(wù)處理——銀行轉(zhuǎn)賬系統(tǒng)!

          例,用戶A轉(zhuǎn)賬10000元給用戶B。應(yīng)該是A賬戶中減少10000元,B賬戶中增加10000元。那么在A提交了10000元后,數(shù)據(jù)庫(kù)發(fā)生問題(比如斷電),那B帳戶增加10000元的語(yǔ)句還沒有執(zhí)行。使用事務(wù)可以很好解決這一問題:

          1. 開啟事務(wù)

          2. A帳戶減少10000元

          3. 中間發(fā)生錯(cuò)誤,回滾事務(wù)

          4. B帳戶增加10000元

          5. 提交事務(wù)

          OK,在上同這一過程中,如果沒有執(zhí)行到第5步。數(shù)據(jù)庫(kù)的內(nèi)容就不會(huì)發(fā)生改變。在此有必要解釋一下回滾事務(wù),回滾事務(wù)在提交事務(wù)后執(zhí)行沒有什么效果,回滾事務(wù)在提交事務(wù)之前執(zhí)行,會(huì)擦除在開啟事務(wù)之后執(zhí)行的SQL語(yǔ)句。

          上面是針對(duì)單一事務(wù)進(jìn)行的處理,在并發(fā)事務(wù)中會(huì)遇到一些問題。設(shè)置事務(wù)的隔離級(jí)別可以很好的解決這一問題。

          首先我們來看一下并發(fā)事務(wù)容易產(chǎn)生的問題:

          臟讀:

          例,用戶A賬戶減少10000元,用戶B賬戶增加了10000元,但A還沒有提交事務(wù)。此時(shí),B去查詢它的賬戶發(fā)現(xiàn)A已經(jīng)給自己轉(zhuǎn)賬了10000元。然后,A回滾了事務(wù)。B被A給騙了,這就是臟讀,B讀到的是未提交的數(shù)據(jù)。我雖對(duì)數(shù)據(jù)庫(kù)事務(wù)的實(shí)現(xiàn)原理不了解(事務(wù)是通過日志處理的),我打個(gè)比方就更好理解這一原因了。數(shù)據(jù)庫(kù)對(duì)所有用戶維護(hù)了一個(gè)虛表,在開啟事務(wù)中,提交事務(wù)前對(duì)數(shù)據(jù)的修改,都是修改的虛表,用戶查詢的也是虛表的內(nèi)容,當(dāng)執(zhí)行了提交事務(wù)操作后,將虛表中被修改的內(nèi)容保存到數(shù)據(jù)庫(kù)。實(shí)際技術(shù)細(xì)節(jié)就不深入了!

          不可重復(fù)讀:

          例,用戶A原賬戶內(nèi)容為100萬(wàn)元,此時(shí)銀行將數(shù)據(jù)顯示到電腦屏幕和保存到文件中。銀行工作人員在電腦屏幕上看到的是100萬(wàn)元,但此時(shí),用戶A向賬戶里又存了100萬(wàn)元。結(jié)果保存到文件的是200萬(wàn)元。到是100萬(wàn)元還是200萬(wàn)元?工作人員有些迷惑。與臟讀相反的是,臟讀是讀取了前一事務(wù)未提交的數(shù)據(jù),不可重復(fù)讀讀取的是前一事務(wù)提交了的事務(wù)。注意,不可重復(fù)讀針對(duì)的是某一記錄!

          虛讀:

          例,銀行要統(tǒng)計(jì)A的報(bào)表。A的賬戶中有200萬(wàn)元,屏幕上報(bào)表顯示的總額是200萬(wàn)元,此時(shí),用戶A向賬戶中又存入了100萬(wàn)元。報(bào)表打印出來的總額就是300萬(wàn)元。工作人員一對(duì)比,到底哪個(gè)是正確的?注意,虛讀針對(duì)的是整個(gè)表。

          不可重復(fù)讀和虛讀,有點(diǎn)難理解。這看起來不是什么問題,如果發(fā)生這一問題,工作人員就刷新一下表唄!如果工作人員處理的是1萬(wàn)個(gè)用戶的報(bào)表,同時(shí)不幸運(yùn)的是這1萬(wàn)個(gè)用戶都發(fā)生了虛讀的問題。那工作人員能受了嗎?你可能還會(huì)說那就以打印的報(bào)表為準(zhǔn)唄!問題是,可能屏幕上的那個(gè)才是正確的,這樣就要重新打印報(bào)表。所以為了使問題得以簡(jiǎn)單的解決,事務(wù)的隔離性發(fā)揮了它的作用!

          處理上邊的三種并發(fā)事務(wù)產(chǎn)生的問題,數(shù)據(jù)庫(kù)定義了四種隔離級(jí)別:

          Serializable:可避免臟讀、不可重復(fù)讀、虛讀情況的發(fā)生。

          Repeatable read:可避免臟讀、不可重復(fù)讀情況的發(fā)生。

          Read committed:可避免臟讀情況發(fā)生。

          Read uncommitted:最低級(jí)別,以上情況均無法保證。

          Serializable,最高安全級(jí)別。設(shè)置了此級(jí)別,當(dāng)一個(gè)事務(wù)訪問了某一表時(shí),此表就會(huì)被封死。其他事務(wù)對(duì)此表的訪問就會(huì)被掛起,直到上一事務(wù)提交后才執(zhí)行一個(gè)事務(wù)。OK,這看起來十分容易理解。

          Repeatable read,讓我再小深入一下,當(dāng)每個(gè)事務(wù)線程訪問同一表時(shí),數(shù)據(jù)庫(kù)針對(duì)每個(gè)線程維護(hù)了一張?zhí)摂M表。此時(shí)各線程看到的都是原始的數(shù)據(jù)內(nèi)容,對(duì)表數(shù)據(jù)的修改相互不發(fā)生影響,即使事務(wù)被提交了。數(shù)據(jù)庫(kù)可能為每個(gè)線程維護(hù)了一張?zhí)摂M表嗎?當(dāng)然不可以,我想這只是為了便于理解。技術(shù)細(xì)節(jié)不深入研究!

          Read committed,數(shù)據(jù)庫(kù)對(duì)每個(gè)用戶的查詢顯示的都是原始內(nèi)容(真實(shí)內(nèi)容)。如果某些用戶對(duì)此表的事務(wù)沒有提交就不會(huì)影響原始內(nèi)容。所以其他用戶查看到的都是原始內(nèi)容或提交了的數(shù)據(jù)內(nèi)容。

          Read uncommitted,這個(gè)就不多說了。

          今天的內(nèi)容也就事務(wù)的隔離性有些難度吧!一般也不會(huì)去用那最高安全級(jí)別,這一級(jí)別在銀行中比較常用。

          正如老方所說,是不是感覺越學(xué)越簡(jiǎn)單了!確實(shí)如此,我想大家應(yīng)該也對(duì)此有感覺。不知不覺明天又休息了!

          加油!


          評(píng)論

          # re: 2009-12-02傳智播客 數(shù)據(jù)庫(kù)&mdash;&mdash;JDBC開發(fā) 連接池與事務(wù)  回復(fù)  更多評(píng)論   

          2010-06-14 09:25 by peixin.xu
          只能說你很強(qiáng)大

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 保靖县| 横峰县| 台中市| 乡宁县| 精河县| 都匀市| 海兴县| 兰溪市| 剑河县| 博客| 行唐县| 修水县| 深圳市| 武冈市| 富民县| 习水县| 道孚县| 永川市| 克什克腾旗| 横峰县| 鄄城县| 衡南县| 肇东市| 沧源| 通城县| 陈巴尔虎旗| 资溪县| 绍兴县| 会泽县| 金门县| 曲靖市| 华蓥市| 阜新市| 湖南省| 大化| 河曲县| 天长市| 长沙县| 九龙坡区| 双柏县| 延川县|