当我想向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);
}
}
答案 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开销,如果没有严格要求,则应避免使用。通常不需要。
根据评论:-
如果args.put(ID_MOVIE,movieFavorite.getId());没有添加,那么数据将被复制
这是一个例子:-
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";
}
}
}
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