androidmysqlormgreendao

android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed using greenDAO ORM


I am using GreenDAO for a local Android project. I am creating the login and register of the users (they work fine) the problem happens after one user is created. Registering more than one user throws this exception:

    E/SQLiteLog: (1555) abort at 21 in [INSERT INTO "USER" ("_id","EMAIL","FIRST_NAME","LAST_NAME","LAND","PASSWORD","IMAGE_REFERENCE","ABOUT","ROLE_ID") VALUES (?,?,?,?,?,?,?,?,?)]: UNIQUE constraint failed: USER._id
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.databaseproject, PID: 7436
    android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: USER._id (code 1555)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at org.greenrobot.greendao.AbstractDao.insertInsideTx(AbstractDao.java:368)
        at org.greenrobot.greendao.AbstractDao.executeInsert(AbstractDao.java:351)
        at org.greenrobot.greendao.AbstractDao.insert(AbstractDao.java:319)
        at com.example.databaseproject.activity.MainActivity.register(MainActivity.java:107)
        at com.example.databaseproject.activity.MainActivity.access$500(MainActivity.java:23)
        at com.example.databaseproject.activity.MainActivity$1.onClick(MainActivity.java:69)
        at android.view.View.performClick(View.java:4756)
        at android.view.View$PerformClick.run(View.java:19749)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Although I have set the user id to @Id(autoIncrement = true) it seems as if the new Users are getting the same id. Also the INSERT INTO sql shows (?,?, ...) for all property values in the console. What am I doing wrong here? This is my User.java

package com.example.databaseproject.entity;

import androidx.annotation.Nullable;

import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.JoinEntity;
import org.greenrobot.greendao.annotation.NotNull;
import org.greenrobot.greendao.annotation.ToMany;
import org.greenrobot.greendao.annotation.Unique;

import java.util.List;

@Entity
public class User {

    @Id(autoincrement = true)
    private long id;

    @NotNull
    @Unique
    private String email;
    @Nullable
    private String first_name;
    @Nullable
    private String last_name;
    @Nullable
    private String land;
    @NotNull
    private String password;
    @Nullable
    private String image_reference;
    @Nullable
    private String about;
    @NotNull
    private long role_id;

    @ToMany
    @JoinEntity(
            entity = UserMusicLiked.class,
            sourceProperty = "user_id",
            targetProperty = "music_id"
    )
    private List<Music> musicsLikedByThisUser;

    @ToMany
    @JoinEntity(
            entity = UserPlaylistFollow.class,
            sourceProperty = "user_id",
            targetProperty = "playlist_id"
    )
    private List<Playlist> playlistsFollowedByThisUser;

    /**
     * Used to resolve relations
     */
    @Generated(hash = 2040040024)
    private transient DaoSession daoSession;

    /**
     * Used for active entity operations.
     */
    @Generated(hash = 1507654846)
    private transient UserDao myDao;

    @Generated(hash = 1603819663)
    public User(long id, @NotNull String email, String first_name, String last_name, String land,
                @NotNull String password, String image_reference, String about, long role_id) {
        this.id = id;
        this.email = email;
        this.first_name = first_name;
        this.last_name = last_name;
        this.land = land;
        this.password = password;
        this.image_reference = image_reference;
        this.about = about;
        this.role_id = role_id;
    }

    @Generated(hash = 586692638)
    public User() {
    }

    public long getId() {
        return this.id;
    }

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

    public String getEmail() {
        return this.email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFirst_name() {
        return this.first_name;
    }

    public void setFirst_name(String first_name) {
        this.first_name = first_name;
    }

    public String getLast_name() {
        return this.last_name;
    }

    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }

    public String getLand() {
        return this.land;
    }

    public void setLand(String land) {
        this.land = land;
    }

    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getImage_reference() {
        return this.image_reference;
    }

    public void setImage_reference(String image_reference) {
        this.image_reference = image_reference;
    }

    public String getAbout() {
        return this.about;
    }

    public void setAbout(String about) {
        this.about = about;
    }

    public long getRole_id() {
        return this.role_id;
    }

    public void setRole_id(long role_id) {
        this.role_id = role_id;
    }

    /**
     * To-many relationship, resolved on first access (and after reset).
     * Changes to to-many relations are not persisted, make changes to the target entity.
     */
    @Generated(hash = 1624161576)
    public List<Music> getMusicsLikedByThisUser() {
        if (musicsLikedByThisUser == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            MusicDao targetDao = daoSession.getMusicDao();
            List<Music> musicsLikedByThisUserNew = targetDao._queryUser_MusicsLikedByThisUser(id);
            synchronized (this) {
                if (musicsLikedByThisUser == null) {
                    musicsLikedByThisUser = musicsLikedByThisUserNew;
                }
            }
        }
        return musicsLikedByThisUser;
    }

    /**
     * Resets a to-many relationship, making the next get call to query for a fresh result.
     */
    @Generated(hash = 1901439151)
    public synchronized void resetMusicsLikedByThisUser() {
        musicsLikedByThisUser = null;
    }

    /**
     * To-many relationship, resolved on first access (and after reset).
     * Changes to to-many relations are not persisted, make changes to the target entity.
     */
    @Generated(hash = 952184019)
    public List<Playlist> getPlaylistsFollowedByThisUser() {
        if (playlistsFollowedByThisUser == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            PlaylistDao targetDao = daoSession.getPlaylistDao();
            List<Playlist> playlistsFollowedByThisUserNew = targetDao
                    ._queryUser_PlaylistsFollowedByThisUser(id);
            synchronized (this) {
                if (playlistsFollowedByThisUser == null) {
                    playlistsFollowedByThisUser = playlistsFollowedByThisUserNew;
                }
            }
        }
        return playlistsFollowedByThisUser;
    }

    /**
     * Resets a to-many relationship, making the next get call to query for a fresh result.
     */
    @Generated(hash = 1490831360)
    public synchronized void resetPlaylistsFollowedByThisUser() {
        playlistsFollowedByThisUser = null;
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 128553479)
    public void delete() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.delete(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 1942392019)
    public void refresh() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.refresh(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 713229351)
    public void update() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.update(this);
    }

    /** called by internal mechanisms, do not call yourself. */
    @Generated(hash = 2059241980)
    public void __setDaoSession(DaoSession daoSession) {
        this.daoSession = daoSession;
        myDao = daoSession != null ? daoSession.getUserDao() : null;
    }
}

This is my MainActivity.java

package com.example.databaseproject.activity;

import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.databaseproject.CurrentUser;
import com.example.databaseproject.R;
import com.example.databaseproject.entity.DaoMaster;
import com.example.databaseproject.entity.DaoSession;
import com.example.databaseproject.entity.User;
import com.example.databaseproject.entity.UserDao;

import java.util.List;

public class MainActivity extends AppCompatActivity {


    private boolean isLogin = true;

    private EditText emailEditText;
    private EditText passwordEditText;
    private EditText firstNameEditText;
    private EditText lastNameEditText;
    private EditText landNameEditText;
    private Spinner roleSpinner;

    private TextView changeLoginRegisterText;
    private Button submitButton;

    private DaoSession daoSession;
    private UserDao userDao;

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


// do this once, for example in your Application class
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "myDatabase", null);
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
        userDao = daoSession.getUserDao();
// do this in your activities/fragments to get hold of a DAO
//        MusicDao musicEntity = daoSession.getMusicDao();

        emailEditText = findViewById(R.id.edit_text_username);
        passwordEditText = findViewById(R.id.edit_text_password);
        submitButton = findViewById(R.id.button_submit);
        changeLoginRegisterText = findViewById(R.id.text_view_register);

        submitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (isLogin) {
                    if (isFieldsValid())
                        login(emailEditText.getText().toString(), passwordEditText.getText().toString());
                } else {
                    if (isFieldsValid())
                        register(emailEditText.getText().toString(), passwordEditText.getText().toString());
                }
            }
        });

        changeLoginRegisterText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                isLogin = !isLogin;
                updateUI();
            }
        });
    }

    private void updateUI() {
        if (isLogin) {
            changeLoginRegisterText.setText("Not a user? Create a new Account");
            submitButton.setText("Login");
        } else {
            changeLoginRegisterText.setText("Already a User? Login");
            submitButton.setText("Register");
        }
    }

    private void register(String email, String password) {

        //Check if user exists

        List<User> user = userDao.queryBuilder().where(UserDao.Properties.Email.eq(email))
                .limit(1)
                .list();
        if (user != null && user.size() > 0) {
            print(user.get(0).getEmail() + " Already Registered");
        } else {
            User newUser = new User();
            newUser.setEmail(email);
            newUser.setPassword(password);
            newUser.setRole_id(0);
            userDao.insert(newUser);
            print(newUser.getEmail() + " Registered successfully");

            setCurrentUser(newUser);
        }
    }

    private void login(String email, String password) {
//        UserDao userDao = daoSession.getUserDao();
        List<User> user = userDao.queryBuilder().where(UserDao.Properties.Email.eq(email))
                .where(UserDao.Properties.Password.eq(password))
                .limit(1)
                .list();
        if (user != null && user.size() > 0) {
            print(user.get(0).getEmail() + " Logged in");
            setCurrentUser(user.get(0));
        } else {
            print("Invalid Username or Password");
        }
    }

    private void setCurrentUser(User user) {
        CurrentUser.user = user;

    }

    private void print(String message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    private boolean isFieldsValid() {
        return emailEditText != null && !emailEditText.getText().toString().equals("")
                && passwordEditText != null && !passwordEditText.getText().toString().equals("");
    }
}

There are other entities as well, but I don't think they are related to the error. Tell me if you need more info.


Solution

  • I changed the id type of long to Long and regenerated the greenDAO code and it works now. Looks like auto increment doesn't work well with the type long. (Although in the documents it says both types are supported)