JDBC為java程序訪問各種類型的關系型數據庫提供了統一的接口,用戶不必針對不同數據庫寫出不同的代碼,但是使用JDBC必須得下載相應的驅動,比如我這里是要連接mysql,於是就到mysql官網去下載x相應驅動 https://dev.mysql.com/downloads/connector/j/
這里我下載解壓得到 mysql-connector-java-5.1.43-bin.jar
在Eclipse中新建java項目只需要Build Path --> Add External Archives把該jar包的路徑添加進來就可以使用。在Android Studio中更是只需要復制粘貼到項目目錄的app\libs下即可,IDE會自動加載。——看似如此。
本地Android Studio的配置
我的Android Studio版本是Community Edition2016.2.5,系統是win7 64位。在添加該jar文件后,build出錯。
按照提示,需要在項目最外層的build.gradle文件中的allprojects { ... }區域(具體見代碼下的圖片,之后就不附圖了)內添加下列代碼
tasks.withType(JavaCompile) { sourceCompatibility = '1.7' targetCompatibility = '1.7' }
這還沒完,此時會提示Error: Unable to find a JDK,起初我懷疑jdk沒添加進環境變量,但是cmd窗口測試無誤,而且Android Studio新建項目也能編譯運行。那么,還是jar包的問題。查了很久后找到了解決方案,看起來是java8的不兼容所致,所以需要全面改為Java7來編譯
Android Studio2.1.2 Java8環境下引用Java Library編譯出錯
1、在所有module的build.gradle文件中的android { ... }區域中添加
compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 }
就我這個新建的項目,沒有添加module,所以需要修改的文件就只有app\build.gradle
2、在主module的build.gradle文件(也就是app\build.gradle)的android { defaultConfig { ... } }區域中添加
jackOptions { enabled true }
此時編譯成功了,但是注意,這才是第一步,僅僅只是成功引用了java庫。Android Studio默認是不允許訪問Internet的。
因此需要在app/src/main/AndroidManifest.xml中的<manifest>...</manifest>區域內添加一行
<uses-permission android:name="android.permission.INTERNET"/>
遠程mysql的配置
然后我的mysql是安裝在Linux虛擬機(Ubuntu 16.04)上的,默認mysql是禁止遠程連接的,所以在Linux上也要進行配置
首先是要讓mysql支持中文字符,登陸mysql用戶后,輸入命令STATUS;可以看到幾個字符集是這樣的
Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8
因此需要用超級用戶權限修改/etc/mysql/my.cnf,添加下列代碼(這里順便綁定了端口為3306,雖然一般默認端口也是3306)
[client] default-character-set=utf8 [mysqld] default-storage-engine=INNODB character-set-server=utf8 collation-server=utf8_general_ci
port=3306
然后重啟mysql,命令如下
$ /etc/init.d/mysql stop
$ /etc/init.d/mysql start
之后再登陸mysql后使用STATUS命令就可以看到字符集全部變成了utf8。
然后mysql默認綁定的IP是127.0.0.1,端口3306,在shell下可以通過shell命令netstat -apn | grep 3306查看端口占用情況,若端口3306對應的第三項(Local Address)是127.0.0.1:3306,那么mysql綁定的就是本地地址,不能遠程連接。
還是用超級用戶權限修改/etc/mysql/my.cnf,添加下列代碼並重啟mysql
bind-address=0.0.0.0
重啟后再用netstat -apn | grep 3306查看第三項就會變成0.0.0.0:3306,那么mysql就支持了遠程連接。
還沒完,雖然mysql賦予了遠程連接的權限,但是mysql用戶並沒有。我這里的mysql用戶名是team,密碼是java123,用root登陸mysql后輸入下列命令
mysql> grant all on *.* to team@'%' identified by 'java123' with grant option; mysql> flush privileges;
team@'%'代表用戶team支持遠程連接,team@'localhost'則代表用戶team支持本地連接
代碼實現
好了,終於可以寫代碼了,於是迫不可待地在MainAcitivity.java的onCreate()方法內部添加下列代碼(先嘗試連接)
// 1.加載JDBC驅動 try { Class.forName("com.mysql.jdbc.Driver"); Log.v(TAG, "加載JDBC驅動成功"); } catch (ClassNotFoundException e) { Log.e(TAG, "加載JDBC驅動失敗"); return; } // 2.設置好IP/端口/數據庫名/用戶名/密碼等必要的連接信息 String ip = "192.168.183.134"; int port = 3306; String dbName = "XYZ"; String url = "jdbc:mysql://" + ip + ":" + port + "/" + dbName; // 構建連接mysql的字符串 String user = "team"; String password = "java123"; // 3.連接JDBC try { Connection conn = DriverManager.getConnection(url, user, password); conn.close(); } catch (SQLException e) { Log.e(TAG, "遠程連接失敗!"); return; }
這里使用了andorid.util.Log而不是System.out.println來打印消息,因為可以設置過濾符,通過TAG、日志級別等信息來篩選出特定的日志
這里我就是通過TAG來篩選,也可以通過Message和Package來篩選,並且支持正則表達式,程序運行時會有大量日志混雜在一起,所以使用Log而不是System.out來打印消息更好。
於是,通過日志過濾符篩選,可以發現連接失敗了。
這個問題折磨了我一天多,參考了很多網上的代碼,和我的基本無異,而且也copy過來測試過都不行,最后在stackoverflow上找到了解答
https://stackoverflow.com/questions/12233145/connecting-to-mysql-from-android-with-jdbchttps://stackoverflow.com/questions/12233145/connecting-to-mysql-from-android-with-jdbc
mysql的連接必須在異步任務類中,也就是說必須新建線程來連接mysql,而不能在主線程中執行代碼。
final Thread thread = new Thread(new Runnable() { @Override public void run() { // 反復嘗試連接,直到連接成功后退出循環 while (!Thread.interrupted()) { try { Thread.sleep(100); // 每隔0.1秒嘗試連接 } catch (InterruptedException e) { Log.e(TAG, e.toString()); } // 2.設置好IP/端口/數據庫名/用戶名/密碼等必要的連接信息 String ip = "192.168.183.134"; int port = 3306; String dbName = "XYZ"; String url = "jdbc:mysql://" + ip + ":" + port + "/" + dbName; // 構建連接mysql的字符串 String user = "team"; String password = "java123"; // 3.連接JDBC try { Connection conn = DriverManager.getConnection(url, user, password); Log.i(TAG, "遠程連接成功!"); conn.close(); return; } catch (SQLException e) { Log.e(TAG, "遠程連接失敗!"); } } } }); thread.start();
成功了,總算可以繼續下一步,向遠程連接的mysql發送命令了,將上述代碼稍作修改
// 3.連接JDBC Connection conn = null; try { conn = DriverManager.getConnection(url, user, password); Log.i(TAG, "遠程連接成功!"); } catch (SQLException e) { Log.e(TAG, "遠程連接失敗!"); } if (conn != null) { String sql = "SELECT * FROM pokemon"; try { // 創建用來執行sql語句的對象 java.sql.Statement statement = conn.createStatement(); // 執行sql查詢語句並獲取查詢信息 ResultSet rSet = statement.executeQuery(sql); // 迭代打印出查詢信息 Log.i(TAG, "寶可夢列表"); Log.i(TAG, "ID\tName\tAttr1\tAttr2"); while (rSet.next()) { Log.i(TAG, rSet.getString("id") + "\t" + rSet.getString("name") + "\t" + rSet.getString("attr1") + "\t" + rSet.getString("attr2")); } } catch (SQLException e) { Log.e(TAG, "createStatement error"); } try { conn.close(); } catch (SQLException e) { Log.e(TAG, "關閉連接失敗"); }
懶得仔細研究java格式化字符串,直接把結果打印出來了,這里只用到了執行查詢語句的executeQuery,返回結果到一組迭代對象。
而execute則可以執行任何sql語句,更多API可以參考JDBC官方教程 http://docs.oracle.com/javase/tutorial/jdbc/basics/index.html