我正在开发一个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>
答案 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
约束。