本文将介绍开发Android程序,连接SQL Server,通过第三方包jtds的方式。
如果你有同样的需求,请跟着做一遍,博主将以最详细的方式,进行介绍。
首先说明,Java、Android连接SQL Server并不是常见的方式,原因是SQL Server的微软的产品,没有打算让Java、Android直接连接,所以能连上的,都是在Java、Android使用第三方的包,
目前总共有2个方法能连,(1)本文将介绍jtds,这是名副其实的直连,Android必须使用1.2.7版本,高版本连不了,Java则可以使用1.3.1
(2)通过ASP.NET的web service连接,这个并非直连,只是通过网页读取,Java使用第三方包axis,Android使用KSOAP。(以后的文章会介绍)
博主由于工作的原因,两种方法都试过了,因此分享到这里。
下面正式开始说明:
首先,先说明一下普通的操作步骤:
一、打开AndroidManifest.xml的网络操作权限
这是博主在之前的文章强调过的,这个必须先打开,以免以后遗忘。
android:versionCode="1"
android:versionName="1.0" >
android:targetSdkVersion="21" />
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:label="@string/app_name" >
二、导入jtds包
1、在项目路径下建立libs文件夹,把jtds-1.2.7.jar放到libs目录中
2、右键点击项目名=>Build Path=>Configure Build Path=>上面打开Libraries=>Add External JARs
=>选中jtds-1.2.7.jar=>上面打开Order and Export=>勾选jtds-1.2.7.jar=>apply
完成
三、简单界面布局
1、res=>values=>strings.xml
界面中的Button,显示文本为Insert
2、res=>layout=>activity_main.xml
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.test.jtdstest.MainActivity" >
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
加入Button控件
3、主程序代码(增Insert、删Delete、改Update)
package com.test.jtdstest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
// 按钮控件
private Button btnInsert;
// jtds驱动路径
private String drive = "net.sourceforge.jtds.jdbc.Driver";
// SQL连接字符串,格式是 jdbc:jtds:sqlserver://服务器IP:端口号/数据库名称
// 端口号默认为1433,如果不是,可以打开SQL Server配置管理器设定,
// 如果你的SQL Server不是默认实例,需添加连接字符串为
// jdbc:jtds:sqlserver://服务器IP:端口号/数据库名称;instance=实例名,不过博主没有试验过,你可以百度一下。
private String connStr = "jdbc:jtds:sqlserver://10.76.25.1:1433/CommonDB";
// 用户名和密码,则是对应的数据库的帐号,博主使用sa进行说明,如果你用的不是sa,记得在数据库表中打开你的帐号的权限。
private String uid = "sa";
private String pwd = "123";
// 连接对象,相当于C#中的SqlConnection
private Connection con = null;
// 执行对象,相当于C#中的SqlCommand
private PreparedStatement pstm = null;
// handler处理对象,用于在跨线程时,在线程间的响应,用于控制主线程的控件(不能跨线程控制控件)
private Handler handler = new Handler();
// 执行语句
private String sql = "insert into [table1]([id],[name]) values ('01','aaa')";
// 执行结果,受影响行数
private int resultCount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到btnInsert按钮
btnInsert = (Button) findViewById(R.id.btnInsert);
// 设定btnInsert的click操作的监听事件,btnInsert被点击时,触发clickEvent()方法
btnInsert.setOnClickListener(clickEvent());
}
// clickEvent()方法
public OnClickListener clickEvent() {
return new OnClickListener() {
// 方法体
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
if (view == btnInsert) {
// 必须开启新的线程执行
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 线程在运行后,执行Insert()方法,返回受影响行数,赋值给resultCount
resultCount = Insert();
// 使用handler,使主线程响应并执行runShowResult方法
handler.post(runShowResult);
}
});
// 线程运行
thread.start();
}
}
};
}
// 操作数据库的方法
public int Insert() {
int count = 0;
try {
// 加载驱动
Class.forName(drive);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
// 创建连接对象,加入连接字符串、用户名、密码
con = DriverManager.getConnection(connStr, uid, pwd);
// 创建执行对象,并加入执行语句
pstm = con.prepareStatement(sql);
// 执行SQL语句,并返回受影响行数
count = pstm.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
count = -1;
} finally {
try {
// 关闭连接
pstm.close();
con.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
return count;
}
// 主线程响应方法,用于显示提示气泡
public Runnable runShowResult = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
String tips = "受影响行数为:" + resultCount;
// 弹出气泡
Toast.makeText(getApplicationContext(), tips, Toast.LENGTH_SHORT).show();
}
};
// 暂不需要理会
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// 暂不需要理会
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
说明:
1、加载驱动时,如果报错,则说明jtds包导入失败,字符串没有错误的情况下,一定能加载成功;
2、如果使用的数据库实例,并不是默认实例,需在连接字符串加入;instance=实例名
3、必须使用跨线程进行网络操作,因为Android4.0后,就禁止在主线程进行网络操作,因为如果操作失败,将导致整个程序崩溃。
4、得到的执行结果,可用公共变量接收,如上面代码的resultCount,在副线程赋值给resultCount后,可直接通过Handler对象,调用Runnable方法,操作主线程的控件,上面代码把结果显示在Toast中。
常见的运行失败现象:
1、Thread跳出,程序中止,说明在处理控件与线程时出现异常
2、EACCES permission,就是AndroidManifest.xml没有插入打开网络权限的语句
如果运行错误,而找不到错误发生在哪里,可以在代码中各步加入System.out.println("标记信息或Exception信息"),在eclipse中打开LogCat监视窗体,观察运行的情况。
完成,执行看看吧。
SqlHelper.java:
package MyJtds;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
public class SqlHelper {
private String drive = "net.sourceforge.jtds.jdbc.Driver";
private String connStr;
private String server;
private String dbName;
private String userName;
private String userPwd;
private Connection con;
private PreparedStatement pstm;
public SqlHelper(String server, String dbName, String userName, String userPwd) {
this.server = server;
this.dbName = dbName;
this.connStr = "jdbc:jtds:sqlserver://" + this.server + ":1433/" + this.dbName;
this.userName = userName;
this.userPwd = userPwd;
try {
Class.forName(drive);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int ExecuteNonQuery(String sql, List
public String ExecuteQuery(String sql, List
activity 界面代码
package com.test.androidsqltest;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import Models.Plan;
import MyJtds.SqlHelper;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class MainActivity extends Activity {
// 定义控件
private EditText etId;
private EditText etPcb;
private EditText etAmount;
private Button btnInsert;
private Button btnUpdate;
private Button btnDelete;
private Button btnClear;
private Button btnSelect;
private GridView gv;
// 定义变量
private String id = "";
private String pcb = "";
private String amount = "";
// SQL帮助类,参数用于设置连接字符串,参数1:主机ip,参数2:数据库名,参数3:用户名,参数4:用户密码
private SqlHelper sh = new SqlHelper("192.168.1.1", "SYSTEM TEST", "sa", "123");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到变量,并且赋值
etId = (EditText) findViewById(R.id.etId);
etPcb = (EditText) findViewById(R.id.etPcb);
etAmount = (EditText) findViewById(R.id.etAmount);
btnInsert = (Button) findViewById(R.id.btnInsert);
btnUpdate = (Button) findViewById(R.id.btnUpdate);
btnDelete = (Button) findViewById(R.id.btnDelete);
btnClear = (Button) findViewById(R.id.btnClear);
btnSelect = (Button) findViewById(R.id.btnSelect);
gv = (GridView) findViewById(R.id.gv);
// 绑定按钮的click事件监听,click事件触发后,运行clickEvent()方法
// 绑定的方式都是clickEvent(),到时在方法体中判断按下的是哪个按键
btnInsert.setOnClickListener(clickEvent());
btnUpdate.setOnClickListener(clickEvent());
btnDelete.setOnClickListener(clickEvent());
btnClear.setOnClickListener(clickEvent());
btnSelect.setOnClickListener(clickEvent());
}
// clickEvent()
private OnClickListener clickEvent() {
// TODO Auto-generated method stub
return new OnClickListener() {
// clickEvent()方法体,参数是控件的基类View,必须加上final
@Override
public void onClick(final View view) {
// TODO Auto-generated method stub
// 用view来判断按下的哪个按钮
if (view == btnClear) {
// ClearEdit()方法用于复位控件
ClearEdit();
} else {
// 如果不是btnClear,那就是增删改查的按钮,必须开启新的线程进行操作
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 通过Message类来传递结果值,先实例化
Message msg = Message.obtain();
// 下面分别是增删改查方法
if (view == btnInsert) {
// 设定msg的类型,用what属性,便于后面的代码区分返回的结果是什么类型
// 这里的1是指操作是否成功,String
// 这里的2是指查询的结果,String,用json的形式表示
msg.what = 1;
msg.obj = Insert();
} else if (view == btnUpdate) {
msg.what = 1;
msg.obj = Update();
} else if (view == btnDelete) {
msg.what = 1;
msg.obj = Delete();
} else if (view == btnSelect) {
String jsonResult = Select();
msg.what = 2;
msg.obj = jsonResult;
} else {
}
// 执行完以后,把msg传到handler,并且触发handler的响应方法
handler.sendMessage(msg);
}
});
// 进程开始,这行代码不要忘记
thread.start();
}
}
};
}
// Handler类用于接收Message的值,并且其父类有一个默认的handleMessage方法,用super。handleMessage()方法,传入msg,就能控制主线程的控件了
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
// 调用super的方法,传入handler对象接收到的msg对象
super.handleMessage(msg);
// 判断msg.what的值,有1和2,
// 1表示操作是否成功,2表示查询时得到的json结果
switch (msg.what) {
case 1:
// 获得执行的结果,String字符串,返回操作是否成功提示
String rst = msg.obj.toString();
// 使用气泡提示
Toast.makeText(getApplicationContext(), rst, Toast.LENGTH_SHORT).show();
break;
case 2:
// 获得查询的json结果
String jsonResult = msg.obj.toString();
// 控制台输出,用于监视,与实际使用无关
System.out.println(jsonResult);
// Gson类,用于json的转类型操作
Gson gson = new Gson();
// 定义查询到的结果类型,每一行记录映射为对象,本程序查询的是生产计划,所以一行记录表示一个品种的生产计划,用Plan类表示,用List收集全部Plan类
Type type = new TypeToken>() {
}.getType();
// 使用gson的fromJson()方法,参数1:json结果,参数2:想要转哪一个类型
List
// 由于要使用GridView表示,绑定数据时只能使用Map
// for循环从json转过来的List
for (Plan plan : plans) {
// 实例化用于接收plan的HashMap
HashMap
// 使用put()方法把数值加入到HashMap
hmPlans.put("id", plan.id);
hmPlans.put("pcb", plan.pcb);
hmPlans.put("amount", plan.amount);
// 把HashMap加入到List
mPlans.add(hmPlans);
}
// SimpleAdapter是GridView的适配器,参数1:上下文内容,参数2:List
// 参数4:String数组,指每一列要绑定到Map中的值,数组中的值就是上文“hmPlans.put("id",
// plan.id);”的键"id"
// 参数5:列头的显示文件,存放在res/values/strings.xml
SimpleAdapter sa = new SimpleAdapter(getApplicationContext(), mPlans, R.layout.planlist,
new String[] { "id", "pcb", "amount" }, new int[] { R.id.header1, R.id.header2, R.id.header3 });
// 把SimpleAdapter绑定到GridView
gv.setAdapter(sa);
// 气泡提示
Toast.makeText(getApplicationContext(), "读取成功!", Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(getApplicationContext(), "操作失败!", Toast.LENGTH_SHORT).show();
break;
}
}
};
// 用于接收EditText的输入值,并赋值到字符串
public void GetMsg() {
id = etId.getText().toString().trim();
pcb = etPcb.getText().toString().trim();
amount = etAmount.getText().toString().trim();
}
// Insert()方法,通过判断受影响行数,返回“添加成功”或“操作失败”
public String Insert() {
String sql = "INSERT INTO [DayPlan]([ID],[PCBA],[AMOUNT]) VALUES (?,?,?)";
GetMsg();
List
public String Update() {
String sql = "UPDATE [DayPlan] SET [PCBA]=?,[AMOUNT]=? where [ID]=?";
GetMsg();
// params用于存放变量参数,即sql中的“?”
List
params.add(pcb);
params.add(amount);
params.add(id);
try {
int count = sh.ExecuteNonQuery(sql, params);
if (count == 1) {
return "更新成功!";
} else {
return "操作失败!";
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
return "操作失败!";
}
}
public String Delete() {
String sql = "DELETE FROM [DayPlan] where [ID]=?";
GetMsg();
List
params.add(id);
try {
int count = sh.ExecuteNonQuery(sql, params);
if (count == 1) {
return "删除成功!";
} else {
return "操作失败!";
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
return "操作失败!";
}
}
// Select()方法,查询生产计划
public String Select() {
String sql = "SELECT [ID] AS id,[PCBA] AS pcb,[AMOUNT] AS amount FROM [DayPlan] ORDER BY id";
String jsonResult = null;
try {
// sh.ExecuteQuery(),参数1:查询语句,参数2:查询用到的变量,用于本案例不需要参数,所以用空白的new
// ArrayList
jsonResult = sh.ExecuteQuery(sql, new ArrayList
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
return null;
}
return jsonResult;
}
// 界面复位,清空显示
public void ClearEdit() {
//清空文本输入框
etId.setText("");
etPcb.setText("");
etAmount.setText("");
//etId获得焦点
etId.setFocusable(true);
etId.setFocusableInTouchMode(true);
etId.requestFocus();
etId.requestFocusFromTouch();
//清空GridView的绑定值
gv.setAdapter(null);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}