异常“SQLiteConstraintException:NOT NULL约束失败:listas.id(代码1299)”

时间:2018-01-17 00:20:14

标签: java android sqlite android-room

我正在开发一个Android项目,使用SQLite的Room库。这是一个待办事项列表应用程序,允许您创建家务琐事列表,每个琐事都有一个子事务清单。但是,我得到了一个例外:

android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: listas.id (code 1299)

对不起,我的大部分代码和评论都是西班牙语,但我希望这是可以理解的:

类“listas”指的是父列表,类“elementos”指的是子句列表。 “anadir”一词意味着添加。

数据库类

package pe.quality.todolist;

import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.arch.persistence.room.migration.Migration;
import android.content.Context;
import android.support.annotation.NonNull;

/**
 * Created by Arturo Cuya on 11/01/2018.
 */

// Clase de la Room Database

@Database(entities = {Lista.class,Elemento.class},version = 4,exportSchema = false)

public abstract class DB extends RoomDatabase {
    private static DB instance;
    public abstract listaDao listaDao();
    public abstract elementoDao elementoDao();

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            // Se añadió a las elementos la columna color
            database.execSQL("ALTER TABLE elementos ADD COLUMN color");
        }
    };

    static final Migration MIGRATION_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            // Se creó la tabla de elementos de cada lista
            database.execSQL("CREATE TABLE elementos(id INTEGER," +
                    "nombreElemento TEXT," +
                    "listaId INTEGER," +
                    "PRIMARY KEY(id)" +
                    "FOREIGN KEY (foreignKeys) REFERENCES listas(id)");
        }
    };

    static final Migration MIGRATION_3_4 = new Migration(3,4) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE listas ALTER COLUMN id TEXT NOT NULL");
            database.execSQL("ALTER TABLE elementos ALTER COLUMN id TEXT NOT NULL");
            database.execSQL("ALTER TABLE elementos ALTER COLUMN listaid TEXT");
            database.execSQL("CREATE INDEX listaId ON elementos(listaId)");
        }
    };

    public static DB getInstance(Context context){
        return Room.databaseBuilder(context.getApplicationContext(),DB.class,"DB_v4")
                .fallbackToDestructiveMigration()
                .allowMainThreadQueries()
                .build();
    }


}

Lista类:

package pe.quality.todolist;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;

// Clase del objeto lista. Cada vez que una clase se marca con el tag @Entity, se sabe que cuando
// se instancie se añadirá a la base de datos.

@Entity (tableName = "listas")
public class Lista {
    @PrimaryKey (autoGenerate = false)
    @NonNull
    private String id;

    @ColumnInfo(name = "lista_nombre")
    private  String listaNombre;

    @ColumnInfo(name = "lista_fechaHora")
    private  String listaFechaHora;

    @ColumnInfo(name = "lista_ubicacion")
    private  String listaUbicacion;

    @ColumnInfo(name = "color")
    private int color;


    public Lista(String listaNombre, String listaFechaHora, String listaUbicacion, int color) {
        this.listaNombre = listaNombre;
        this.listaFechaHora = listaFechaHora;
        this.listaUbicacion = listaUbicacion;
        this.color = color;
    }

    public String getListaNombre() {
        return listaNombre;
    }

    public void setListaNombre(String listaNombre) {
        this.listaNombre = listaNombre;
    }

    public String getListaFechaHora() {
        return listaFechaHora;
    }

    public void setListaFechaHora(String listaFechaHora) {
        this.listaFechaHora = listaFechaHora;
    }

    public String getListaUbicacion() {
        return listaUbicacion;
    }

    public void setListaUbicacion(String listaUbicacion) {
        this.listaUbicacion = listaUbicacion;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }


    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }
}

Lista适配器类:

package pe.quality.todolist;

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.List;

// Adapter del objeto lista. Un adapter actúa como puente entre AdapterView y la base de datos de
// donde se extraen sus datos. Cada elemento de la base de datos debe tener su propio Adapter.

class ListaAdapter extends RecyclerView.Adapter<ListaAdapter.ViewHolder> {  // Esta extensión es importante
    public List<Lista> listas;
    private Context context;

    public ListaAdapter(List<Lista> listas, Context context) {   // Constructor de la clase
        this.listas = listas;   // En esta lista se almacenarán las instancias de Lista,
                                // para luego ponerlas en la DB
        this.context = context;
    }

    @Override
    public ListaAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.vista_lista_fila,parent,false);
        return new ViewHolder(view);
        // Esta función retorna una variable que contiene el archivo xml que será rellenado con datos
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        // Aquí se capturan las posiciones en donde se guardarán los datos de la DB
        public TextView listaNombre;
        public TextView listaFechaHora;
        public TextView listaUbicacion;
        public LinearLayout contenedor;
        public ViewHolder(View itemView) {
            super(itemView);
            listaNombre = itemView.findViewById(R.id.listaNombre);
            listaFechaHora = itemView.findViewById(R.id.listaFechaHora);
            listaUbicacion = itemView.findViewById(R.id.listaUbicacion);
            contenedor = itemView.findViewById(R.id.vistaFila);
        }
    }

    @Override
    public void onBindViewHolder(final ListaAdapter.ViewHolder holder, int position) {
        // Aquí se coloca en cada instancia del archivo xml los datos correspondientes de cada elemento de la DB
        holder.listaNombre.setText(listas.get(position).getListaNombre());
        holder.listaFechaHora.setText(listas.get(position).getListaFechaHora());
        holder.listaUbicacion.setText(listas.get(position).getListaUbicacion());
        //holder.contenedor.setId(listas.get(position).getId());
        holder.contenedor.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(context,ListActivity.class);
                intent.putExtra("EXTRA_NOMBRE_FILA",
                        listas.get(holder.getAdapterPosition())
                                .getListaNombre());
                intent.putExtra("EXTRA_FH_FILA",
                        listas.get(holder.getAdapterPosition())
                                .getListaFechaHora());
                intent.putExtra("EXTRA_UBICACION_FILA",
                        listas.get(holder.getAdapterPosition())
                                .getListaUbicacion());
                intent.putExtra("EXTRA_ID_FILA",
                        listas.get(holder.getAdapterPosition())
                                .getId());
                context.startActivity(intent);
            }
        });
    }

    @Override
    public int getItemCount() { // Esto devuelve el número de elementos de la DB
        return listas.size();
    }


}

Lista DAO:

package pe.quality.todolist;

import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.Query;

import java.util.List;

/**
 * Created by Arturo Cuya on 11/01/2018.
 */

// El archivo dao es el que se encarga de acceder a los datos. Cada elemento distinto de la DB debe tener un DAO
// Dao : Data access object

@Dao
public interface listaDao {
    @Query("SELECT * FROM listas")
    List<Lista> getAllListas();

    @Insert
    void insertAll(Lista... listas);

}

元素类:

package pe.quality.todolist;

import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;

import static android.arch.persistence.room.ForeignKey.CASCADE;

// Clase del elemento de lista. Cada elemento está enlazado a una lista mediante la Foreign Key "listaId"

@Entity(tableName = "elementos",
        foreignKeys = @ForeignKey(
        entity = Lista.class,
        parentColumns = "id",
        childColumns = "listaId",
        onDelete = CASCADE),
        indices = @Index(value = "listaId"))

public class Elemento {
    @PrimaryKey(autoGenerate = false)
    @NonNull
    public String id;
    public String nombreElemento;
    public final String listaId;

    public Elemento(String nombreElemento, final String listaId){

        this.nombreElemento = nombreElemento;
        this.listaId = listaId;
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public String getNombreElemento() {
        return nombreElemento;
    }
    public void setNombreElemento(String nombreElemento) {
        this.nombreElemento = nombreElemento;
    }

    public String getListaId() {
        return listaId;
    }

}

Elemento适配器:

package pe.quality.todolist;

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

/**
 * Created by Arturo Cuya on 15/01/2018.
 */

class ElementoAdapter extends RecyclerView.Adapter<ElementoAdapter.ViewHolder> {  // Esta extensión es importante
    public List<Elemento> elementos;
    private Context context;

    public ElementoAdapter(List<Elemento> elementos, Context context) {   // Constructor de la clase
        this.elementos = elementos;
        this.context = context;
    }

    @Override
    public ElementoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.vista_elemento_fila,parent,false);
        return new ViewHolder(view);
        // Esta función retorna una variable que contiene el archivo xml que será rellenado con datos
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        // Aquí se capturan las posiciones en donde se guardarán los datos de la DB
        public TextView elementoNombre;
        public LinearLayout contenedor;
        public ViewHolder(View itemView) {
            super(itemView);
            elementoNombre = itemView.findViewById(R.id.nombreElemento);
            contenedor = itemView.findViewById(R.id.elementoContenedor);

            contenedor.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(context, "Has hecho click", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    @Override
    public void onBindViewHolder(ElementoAdapter.ViewHolder holder, int position) {
        // Aquí se coloca en cada instancia del archivo xml los datos correspondientes de cada elemento de la DB
        holder.elementoNombre.setText(elementos.get(position).getNombreElemento());
    }

    @Override
    public int getItemCount() { // Esto devuelve el número de elementos de la DB
        return elementos.size();
    }


}

元素DAO:

package pe.quality.todolist;

import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.Query;
import android.arch.persistence.room.Update;

import java.util.List;

@Dao
public interface elementoDao {
    @Insert
    void insert(Elemento elemento);

    @Update
    void update(Elemento... elementos);

    @Delete
    void delete(Elemento... Elementos);


    @Query("SELECT * FROM elementos WHERE listaId=:listaId")
    List<Elemento> findElementFromList(final String listaId);


}

Anadir lista类:

package pe.quality.todolist;

import android.arch.persistence.room.Room;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

// Esta clase se encarga de añadir un nuevo objeto lista a la Room Database,
// y de la interfaz de la vista activity_anadir_lista.xml

public class AnadirLista extends AppCompatActivity {

    EditText nombre_nueva_lista;
    Button btn_anadir_lista;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_anadir_lista);

        nombre_nueva_lista = findViewById(R.id.nombre_nueva_lista);
        btn_anadir_lista = findViewById(R.id.btn_anadir_lista);

        final DB db = DB.getInstance(this);

        btn_anadir_lista.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {    // Acciones que realiza el botón btn_anadir_lista
                String time = new SimpleDateFormat("hh:mm:ss").format(System.currentTimeMillis());

                Lista lista = new Lista(
                        nombre_nueva_lista.getText().toString(),
                        DateFormat.getDateInstance().format(Calendar.getInstance().getTime()) + "\n" +
                                time,
                        "Ubicación respectiva", 1);
                db.listaDao().insertAll(lista);
                startActivity(new Intent(AnadirLista.this,MainActivity.class));
//              ^ Luego de añadir el nuevo elemento a la DB, la vista regresa a activity_main.xml
            }
        });
    }
}

Anadir elemento类:

package pe.quality.todolist;

import android.content.Intent;
import android.support.design.widget.TextInputEditText;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class AnadirElemento extends AppCompatActivity {

    TextInputEditText nombreElemento;
    Button anadirElemento;

    final String id_fila_aux = getIntent().getStringExtra("EXTRA_ID_FILA");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_anadir_elemento);

        nombreElemento = findViewById(R.id.nombre_nuevo_elemento);
        anadirElemento = findViewById(R.id.btn_anadir_elemento);

        final DB db = DB.getInstance(this);

        anadirElemento.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Elemento elemento = new Elemento(nombreElemento.getText().toString(),id_fila_aux    );
                db.elementoDao().insert(elemento);
                startActivity(new Intent(getApplicationContext(),ListActivity.class));
            }
        });
    }
}

主要活动:

package pe.quality.todolist;

import android.app.Activity;
import android.content.Intent;
import android.support.design.widget.FloatingActionButton;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import java.util.List;

// La actividad de la pantalla principal

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity"; // Necesario para crear tags
    FloatingActionButton fab;
    RecyclerView recyclerView;
    RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fab = findViewById(R.id.mainFab);
        recyclerView = findViewById(R.id.recycler_view);


        final DB db = DB.getInstance(this);
        List<Lista> listas = db.listaDao().getAllListas();

        // Estas tres lineas inicializan el RecyclerView, el adapter y los setean
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ListaAdapter(listas, this);
        recyclerView.setAdapter(adapter);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Al presionar el fab, la pantalla cambia a activity_anadir_lista.xml
                Intent intent = new Intent(MainActivity.this,AnadirLista.class);
                startActivity(intent);
            }
        });


    }
}

列出活动(您可以在其中看到您在主要活动中选择的特定列表,查看其子项并添加新子项

package pe.quality.todolist;

import android.app.Activity;
import android.content.Intent;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.util.List;

public class ListActivity extends Activity {
    FloatingActionButton fab;
    TextView nombreFila;
    TextView fechaHoraFila;
    TextView ubicacionFila;

    RecyclerView recyclerView;
    RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        String nombre_fila_aux = getIntent().getStringExtra("EXTRA_NOMBRE_FILA");
        String fh_fila_aux = getIntent().getStringExtra("EXTRA_FH_FILA");
        String ubicacion_fila_aux = getIntent().getStringExtra("EXTRA_UBICACION_FILA");

        final String id_fila_aux = getIntent().getStringExtra("EXTRA_ID_FILA");

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);

        final DB db = DB.getInstance(this);
        List<Elemento> elementos = db.elementoDao().findElementFromList(id_fila_aux);

        nombreFila = findViewById(R.id.listaNombreInside);
        fechaHoraFila = findViewById(R.id.listaFechaHoraInside);
        ubicacionFila = findViewById(R.id.listaUbicacionInside);
        recyclerView = findViewById(R.id.recycler_view);

        nombreFila.setText(nombre_fila_aux);
        fechaHoraFila.setText(fh_fila_aux);
        ubicacionFila.setText(ubicacion_fila_aux);

        // Estas tres lineas inicializan el RecyclerView, el adapter y los setean
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ElementoAdapter(elementos,this);
        recyclerView.setAdapter(adapter);

        fab = findViewById(R.id.listaFabInside);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), AnadirElemento.class);
                intent.putExtra("EXTRA_LISTA_ID",id_fila_aux);
                startActivity(intent);
            }
        });

    }
}

我知道要审核的代码很多,但我会真诚地感谢任何能引导我解决此问题的方法,因为我必须在周六提出这个应用程序:( < / p>

1 个答案:

答案 0 :(得分:0)

这就是你收到错误的原因:

database.execSQL("ALTER TABLE listas ALTER COLUMN id TEXT NOT NULL");

此按钮将Listas添加到数据库中,但未插入id

btn_anadir_lista.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {    // Acciones que realiza el botón btn_anadir_lista
            String time = new SimpleDateFormat("hh:mm:ss").format(System.currentTimeMillis());

            Lista lista = new Lista(
                    nombre_nueva_lista.getText().toString(),
                    DateFormat.getDateInstance().format(Calendar.getInstance().getTime()) + "\n" +
                            time,
                    "Ubicación respectiva", 1);
            db.listaDao().insertAll(lista);
            startActivity(new Intent(AnadirLista.this,MainActivity.class));  
            //  ^ Luego de añadir el nuevo elemento a la DB, la vista regresa a activity_main.xml
        }
    });

这是设置Lista的构造函数,在这里我看不到id

public Lista(String listaNombre, String listaFechaHora, String listaUbicacion, int color) {
    this.listaNombre = listaNombre;
    this.listaFechaHora = listaFechaHora;
    this.listaUbicacion = listaUbicacion;
    this.color = color;
}


因此,您需要将Listas.id插入数据库,因为NOT NULL会因id插入null而违反SharedModule约束。