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.
E ae amigo, aqui no meu teste não deu certo!
ResponderExcluirQual a versão do Android que você usou?
Eu usei a 2.3.3, será que é isso?
Grato desde já!
Felipe,
ResponderExcluirDiga qual o erro que teve ou em qual momento não funcionou pra tentar te ajudar.
A versão 2.3.3 deveria funcionar ...
Seguindo pelo Debug, dá pau nesse trecho:
Excluirpublic class ConexaoDao{
@SuppressLint("NewApi")
public Connection dbConnect(ObjetoConexao objConexao) {
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder().permitAll().build();
Exatamente onde instância esse ThreadPolicy!
Felipe,
ExcluirEntão, essa Thread é a que verifica algum eventual erro na execução. Precisaríamos ver qual o erro que ela fornece para ver onde está o erro.
Se ela finaliza a execução, significa que ela identificou algum erro e não vai prosseguir com a conexão.
Veja o Log:
Excluir02-19 17:30:28.737: D/dalvikvm(280): VFY: replacing opcode 0x22 at 0x0000
02-19 17:30:28.737: D/dalvikvm(280): VFY: dead code 0x0002-006f in Lcom/example/testesqlserver/ConexaoDao;.dbConnect (Lcom/example/testesqlserver/ObjetoConexao;)Ljava/sql/Connection;
02-19 17:30:28.747: D/AndroidRuntime(280): Shutting down VM
02-19 17:30:28.747: W/dalvikvm(280): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
02-19 17:30:28.777: E/AndroidRuntime(280): FATAL EXCEPTION: main
02-19 17:30:28.777: E/AndroidRuntime(280): java.lang.NoClassDefFoundError: android.os.StrictMode$ThreadPolicy$Builder
02-19 17:30:28.777: E/AndroidRuntime(280): at com.example.testesqlserver.ConexaoDao.dbConnect(ConexaoDao.java:15)
02-19 17:30:28.777: E/AndroidRuntime(280): at com.example.testesqlserver.Dao.nomeTabela(Dao.java:18)
02-19 17:30:28.777: E/AndroidRuntime(280): at com.example.testesqlserver.MainActivity.onCreate(MainActivity.java:18)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.os.Handler.dispatchMessage(Handler.java:99)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.os.Looper.loop(Looper.java:123)
02-19 17:30:28.777: E/AndroidRuntime(280): at android.app.ActivityThread.main(ActivityThread.java:4627)
02-19 17:30:28.777: E/AndroidRuntime(280): at java.lang.reflect.Method.invokeNative(Native Method)
02-19 17:30:28.777: E/AndroidRuntime(280): at java.lang.reflect.Method.invoke(Method.java:521)
02-19 17:30:28.777: E/AndroidRuntime(280): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
02-19 17:30:28.777: E/AndroidRuntime(280): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
02-19 17:30:28.777: E/AndroidRuntime(280): at dalvik.system.NativeStart.main(Native Method)
02-19 17:30:32.197: I/Process(280): Sending signal. PID: 280 SIG: 9
Amigo, estou com um problema que dá o seguinte erro: net.sourceforce.jtds.jdbc.Driver Já Copiei e adicionei o jtds.jar para o projeto e mesmo assim não consigo resolver esse erro ... Espero que possa me ajudar! Desde já agradeço.
ResponderExcluirJuninho,
ExcluirDê maiores detalhes sobre esse erro. O que vem depois de net.sourceforce.jtds.jdbc.Driver ?
O seu SQL server está com o usuário sa e a senha 1234, como no exemplo ? Se estiver diferente, você alterou o objeto ?
Leandro, meu exemplo está tudo igual o que foi passado por voce ... criei um banco no sql server chamado teste1 e minha senha é 123456 mas já alterei na programação ... Eu já tentei fazer alguns exemplos mas não tive sucesso nos mesmos, e axei esse seu exemplo bem claro, mas não sei o motivo desse erro, eu acho que o eclipse não está encontrando meu Drive jdbc mas eu já copiei o arquivo jar e adicionei no projeto mas o erro continua... Baixei o arquivo jtds 1.3.0 é esse mesmo ?
ResponderExcluirDeu erro no driver, mesmo colocando na pasta lib. Segue o log:
ResponderExcluirW/System.err(1334): java.lang.ClassNotFoundException: net.sourceforge.jtds.jdbc.Driver
W/System.err(1334): at java.lang.Class.classForName(Native Method)
W/System.err(1334): at java.lang.Class.forName(Class.java:217)
W/System.err(1334): at java.lang.Class.forName(Class.java:172)
W/System.err(1334): at baguio.Baguio.ConexaoBD.dbConnect(ConexaoBD.java:20)
W/System.err(1334): at baguio.Baguio.BD.nomeTabela(BD.java:19)
W/System.err(1334): at baguio.Baguio.MainActivity.onCreate(MainActivity.java:20)
W/System.err(1334): at android.app.Activity.performCreate(Activity.java:5008)
W/System.err(1334): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
W/System.err(1334): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
W/System.err(1334): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
W/System.err(1334): at android.app.ActivityThread.access$600(ActivityThread.java:130)
W/System.err(1334): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
W/System.err(1334): at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err(1334): at android.os.Looper.loop(Looper.java:137)
W/System.err(1334): at android.app.ActivityThread.main(ActivityThread.java:4745)
W/System.err(1334): at java.lang.reflect.Method.invokeNative(Native Method)
W/System.err(1334): at java.lang.reflect.Method.invoke(Method.java:511)
W/System.err(1334): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
W/System.err(1334): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
W/System.err(1334): at dalvik.system.NativeStart.main(Native Method)
W/System.err(1334): Caused by: java.lang.NoClassDefFoundError: net/sourceforge/jtds/jdbc/Driver
W/System.err(1334): ... 20 more
W/System.err(1334): Caused by: java.lang.ClassNotFoundException: net.sourceforge.jtds.jdbc.Driver
W/System.err(1334): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
W/System.err(1334): at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
W/System.err(1334): at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
W/System.err(1334): ... 20 more
D/gralloc_goldfish(1334): Emulator without GPU emulation detected.
Leandro, estou com o mesmo problema do Juninho. Meu código está igual tbm, mas na hora do Class.forName ele fala q não achou o Driver. Como você fez a inclusão do JAR? Pode explicar passo a passo?
ResponderExcluirOlá Pessoal, talvez o problema que vocês estejam tendo esteja relacionado com a versão do driver jdts, eu estava usando a versão jtds-1.3.0.jar e estava recebendo o mesmo erro, então eu fiz um teste com a versão jtds-1.2.1.jar e funcionou perfeitamente. Segue o link:
ResponderExcluirhttp://www.java2s.com/Code/Jar/j/Downloadjtds121jar.htm
Amigo tem como voce mandar o exemplo por email resende.daniel83@gmail.com. grato e Parabéns
ResponderExcluirConsegui fazer funcionar até a versão jtds-1.2.8, a partir dai apresenta erro.
ResponderExcluirGalera estou tento um problema. Quando fui instalar o SQL SERVER 2008 eu mudei o server name. Que mudanças essa alteração implicam no projeto original? agradeço.
ResponderExcluirAté a versão jtds-1.2.8 funciona com android versão 2.x, mas na versão 4.x não funciona, alguma sugestão?
ResponderExcluirAlguem ja testou com mais de 100 aparelhos conectados ao mesmo tempo e se tem algum problema deixar conectado direto???
ResponderExcluirLeandro, Excelente post!!!
ResponderExcluirAgora minha dúvida é sobre a parte de segurança.
Este método tem implicações de segurança se o SQL Servir for um servidor remoto !?
Eu creio que não, até pq a chamada está na base classe DAO
Prezado Leandro , Obrigado pelas dicas. Sabe se irá funcionar em uma base de dados não na rede interna e sim na externa, um server SQL da minha pagina da net, ela aceita requisições de fora) Acha que colocando o ip externo, usuário e senha BD_SQL irá funcionar?
ResponderExcluirFunciona, desde que seu SQL Server esteja configurado para aceitar conexões externas via TCP/IP
ExcluirPqp amigo, perfeito. Orbigado :D
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirboa tarde leandro fiz a conexao seguindo seus passos aqui e funcionou perfeitamente. só estou com um problema. caso não consiga comunicar com o banco de dados, ou não tenha conexao com a internet ele entra em uma tela preta e não me retorna nenhum erro. o que pode ser?
ResponderExcluirOla boa tarde. É necessário criar uma instancia do SQL?
ResponderExcluirExemplo: 192.168.0.1\sqlexpress?
pois não estou conseguindo conectar ao meu banco de dados.