quinta-feira, 17 de janeiro de 2013

Conectar Android com SQL Server usando jTDS

Esse é o meu primeiro posts nesse Blog e, não sei se haverão outros, no entanto, achei importante compartilhar pois, verifico que muitas pessoas tem dúvidas de como conectar um app Android com SQL Server em conexão direta e, entre trolls e muito desconhecimento de documentação do jTDS, é preciso muito garimpo para conseguir fazer essa conexão.

Então, vamos lá:

Primeiro, eu criei uma Database no SQL Server 2008, chamei de teste1:

CREATE DATABASE teste1

Deixei-a ativa:

USE teste1

Criei a tabela teste:

CREATE TABLE teste (
id int not null primary key,
nome varchar(100))

Por fim, inseri a seguinte linha (ATENÇÃO - É um teste simples):

INSERT INTO teste VALUES (1,'Leandro Colevati')


A partir daí, é só mexer no Eclipse + SDK Android. Se você veio até aqui, significa que, já sabe criar um projeto, portanto, vou pular a introdução.

Se você não conhece o jTDS, http://jtds.sourceforge.net/
Faça o download do driver.
Para quem está acostumado com Java Desktop, a integração é feita diferente, não se usa o Build Path.

Basta copiar o jar do jTDS para a pasta libs do seu projeto Android.

Feito isso, temos que modificar o AndroidManifest.xml, adicionando as seguintes linhas, que permitem as conexões de rede e internet:


<uses-permission android:name="android.permission.VIBRATE"/>  
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

No meu exemplo, projeto que chamei de TestejTDS2, ficou:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testejtds2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    
    <uses-permission android:name="android.permission.VIBRATE"/>  
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.testejtds2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Como existem milhões de formas de se descascar esse abacaxi, fiz bem dividido, pra facilitar a busca de erros.

Criei um objeto chamado ObjetoConexao.java:


public class ObjetoConexao {
public String db_connect_string;
public String db_name;
public String db_userid;
public String db_password;

public String getDb_connect_string() {
return db_connect_string;
}
public void setDb_connect_string(String db_connect_string) {
this.db_connect_string = db_connect_string;
  }
public String getDb_name() {
return db_name;
}
public void setDb_name(String db_name) {
this.db_name = db_name;
}
public String getDb_userid() {
return db_userid;
}
public void setDb_userid(String db_userid) {
this.db_userid = db_userid;
}
public String getDb_password() {
return db_password;
}
public void setDb_password(String db_password) {
this.db_password = db_password;
}
}

Criei uma Classe ConexaoDao.java:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import android.annotation.SuppressLint;
import android.os.StrictMode;

public class ConexaoDao {
@SuppressLint("NewApi")
public Connection dbConnect(ObjetoConexao objConexao) {
StrictMode.ThreadPolicy policy = new 
                StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Connection conn = null;
String connectionUrl = null;
try {
Class.forName("net.sourceforge.jtds.jdbc.Driver");
connectionUrl = "jdbc:jtds:sqlserver://" + 
                        objConexao.db_connect_string + ";" +
"databaseName=" + objConexao.db_name + ";user=" 
                        +objConexao.db_userid + 
                        ";password=" +  objConexao.db_password + ";";
conn = DriverManager.getConnection(connectionUrl);
} catch (SQLException se) {
se.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception se) {
se.printStackTrace();
}
return conn;
}
}

A Classe ConexaoDao.java é quem provê a conexão com o SQL Server, pra quem faz conexão do Java com o SQL Server, verá que não há diferença nenhuma.

Por fim, fiz a classe Dao.java com os métodos de chamada do Banco de Dados. Nesse exemplo, só há um método de retorno de String.


import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Dao {

public String nomeTabela() {
String nome = "";
ConexaoDao conexao = new ConexaoDao();
ObjetoConexao objConexao = new ObjetoConexao();
objConexao.db_connect_string = "10.0.2.2:1433";
objConexao.db_name = "teste1";
objConexao.db_userid = "sa";
objConexao.db_password = "1234";
Connection conn = conexao.dbConnect(objConexao);
if (conn != null) {
try {
Statement statement = conn.createStatement();
String queryString = "select nome from teste where id = 
                        1";
ResultSet rs;
rs = statement.executeQuery(queryString);
if (rs.next()) {
nome = rs.getString("nome");
}
} catch (SQLException e) {
nome = e.getMessage();
}
}
return nome;
}

}


É fácil de verificar que a String queryString pode ser modificado por qualquer chamada de Banco de Dados
Essa talvez seja a Classe mais importante do nosso projeto, uma vez que ela que chama a conexão com o Banco de Dados.
O ip de conexão (10.0.2.2:1433), que é o db_connect_string que está no Objeto criado anteriormente, é o ip utilizado pelo emulador do SDK Android para se conectar com a instância do SQL Server instalada no computador. A porta 1433 é a porta utilizada pelo SQL Server.
Não esqueça que, quando for gerar o apk, você deverá mudar o ip de conexão para o ip do computador servidor de SQL Server na rede.
O db_name, no mesmo objeto, é a Database criada no início do projeto.
As Strings db_userid e db_password são os dados de usuário para conexão com o SQL Server.

Como o meu ResultSet só retorna um valor (forçado por mim), eu coloquei o rs.getString dentro de um IF, caso o seu retorne mais de um valor, utilize o WHILE.
Construído o objeto, ele é passado para a classe ConexaoDao.java com os dados para que a conexão seja feita.

Para verificar se o Android está recebendo o dado, eu criei uma tela com um TEXTVIEW que vai receber o nome:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/tvNome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="31dp"
        android:text=" - " />

</RelativeLayout>

E alterei a MainActivity.java para passar o dado para a TEXTVIEW:


package com.example.testejtds2;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

TextView tvNome;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvNome = (TextView) findViewById(R.id.tvNome);
Dao dao = new Dao();
String nome = dao.nomeTabela();
tvNome.setText(nome);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
             // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
     return true;
}

}

É importante verificar que eu fiz o link do TEXTVIEW criado para que ele possa receber o valor. Instanciei a classe Dao e chamei o método, jogando a saída no TEXTVIEW.

Pronto !

Muitos podem dizer que é ambiente inseguro, que não serve para criar uma conexão Internet. Que a melhor solução é criar um WebService.
Considero que, cada um sabe onde seu calo aperta e, se há a necessidade e a tecnologia permite, use.