【Android】直接连接SqlServer

本文将介绍开发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的网络操作权限

  这是博主在之前的文章强调过的,这个必须先打开,以免以后遗忘。

 


    package="com.test.androidsqltest"
    android:versionCode="1"
    android:versionName="1.0" >
    
   

            android:minSdkVersion="14"
        android:targetSdkVersion="21" />

            android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
                    android:name=".MainActivity"
            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

 

 


    JtdsTest
    Hello world!
    Settings
    Insert

 

  界面中的Button,显示文本为Insert

 

  2、res=>layout=>activity_main.xml

 

    xmlns:tools="http://schemas.android.com/tools"
    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:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

            android:id="@+id/btnInsert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="50dp"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="40dp"
        android:text="@string/btnInsert" />

 

 

 

  加入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 params) {
        try {
            con = DriverManager.getConnection(this.connStr, this.userName, this.userPwd);
            pstm = con.prepareStatement(sql);
            if (params != null && !params.equals("")) {
                for (int i = 0; i < params.size(); i++) {
                    pstm.setObject(i + 1, params.get(i));
                }
            }
            return pstm.executeUpdate();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return -1;
        } finally {
            try {
                pstm.close();
                con.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public String ExecuteQuery(String sql, List params) {
        // TODO Auto-generated method stub
        JSONArray jsonArray = new JSONArray();
        try {
            con = DriverManager.getConnection(this.connStr, this.userName, this.userPwd);
            pstm = con.prepareStatement(sql);
            if (params != null && !params.equals("")) {
                for (int i = 0; i < params.size(); i++) {
                    pstm.setObject(i + 1, params.get(i));
                }
            }
            ResultSet rs = pstm.executeQuery();
            ResultSetMetaData rsMetaData = rs.getMetaData();
            while (rs.next()) {
                JSONObject jsonObject = new JSONObject();
                for (int i = 0; i < rsMetaData.getColumnCount(); i++) {
                    String columnName = rsMetaData.getColumnLabel(i + 1);
                    String value = rs.getString(columnName);
                    jsonObject.put(columnName, value);
                }
                jsonArray.put(jsonObject);
            }
            return jsonArray.toString();
        } catch (Exception e) {
            // TODO: handle exception
            return null;
        } finally {
            try {
                pstm.close();
                con.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

 

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 plans = gson.fromJson(jsonResult, type);

                // 由于要使用GridView表示,绑定数据时只能使用Map的类型,并且多个记录时,要用List>
                // 先实例化
                List> mPlans = new ArrayList>();
                // 实例化一个title,是GridView的列头
                Map title = new HashMap();
                title.put("id", "序号");
                title.put("pcb", "机种名");
                title.put("amount", "计划数");
                // 首先把表头追加到List>
                mPlans.add(title);

                // for循环从json转过来的List
                for (Plan plan : plans) {
                    // 实例化用于接收plan的HashMap
                    HashMap hmPlans = new HashMap();
                    // 使用put()方法把数值加入到HashMap,参数1:键,参数2:值
                    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>对象,参数3:GridView的布局文件,指每一个item的布局,需要在res/layout中创建xml,
                // 参数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 params = new ArrayList();
        params.add(id);
        params.add(pcb);
        params.add(amount);
        try {
            int count = sh.ExecuteNonQuery(sql, params);
            if (count == 1) {
                return "添加成功!";
            } else {
                return "操作失败!";
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            System.out.println(e.getMessage());
            return "操作失败!";
        }
    }

    public String Update() {
        String sql = "UPDATE [DayPlan] SET [PCBA]=?,[AMOUNT]=? where [ID]=?";
        GetMsg();
        // params用于存放变量参数,即sql中的“?”
        List params = new ArrayList();
        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 = new ArrayList();
        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);
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Android技术)