java - SQLite DB创建表时名称列错误

标签 java android sqlite

在android中创建表Movie并管理它,我编写了以下代码:

MovieDatabase.java

package com.example.msnma.movienotifier.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import com.example.msnma.movienotifier.MainActivity;
import com.example.msnma.movienotifier.databaseModel.MovieDBModel;
import com.example.msnma.movienotifier.databaseModel.TypeDBModel;
import com.example.msnma.movienotifier.mapper.MovieMapper;
import com.example.msnma.movienotifier.model.Movie;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class MovieDatabase extends SQLiteOpenHelper {

    private static final String LOG = "DatabaseHelper";
    private static final String DATABASE_NAME = "movieDatabase";
    private static final int DATABASE_VERSION = 1;

    //Table Names
    private static final String TABLE_MOVIE = "MovieTable";
    private static final String TABLE_TYPE = "TypeTable";
    private static final String TABLE_MOVIE_TYPE = "MovieTypeTable";
    //Table Fields
    private static final String MOVIE_ID ="movie_id";
    private static final String TITLE ="title";
    private static final String OVERVIEW = "overview";
    private static final String POSTER_URL = "posterUrl";
    private static final String BACKDROP_URL = "backdropUrl";
    private static final String TRAILER_URL = "trailerUrl";
    private static final String RELEASE_DATE = "releaseDate";
    private static final String RATING = "rating";
    private static final String ADULT = "adult";

    private static final String TYPE_ID = "type_id";
    private static final String TYPE_DESCR = "type_descr";

    private static final String MOVIE_TYPE_ID = "movie_type_id";
    private static final String M_ID = "movie_id";
    private static final String T_ID = "type_id";
    private static final String notify_datetime = "notify_datetime";

    // Table Create Statements
    private static final String CREATE_TABLE_MOVIE = "CREATE TABLE "
            + TABLE_MOVIE + "(" + MOVIE_ID + " INTEGER PRIMARY KEY," + TITLE + " TEXT,"
            + OVERVIEW + " TEXT," + POSTER_URL + " TEXT," + BACKDROP_URL + " TEXT," + TRAILER_URL + " TEXT,"
            + RATING + " REAL," + ADULT + " INTEGER," + RELEASE_DATE + " DATETIME," + notify_datetime + " DATETIME"  + ")";

    // Tag table create statement
    private static final String CREATE_TABLE_TYPE = "CREATE TABLE " + TABLE_TYPE
            + "(" + TYPE_ID + " INTEGER PRIMARY KEY," + TYPE_DESCR + " TEXT" + ")";

    // todo_tag table create statement
    private static final String CREATE_TABLE_MOVIE_TYPE = "CREATE TABLE "
            + TABLE_MOVIE_TYPE + "(" + MOVIE_TYPE_ID + " INTEGER PRIMARY KEY,"
            + M_ID + " INTEGER," + T_ID + " INTEGER" + ")";

    SQLiteDatabase database;

    public MovieDatabase(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        database = getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // creating required tables
        db.execSQL(CREATE_TABLE_MOVIE);
        db.execSQL(CREATE_TABLE_TYPE);
        db.execSQL(CREATE_TABLE_MOVIE_TYPE);
        insertType(db); //NOTA: questo va chiamato solo la prima volta che instanziamo il DB, AGGIUNGERE CONTROLLO!
        //in teoria onCreate verrà chiamato solo la prima volta...
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        // on upgrade drop older tables
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_MOVIE);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_TYPE);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_MOVIE_TYPE);

        // create new tables
        onCreate(db);
    }
    //questa funzione serve solo per inserire casualmente dai suggested movies, alcuni notify e watched movies
    public static void saveMoviesOnDB(List<Movie> movies, String type){
        MovieDatabase db = MainActivity.getMovieDatabase();
        MovieMapper mapper = new MovieMapper();
        List<MovieDBModel> moviesDB = mapper.toMovieDBModelList(movies);
        Integer typeId = 0;
        int index = 0;
        if(type.equals("notify")){
            typeId = 1;
        }else if(type.equals("watched")){
            typeId = 2;
            index = index+5;
        }else{
            //todo catch invalid type Id exception
        }
        for(int a = index; a<index+5; a++){
            insertMovie(moviesDB.get(a), typeId, db);
        }
    }
    //open the database, maybe not useful...
    public MovieDatabase open() throws SQLException
    {
        database = getWritableDatabase();
        return this;
    }

    // closing database
    public void closeDB() {
        SQLiteDatabase db = this.getReadableDatabase();
        if (db != null && db.isOpen())
            db.close();
    }

    //CRUDs
    public static void insertMovie(MovieDBModel movie, Integer typeId, MovieDatabase db) {
        SQLiteDatabase database = db.getWritableDatabase();
        if(checkMovieUniqueness(movie.getTitle(), db)) {        //NON VOGLIO INSERIRE PIù VOLTE LO STESSO FILM
            ContentValues values = new ContentValues();
            values.put(TITLE, movie.getTitle());
            values.put(OVERVIEW, movie.getOverview());
            values.put(POSTER_URL, movie.getPosterUrl());
            values.put(BACKDROP_URL, movie.getBackdropUrl());
            values.put(TRAILER_URL, movie.getTrailerUrl());
            values.put(RELEASE_DATE, movie.getReleaseDate().toString());
            values.put(RATING, movie.getRating());
            values.put(ADULT, movie.isAdult());
            //inizio nuovo codice
            if(typeId == 1) {
                values.put(notify_datetime, movie.getNotifyDate().toString());
                Log.i("TYPEID", "Siamo dentro");
            }
            //else values.putNull(NOTIFY_TIME_DATE);
            //fine nuovo codice
            // insert row
            long movieId = database.insert(TABLE_MOVIE, null, values);
            insertMovieType(movieId, typeId, db);
        }
    }

    public static void insertMovieType(Long movieId, Integer typeId, MovieDatabase db){
        SQLiteDatabase database = db.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(M_ID, movieId.intValue());
        values.put(T_ID, typeId);

        database.insert(TABLE_MOVIE_TYPE, null, values);
    }

    private void insertType(SQLiteDatabase db){

        ContentValues descr1 = new ContentValues();
        descr1.put(TYPE_DESCR, "NOTIFY");

        ContentValues descr2 = new ContentValues();
        descr2.put(TYPE_DESCR, "WATCHED");

        // insert row
        db.insert(TABLE_TYPE, null, descr1);
        db.insert(TABLE_TYPE, null, descr2);
    }

    private TypeDBModel getTypeByTypeDescr(String typeDescr) {
        SQLiteDatabase db = this.getReadableDatabase();

        String selectQuery = "SELECT  * FROM " + TABLE_TYPE + " WHERE "
                + TYPE_DESCR + " = " + typeDescr;

        Log.e(LOG, selectQuery);

        Cursor c = db.rawQuery(selectQuery, null);

        if (c != null)
            c.moveToFirst();

        TypeDBModel td = new TypeDBModel();
        td.setId(c.getInt(c.getColumnIndex(TYPE_ID)));
        td.setDescription((c.getString(c.getColumnIndex(TYPE_DESCR))));

        return td;
    }

    private static boolean checkMovieUniqueness(String movieTitle, MovieDatabase db) {
        SQLiteDatabase database = db.getReadableDatabase();

        String selectQuery = "SELECT  * FROM " + TABLE_MOVIE + " WHERE "
                + TITLE + " = " + "'"+ movieTitle + "'";

        Log.e(LOG, selectQuery);

        Cursor c = database.rawQuery(selectQuery, null);

        if(c.moveToFirst()){
            c.close();
            return false;
        }else{
            c.close();
            return true;
        }
    }

    public List<MovieDBModel> getAllMovieByType(String typeDescr) throws ParseException {
        List<MovieDBModel> movies = new ArrayList<MovieDBModel>();

        String selectQuery = "SELECT  * FROM " + TABLE_MOVIE + " mv, "
                + TABLE_TYPE + " type, " + TABLE_MOVIE_TYPE + " tmt WHERE type."
                + TYPE_DESCR + " = '" + typeDescr + "'" + " AND type." + TYPE_ID
                + " = " + "tmt." + T_ID + " AND mv." + MOVIE_ID + " = "
                + "tmt." + M_ID;

        Log.e(LOG, selectQuery);

        SQLiteDatabase db = this.getReadableDatabase();
        Cursor c = db.rawQuery(selectQuery, null);
        SimpleDateFormat sdf3 = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
        // looping through all rows and adding to list
        if (c.moveToFirst()) {
            do {
                MovieDBModel td = new MovieDBModel();
                td.setId(c.getInt((c.getColumnIndex(MOVIE_ID))));
                td.setTitle((c.getString(c.getColumnIndex(TITLE))));
                td.setOverview(c.getString(c.getColumnIndex(OVERVIEW)));
                td.setPosterUrl(c.getString((c.getColumnIndex(POSTER_URL))));
                td.setBackdropUrl((c.getString(c.getColumnIndex(BACKDROP_URL))));
                td.setTrailerUrl(c.getString(c.getColumnIndex(TRAILER_URL)));
                String dateString =c.getString((c.getColumnIndex(RELEASE_DATE)));
                Date date = sdf3.parse(dateString);
                td.setReleaseDate(date);
                td.setRating((c.getFloat(c.getColumnIndex(RATING))));
                int adult = c.getInt(c.getColumnIndex(ADULT));
                if(adult == 0){
                    td.setAdult(true);          //NOT sure if is the contrary
                }else{
                    td.setAdult(false);
                }

                movies.add(td);
            } while (c.moveToNext());
        }

        return movies;
    }

    public void deleteMovie(long tado_id) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_MOVIE, MOVIE_ID + " = ?",
                new String[] { String.valueOf(tado_id) });
    }

    public void deleteMovieType(long tado_id) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_MOVIE_TYPE, M_ID + " = ?",
                new String[] { String.valueOf(tado_id) });
    }

    public int updateMovieType(long id, long tag_id) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(T_ID, tag_id);

        // updating row
        return db.update(TABLE_MOVIE_TYPE, values, M_ID + " = ?",
                new String[] { String.valueOf(id) });
    }
}

现在,当应用程序运行时,我在日志中出现此错误:

2018-08-27 23:08:02.336 3786-3786/com.example.msnma.movienotifier E/SQLiteDatabase: Error inserting rating=7.0 title=Crazy Rich Asians notify_datetime=Thu Sep 27 09:30:00 GMT+02:00 2018 posterUrl=https://image.tmdb.org/t/p/w185/gnTqi4nhIi1eesT5uYMmhEPGNih.jpg releaseDate=Wed Aug 15 00:00:00 GMT+02:00 2018 adult=false trailerUrl=https://youtube.com/watch?v=PI5mBzQ1Iok overview=An American-born Chinese economics professor accompanies her boyfriend to Singapore for his best friend's wedding, only to get thrust into the lives of Asia's rich and famous. backdropUrl=https://image.tmdb.org/t/p/w300/zeHB7aP46Xs3u4aFLuAq2GFeUGb.jpg
    android.database.sqlite.SQLiteException: table MovieTable has no column named notify_datetime (Sqlite code 1): , while compiling: INSERT INTO MovieTable(rating,title,notify_datetime,posterUrl,releaseDate,adult,trailerUrl,overview,backdropUrl) VALUES (?,?,?,?,?,?,?,?,?), (OS error - 2:No such file or directory)
        at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
        at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:910)
        at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:521)
        at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:603)
        at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:63)
        at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
        at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1725)
        at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1593)
        at com.example.msnma.movienotifier.database.MovieDatabase.insertMovie(MovieDatabase.java:151)
        at com.example.msnma.movienotifier.adapter.MoviesAdapter$7.onClick(MoviesAdapter.java:378)
        at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:166)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:156)
        at android.app.ActivityThread.main(ActivityThread.java:6523)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)

事实上,我的最后一列名为 notify_datetimeDATETIME 而不是 notify_datetime

Wrong name last column

它似乎用 notify_datetime + "DATETIME"连接两个字符串

我可以做什么来解决这个问题?

最佳答案

如果您尚未发布该应用程序,您只需更改列的名称,然后进行全新安装。但是,一旦发布应用程序,您将必须增加数据库版本号,然后覆盖 onUpgrade。在该方法中,您可以检查旧版本是否低于最新版本并相应地修改表,如果数据可能丢失,则只需删除并重新创建表,如果不能丢失,请使用 alter table 语句。

例如

private static final int DATABASE_VERSION = 2;
private static final int DATABASE_NAME = "myDb.sqlite";

private DatabaseOpenHelper(Context context, SharedPrefsContract.Manager sharedPrefsManager) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion < 2) {
        // There is where you alter the table
    }
}

更改表格

首先重命名旧表:

ALTER TABLE orig_table_name RENAME TO tmp_table_name;

然后根据旧表但使用更新的列名称创建新表:

CREATE TABLE orig_table_name (
  col_a INT
, col_b INT
);

然后复制原始表格中的内容。

INSERT INTO orig_table_name(col_a, col_b)
SELECT col_a, colb
FROM tmp_table_name;

最后,删除旧表。

DROP TABLE tmp_table_name;

将所有这些包装在 BEGIN TRANSACTION;COMMIT; 中也可能是一个好主意。

关于java - SQLite DB创建表时名称列错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52047213/

相关文章:

java - 如何在 Vaadin 中向选项卡添加内容?

java - 我应该在 Web 应用程序的 Java bean 中添加对 PropertyChangeSupport 和 PropertyChangeListener 的支持吗?

android - 为 Presenter 的私有(private)方法编写单元测试

Android- 尝试在空对象引用上调用虚拟方法 'double java.lang.Double.doubleValue()'

python - 连接到 Flask 中的数据库,哪种方法更好?

c++ - SQLite:通过 100K 元素列表进行选择

java - 如何在 JFrame 内的 JPanel 中正确重画

java - "Match the Shoes"挑战我的错误在哪里

java - 什么可能阻止显示 HomeAsUp 图标

python - 我的 flask sqlite 数据库中的图像应该使用什么数据类型?