Erro Foreign Key

Meus amigos, to perdido nesse código. Criei as tabelas mas quando vou gravar o cliente, dá um erro:

08-21 21:19:08.616: E/Database(146: Failure 1 (near “,”: syntax error) on 0x263a80 when preparing ‘create table carros (_id integer primary key autoincrement, id_cliente integer, modelo text, placa text), FOREIGN KEY (id_cliente) REFERENCES clientes (_id) ON DELETE RESTRICT ON UPDATE CASCADE);’.

O que pode ser? Não sei mais o que fazer.


private static final String DATABASE_NAME = "testeoficina";
	private static final int DATABASE_VERSION = 1;
	
	private static final String DATABASE_TABLE_CLIENTES = "clientes";
	private static final String DATABASE_TABLE_CARROS = "carros";
	
//  CAMPOS DA TABELA CLIENTES	
	public static final String KEY_IDCLI = "_id";
	public static final String KEY_NOME = "nome";
	public static final String KEY_ENDERECO = "endereco";

//  CAMPOS DA TABELA CARROS	
	public static final String KEY_IDCARRO = "_id";
	public static final String KEY_IDCLIENTE = "id_cliente";
	public static final String KEY_MODELO = "modelo";
	public static final String KEY_PLACA = "placa";

//  CRIA A TABELA CLIENTES
	private static final String DATABASE_CREATE_CLIENTES = "create table " + DATABASE_TABLE_CLIENTES + " ("
	+ KEY_IDCLI + " integer primary key autoincrement, "
	+ KEY_NOME + " text, "
	+ KEY_ENDERECO + " text);";

	
//  CRIA A TABELA CARROS
	private static final String DATABASE_CREATE_CARROS = "create table " + DATABASE_TABLE_CARROS + " ("
	+ KEY_IDCARRO + " integer primary key autoincrement, "
	+ KEY_IDCLIENTE + " integer, "
	+ KEY_MODELO + " text, "
	+ KEY_PLACA + " text), FOREIGN KEY ( " + KEY_IDCLIENTE
	+ " ) REFERENCES " + DATABASE_TABLE_CLIENTES + " (" + KEY_IDCLI
	+ " ) ON DELETE RESTRICT ON UPDATE CASCADE);";


// Grava cliente
	public long gravarCliente(String nome, String endereco){
		ContentValues valores = new ContentValues();
		valores.put(KEY_NOME, nome);
		valores.put(KEY_ENDERECO, endereco);
		
		return mDb.insert(DATABASE_TABLE_CLIENTES, null, valores);
	}
	
// Grava carro
	public long gravarCarroCliente(String modelo, String placa, String idcliente){
		ContentValues valores = new ContentValues();
		valores.put(KEY_IDCLIENTE, idcliente);
		valores.put(KEY_MODELO, modelo);
		valores.put(KEY_PLACA, placa);
		return mDb.insert(DATABASE_TABLE_CARROS, null, valores);
	}


		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL(DATABASE_CREATE_CLIENTES);
			db.execSQL(DATABASE_CREATE_CARROS);
		}

Método do botão salvar Cliente:

		// Código do botão Salvar da Tela de Cadastro de Clientes
		salvar = (ImageButton) findViewById(R.id.btSalvar);

		salvar.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				String nomeString = Nome.getText().toString();
				String enderecoString = Endereco.getText().toString();
				mDb.open();
				if(Nome.getText().toString().equals("") || Endereco.getText().toString().equals("")){
					mensagemExibir("Erro ao Salvar","Dados em branco, verifique!");
					return;
				}
				
				mDb.gravarCliente(nomeString, enderecoString);
				mensagemExibir("Salvar","Cliente cadastrado com sucesso!");
				mDb.close();
			}
		});

Me parece que os parênteses da sua consulta sql não estão balanceados. Tem um ‘)’ a mais (depois de placa text).

Legal, meu amigo. Só que tem uma coisa estranha ai. Na consulta por placa, não traz nada. Grava mas não mostra.

O código é esse:

	public Cursor getCarroPorPlaca(String placaString){
		String[] selectionArgs = {placaString + "%"};  
		return mDb.rawQuery("SELECT placa, modelo, nome FROM clientes INNER JOIN carros ON clientes._id = carros.id_cliente WHERE placa like ?", selectionArgs);
	}

Pode me ajudar? Será que tem alguma coisa haver com String idcliente? É integer na tabela.

Posta a consulta aí. Você tá testando este select pelo terminal (adb) ou está direto no seu programa?

Estou, direto pelo programa. Como faço pelo adb? Você tem msn?

Tem certeza que há clientes e carros?

Pro inner join funcionar deve ter dados nas duas tabelas.

Use o adb shell pra fazer selects nas tabelas e tal. Dentro da pasta do android tem uma chamada platform-tools. Entre em modo texto nela e execute:

adb.exe shell

Depois execute o sqlite:

sqlite3 /data/data/pacote_da_app/databases/nome_do_banco

Aí vc pode executar os selects…

Dá mensagem de gravado com sucesso mas na hora de consultar necas. To testando pelo programa a consulta por cliente, funciona. No terminal, não traz nada.

Como listo as tabelas no adb?

Depois que você faz o que eu disse no post anterior, se tudo der certo, você entra no console do sqlite. Aí você pode executar selects, inserts, etc, como se fosse na sua app:

select * from carros;
select * from clientes;

Tá assim no terminal:

Ai coloquei: sqlite3 /data/data/pacote_da_app/databases/testeoficina

sqlite3 /data/data/pacote_da_app/databases/testeoficina

sqlite3 /data/data/pacote_da_app/databases/testeoficina
SQLite version 3.6.22
Enter “.help” for instructions
Enter SQL statements terminated with a ";"
sqlite>

E o cursor piscando.

Acho que aí vc tem que digitar a parte do:

sqlite3 /data/data/pacote_da_sua_app/databases/seu_banco

Coloquei e deu esse erro:

sqlite3 /data/data/pacote_da_sua_app/databases/testeoficina

Error: unable to open database “/data/data/pacote_da_app/databases/testeoficina”: unable to open database file

Beleza, no terminal deu certo. Ta gravando. No programa não traz.

A sql tá assim:

SELECT carros.placa, carros.modelo, clientes.nome FROM clientes INNER JOIN carros on clientes._id = carros.id_cliente;

No terminal não traz nada…

Tem clientes e carros gravados? Lembre-se que usando um inner join precisa ter dados nas duas tabelas (com ids relacionados).

Tem sim. Acho que o erro tá aqui:


	public Cursor getCarroPorPlaca(String placaString){
		String[] selectionArgs = {placaString + "%"};  

		return mDb.rawQuery("SELECT placa, modelo, nome FROM clientes INNER JOIN carros ON clientes._id = carros.id_cliente WHERE placa like ?", selectionArgs);
	}

Tá retornando isso digitando lá no terminal:

sqlite> select * from carros;
select * from carros;
1| 1|Passat|www-1234
sqlite> select * from clientes;
select * from clientes;
1|Fabio|o mesmo
sqlite>

Cara, pode ser apenas impressão minha, mas me parece que no select de carros, ele armazenou id_cliente como uma string e tem um espaço antes (" 1"), isso traria problemas no join. No sqlite mesmo especificando tipos, cada célula pode ter um tipo diferente.

Eu sugiro que lá no gravarCarroCliente, você passe o valor idCliente como um inteiro mesmo. Preferencialmente já passe para o método um inteiro. Basta:

int codigoCliente = Integer.valueOf(idCliente.trim());

Armazene este valor ao invés da string.

É só copiar esse código lá?

Para um teste inicial você pode fazer o seguinte:

// Grava carro
   public long gravarCarroCliente(String modelo, String placa, String idcliente){
      ContentValues valores = new ContentValues();
      valores.put(KEY_IDCLIENTE, Integer.valueOf(idcliente.trim()));
      valores.put(KEY_MODELO, modelo);
      valores.put(KEY_PLACA, placa);
      return mDb.insert(DATABASE_TABLE_CARROS, null, valores);
   }

Insira os valores novamente na tabela e veja se o inner join vai funcionar.

Entretanto, o melhor é que esta lógica de parsing não ficasse aí (vai funcionar igual, isto é apenas uma questão do local melhor para botar o código). Seria melhor fazer este parsing lá na view onde tu recebe o valor. Aí você possivelmente faz um tratamento de exceção adequado, mas isto é depois.

Ok, vou testar e retorno, ok? Como seria a sql para usar com INNER JOIN?