android.database.sqlite.SQLiteConstraintException:唯一约束失败

时间:2019-09-28 04:40:25

标签: android sqlite

当我想向sqlite添加数据时,会弹出此错误。以前在SQLite中已经有一个数据。您能为我的问题提供解决方案吗? android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed

public long insertMovie(MovieFav movieFavorite) {
        ContentValues args = new ContentValues();
        args.put(ID_MOVIE, movieFavorite.getId());
        args.put(TITLE, movieFavorite.getTitle());
        args.put(OVERVIEW, movieFavorite.getOverview());
        args.put(RELEASE_DATE, movieFavorite.getRelease_date());
        args.put(VOTE_AVERAGE, movieFavorite.getVote_average());
        args.put(POSTER_PATH, movieFavorite.getPoster_path());
        return sqLiteDatabase.insert(DATABASE_TABLE, null, args);
    }


public class DbHelper  extends SQLiteOpenHelper {

    private static  final String DATABASE_NAME = "movielist";
    private static final int DATABASE_VERSION = 1;
    private static final String SQL_CREATE_TABLE_MOVIE_FAV = String.format("CREATE TABLE %s"
            + " (%s INTEGER PRIMARY KEY AUTOINCREMENT," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL)",

            TABLE_MOVIE,
            DbContract.MovieListFavorite.MOVIE_ID,
            DbContract.MovieListFavorite.MOVIE_TITLE,
            DbContract.MovieListFavorite.MOVIE_OVERVIEW,
            DbContract.MovieListFavorite.MOVIE_RELEASE_DATE,
            DbContract.MovieListFavorite.MOVIE_PHOTO,
            DbContract.MovieListFavorite.MOVIE_VOTE_AVERAGE
    );




    DbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_TABLE_MOVIE_FAV);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_MOVIE);

        onCreate(db);
    }
}

2 个答案:

答案 0 :(得分:1)

设置ID自动递增时为什么要插入ID
从插入查询中删除<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <form method="get"> <input type="text" name="name" id="name" hidden> language <input type="checkbox" name="language" class="language" id="1" value="1"> cast <input type="checkbox" name="cast" class="cast" id="2" value="1"> language <input type="checkbox" name="language" class="language" id="3" value="2"> cast <input type="checkbox" name="cast" class="cast" id="4" value="3"> <input type="submit" value="submit"> </form>,如下所示

args.put(ID_MOVIE, movieFavorite.getId());

答案 1 :(得分:0)

您可能不想有这行:-

args.put(ID_MOVIE, movieFavorite.getId());

insertMovie 方法中。

如果没有这一行,将自动生成 id (依次为1、2、3、3)。

如果确实包含上述​​行,则将尝试插入 BUT (如果该列的其他行中已使用该值),则将出现UNIQUE约束冲突。也就是说,主键必须是UNIQUE,因此,即使不对UNIQUE进行编码,也就好像对UNIQUE进行了编码(即隐含UNIQUE约束)。

注意;您很可能不需要 AUTOINCREMENT + " (%s INTEGER PRIMARY KEY," +仍会自动生成 id ,但效率更高。按照:-

  

AUTOINCREMENT关键字强加了额外的CPU,内存,磁盘空间和磁盘I / O开销,如果没有严格要求,则应避免使用。通常不需要。

SQLite Autoincrement

根据评论:-

  

如果args.put(ID_MOVIE,movieFavorite.getId());没有添加,那么数据将被复制

这是一个例子:-

DbHelper.java

public class DbHelper extends SQLiteOpenHelper {

    private static  final String DATABASE_NAME = "movielist";
    private static final int DATABASE_VERSION = 1;
    private static final String DATABASE_TABLE = DbContract.TABLE_MOVIE;
    private static final String SQL_CREATE_TABLE_MOVIE_FAV = String.format("CREATE TABLE %s"
            + " (%s INTEGER PRIMARY KEY AUTOINCREMENT," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL," +
            " %s TEXT NOT NULL)",
            DATABASE_TABLE,
            DbContract.MovieListFavorite.MOVIE_ID,
            DbContract.MovieListFavorite.MOVIE_TITLE,
            DbContract.MovieListFavorite.MOVIE_OVERVIEW,
            DbContract.MovieListFavorite.MOVIE_RELEASE_DATE,
            DbContract.MovieListFavorite.MOVIE_PHOTO,
            DbContract.MovieListFavorite.MOVIE_VOTE_AVERAGE
    );

    SQLiteDatabase sqLiteDatabase;

    public DbHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        sqLiteDatabase = this.getWritableDatabase();
    }

    public long insertMovie(MovieFav movieFavorite) {
        ContentValues args = new ContentValues();
        args.put(DbContract.MovieListFavorite.MOVIE_TITLE, movieFavorite.getTitle());
        //<<<<<<<<<< ID REMOVED >>>>>>>>>>
        args.put(DbContract.MovieListFavorite.MOVIE_OVERVIEW, movieFavorite.getOverview());
        args.put(DbContract.MovieListFavorite.MOVIE_RELEASE_DATE, movieFavorite.getRelease_date());
        args.put(DbContract.MovieListFavorite.MOVIE_VOTE_AVERAGE, movieFavorite.getVote_average());
        args.put(DbContract.MovieListFavorite.MOVIE_PHOTO, movieFavorite.getPoster_path());
        return sqLiteDatabase.insert(DATABASE_TABLE, null, args);
    }

    //<<<<<<<<<< ADDED FOR THE DEMO TO LIST MOVIES WITH THE ID >>>>>>>>>>
    public void logAllMovies() {
        Cursor csr = sqLiteDatabase.query(DATABASE_TABLE,null,null,null,null,null,null);
        while (csr.moveToNext()) {
            Log.d("MOVIELIST",
                    "Title is " + csr.getString(csr.getColumnIndex(DbContract.MovieListFavorite.MOVIE_TITLE)) +
                    "\n\tID is " + csr.getString(csr.getColumnIndex(DbContract.MovieListFavorite.MOVIE_ID)) +
                    "\n\tOverview is \n\t\t" + csr.getString(csr.getColumnIndex(DbContract.MovieListFavorite.MOVIE_OVERVIEW))
                    /* and so on */
            );
        }
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_TABLE_MOVIE_FAV);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    //<<<<<<<<<< ADDED to complete (may be different) >>>>>>>>>>
    private class DbContract {

        private static final String TABLE_MOVIE = "movie";

        private class  MovieListFavorite {
            private static final String MOVIE_ID = BaseColumns._ID;
            private static final String MOVIE_TITLE = "title";
            private static final String MOVIE_OVERVIEW = "overview";
            private static final String MOVIE_RELEASE_DATE = "release_date";
            private static final String MOVIE_PHOTO = "photo";
            private static final String MOVIE_VOTE_AVERAGE = "vote_average";
        }
    }
}

MovieFav.java

public class MovieFav {

    private long id;
    private String Title;
    private String Overview;
    private String Release_date;
    private String Vote_average;
    private String Poster_path;

    public MovieFav() {
    }

    public MovieFav(String title, String overview, String release_date, String vote_average, String poster_path) {
        this.Title = title;
        this.Overview = overview;
        this.Release_date = release_date;
        this.Vote_average = vote_average;
        this.Poster_path = poster_path;
    }

    public long getId() {
        return id;
    }

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

    public String getTitle() {
        return Title;
    }

    public void setTitle(String title) {
        Title = title;
    }

    public String getOverview() {
        return Overview;
    }

    public void setOverview(String overview) {
        Overview = overview;
    }

    public String getRelease_date() {
        return Release_date;
    }

    public void setRelease_date(String release_date) {
        Release_date = release_date;
    }

    public String getVote_average() {
        return Vote_average;
    }

    public void setVote_average(String vote_average) {
        Vote_average = vote_average;
    }

    public String getPoster_path() {
        return Poster_path;
    }

    public void setPoster_path(String poster_path) {
        Poster_path = poster_path;
    }
}

活动中的代码

public class MainActivity extends AppCompatActivity {

    DbHelper mMyDBHlpr;


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

        mMyDBHlpr = new DbHelper(this); //Instantiate Database Helper

        // Add some Movies WITHOUT specifying ID
        mMyDBHlpr.insertMovie(new MovieFav("Gone with the wind","It blows","2019-01-01","medium","//posters/movies/gwtw"));
        mMyDBHlpr.insertMovie(new MovieFav("The Magnificent Seven","Magnificent","2019-10-01","medium","//posters/movies/tms"));
        mMyDBHlpr.insertMovie(new MovieFav("The Lord of the Rings","I'll ring you about this one","2015-12-01","great","//posters/movies/lotrp1"));
        mMyDBHlpr.insertMovie(new MovieFav("War of the Worlds","Alien to me","2010-01-01","ok","//posters/movies/wotw"));
        // Output Movies information to the log INCLUDING ID
        mMyDBHlpr.logAllMovies();

    }

日志结果:-

2019-09-28 19:51:52.242 7088-7088/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is Gone with the wind
        ID is 1
        Overview is 
            It blows
2019-09-28 19:51:52.243 7088-7088/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Magnificent Seven
        ID is 2
        Overview is 
            Magnificent
2019-09-28 19:51:52.243 7088-7088/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Lord of the Rings
        ID is 3
        Overview is 
            I'll ring you about this one
2019-09-28 19:51:52.243 7088-7088/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is War of the Worlds
        ID is 4
        Overview is 
            Alien to me

由于上述总是尝试添加4行,如果第二次运行,则:-

2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is Gone with the wind
        ID is 1
        Overview is 
            It blows
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Magnificent Seven
        ID is 2
        Overview is 
            Magnificent
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Lord of the Rings
        ID is 3
        Overview is 
            I'll ring you about this one
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is War of the Worlds
        ID is 4
        Overview is 
            Alien to me
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is Gone with the wind
        ID is 5
        Overview is 
            It blows
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Magnificent Seven
        ID is 6
        Overview is 
            Magnificent
2019-09-28 20:00:52.454 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is The Lord of the Rings
        ID is 7
        Overview is 
            I'll ring you about this one
2019-09-28 20:00:52.455 7188-7188/aso.asoinmemorysbtofiledb D/MOVIELIST: Title is War of the Worlds
        ID is 8
        Overview is 
            Alien to me

即相同的行已添加,但带有其他ID(这次为5-8)

没有重复的电影

如果您想要的是不复制数据(例如整个电影),这样就不会添加5-8,那么您可以创建一个适合于不想复制的数据的UNIQUE约束。从电影信息的角度来看, ID 将永远不会重复,就数据而言,它不会重复也没有用(从数据库的角度来看,ID非常有用)。< / p>

假设如果已经存在具有相同标题和相同发行日期的电影,则假设您永远不希望添加电影,那么可以将电影标题和电影的发行日期组合为唯一。

例如对于上述内容,则用于创建表的代码可能是(请参见注释):-

private static  final String DATABASE_NAME = "movielist";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_TABLE = DbContract.TABLE_MOVIE;
private static final String SQL_CREATE_TABLE_MOVIE_FAV = String.format("CREATE TABLE %s"
        + " (%s INTEGER PRIMARY KEY AUTOINCREMENT," +
        " %s TEXT NOT NULL," +
        " %s TEXT NOT NULL," +
        " %s TEXT NOT NULL," +
        " %s TEXT NOT NULL," +
        " %s TEXT NOT NULL" + //<<<<<<<<<< REMOVED CLOSING PARENTHESIS
        /* ADDED the UNQIUE CONSTRAINT ON THE Title and release date */
                ", UNIQUE ("
                + DbContract.MovieListFavorite.MOVIE_TITLE +
                "," + DbContract.MovieListFavorite.MOVIE_RELEASE_DATE
                + ")" + // END Of THE UNIQUE CONSTRAINT
                ")", //<<<<<<<<<< END OF CHANGES FOR ADDING UNIQUE constraint
        /**
         * NOTE AS THE SCHEMA HAS CHANGED THE DATABASE NEEDS TO BE DELETED (altering is possible but harder)
         * THEREFORE TO INTRODUCE CHANGES >>>>>>>>>>UNINSTALL THE APP AND THEN RERUN<<<<<<<<<<.
         */
        DATABASE_TABLE,
        DbContract.MovieListFavorite.MOVIE_ID,
        DbContract.MovieListFavorite.MOVIE_TITLE,
        DbContract.MovieListFavorite.MOVIE_OVERVIEW,
        DbContract.MovieListFavorite.MOVIE_RELEASE_DATE,
        DbContract.MovieListFavorite.MOVIE_PHOTO,
        DbContract.MovieListFavorite.MOVIE_VOTE_AVERAGE
);

错误(不是这样)

关于该消息,这是一个陷阱异常,它不会导致失败,并且不会添加所报告的行。

如果您想绕过日志中的报告,则必须使用另一种方法,该方法需要生成使用INSERT OR IGNORE INTO the_table (a_comma_separated_list_of_the_columns_for_which_data_is_supplied) VALUES(a_comma_separated_list_of_values)的插入SQL(组成句子的值描述了整个值,它不是一个会使用的特殊值。

简而言之,插入方法可以是:-

public long insertMovieAlternative(MovieFav movieFavorite) {
    String insertSQL = "INSERT OR IGNORE INTO " + DATABASE_TABLE +
            "(" +
            DbContract.MovieListFavorite.MOVIE_TITLE + "," +
            DbContract.MovieListFavorite.MOVIE_OVERVIEW + "," +
            DbContract.MovieListFavorite.MOVIE_RELEASE_DATE + "," +
            DbContract.MovieListFavorite.MOVIE_VOTE_AVERAGE + "," +
            DbContract.MovieListFavorite.MOVIE_PHOTO +
            ") VALUES (?,?,?,?,?)";
    SQLiteStatement sqlstmnt = sqLiteDatabase.compileStatement(insertSQL);
    sqlstmnt.bindAllArgsAsStrings(new String[]{
            movieFavorite.getTitle(),
            movieFavorite.getOverview(),
            movieFavorite.getRelease_date(),
            movieFavorite.getVote_average(),
            movieFavorite.getPoster_path()
    });
    Log.d("ALTINSERTSQL",sqlstmnt.toString());
    return sqlstmnt.executeInsert();
}
  • 请注意,不需要行Log.d("ALTINSERTSQL",sqlstmnt.toString());,它被添加来显示SQL生成的内容,例如传递给SQLite的

  • INSERT OR IGNORE INTO movie(title,overview,release_date,vote_average,photo) VALUES (?,?,?,?,?)

  • ?受SQLite API绑定(与使用String.format相似),因此使用 bindAllArgsAsStrings 允许SQlite替换每个?具有适当的值。这样可以防止SQLInjection。

考虑以上内容并使用:-

    mMyDBHlpr = new DbHelper(this); //Instantiate Database Helper

    // Add some Movies WITHOUT specifying ID
    mMyDBHlpr.insertMovie(new MovieFav("Gone with the wind","It blows","2019-01-01","medium","//posters/movies/gwtw"));
    mMyDBHlpr.insertMovie(new MovieFav("The Magnificent Seven","Magnificent","2019-10-01","medium","//posters/movies/tms"));
    mMyDBHlpr.insertMovie(new MovieFav("The Lord of the Rings","I'll ring you about this one","2015-12-01","great","//posters/movies/lotrp1"));
    mMyDBHlpr.insertMovie(new MovieFav("War of the Worlds","Alien to me","2010-01-01","ok","//posters/movies/wotw"));

    mMyDBHlpr.insertMovieAlternative(new MovieFav("Gone with the wind","It blows","2019-01-01","medium","//posters/movies/gwtw"));
    mMyDBHlpr.insertMovieAlternative(new MovieFav("The Magnificent Seven","Magnificent","2019-10-01","medium","//posters/movies/tms"));
    mMyDBHlpr.insertMovieAlternative(new MovieFav("The Lord of the Rings","I'll ring you about this one","2015-12-01","great","//posters/movies/lotrp1"));
    mMyDBHlpr.insertMovieAlternative(new MovieFav("War of the Worlds","Alien to me","2010-01-01","ok","//posters/movies/wotw"));

    // Output Movies information to the log INCLUDING ID
    mMyDBHlpr.logAllMovies();

这将尝试添加第二组相同的电影,但是第二次赢得; t因为为电影标题Moview发布日期组合添加了唯一性约束,但日志不包含异常消息(没有异常,因为INSERT或IGNORE在发生冲突时充当noop(不执行任何操作)。

日志将改为:-

2019-09-28 20:52:06.304 D/ALTINSERTSQL: SQLiteProgram: INSERT OR IGNORE INTO movie(title,overview,release_date,vote_average,photo) VALUES (?,?,?,?,?)
2019-09-28 20:52:06.305 I/chatty: uid=10419(aso.asoinmemorysbtofiledb) identical 2 lines
2019-09-28 20:52:06.306 D/ALTINSERTSQL: SQLiteProgram: INSERT OR IGNORE INTO movie(title,overview,release_date,vote_average,photo) VALUES (?,?,?,?,?)
2019-09-28 20:52:06.307 D/MOVIELIST: Title is Gone with the wind
        ID is 1
        Overview is 
            It blows
2019-09-28 20:52:06.307 D/MOVIELIST: Title is The Magnificent Seven
        ID is 2
        Overview is 
            Magnificent
2019-09-28 20:52:06.307 D/MOVIELIST: Title is The Lord of the Rings
        ID is 3
        Overview is 
            I'll ring you about this one
2019-09-28 20:52:06.307 D/MOVIELIST: Title is War of the Worlds
        ID is 4
        Overview is 
            Alien to me