How to add insert and update operations to a m:n relationship in Room

I have to do some andoid development (in Java) for school and that’s why I play around with Room.

My goal is to provide a very simple user interface that allows users to add participants to events. An event has exactly one owner (who is a person) and several participants (who are also person’s). Each person can take part in several events.

I have to use LiveData and ViewModel.

Entities:

@Entity(tableName = "events") public class Event {    public Event(Person owner, String name, Instant date) {     this.name = name;     this.owner = owner;     this.date = date;   }    @PrimaryKey(autoGenerate = true)   @ColumnInfo(name = "event_id")   private long id;   private Instant date;   private String name;    @Embedded(prefix = "owner_")   private final Person owner;    // Getters and setters ...  
 @Entity(tableName = "persons") public class Person {    @PrimaryKey(autoGenerate = true)   @ColumnInfo(name = "person_id")   private long id;   private String name;    // Getters and setters ...  

For the m:n relationship mapping i use this entity:

 @Entity(primaryKeys = {"event_id", "person_id"}) public class EventToPerson {    @ColumnInfo(name = "event_id")   public long eventId;    @ColumnInfo(name = "person_id")   public long personId; }  

And this class to access it:

   public class EventWithPersons {    @Embedded   public Event event;    @Relation(       parentColumn = "event_id",       entityColumn = "person_id",       associateBy = @Junction(EventToPerson.class)   )   public List<Person> participants;    public EventWithPersons() {   }    public EventWithPersons(Event event, Person participant) {     this.event = event;     this.participants = new ArrayList<>();     this.participants.add(participant);   } }  

The DAO:

@Dao public interface EventDao {    @Insert()   void insert(Event event);    @Query("DELETE FROM events")   void deleteAll();    @Delete   void deleteDbEvent(Event event);    @Update   public void updateDbEvent(Event event);    @Query("SELECT * from events")   LiveData<List<Event>> getAllEvents();    @Transaction   @Query("SELECT * FROM events")   LiveData<List<EventWithPersons>> getEventsWithPersons();    @Transaction   @Update   public void updateEventToPerson(EventToPerson eventToPerson);    @Transaction   @Delete   public void deleteEventToPerson(EventToPerson eventToPerson);    @Transaction   @Insert   void insert(EventToPerson eventToPerson); }  

The Repository:

   public class EventRepo {    private EventDao mEventDao;   private LiveData<List<EventWithPersons>> allEvents;    public EventRepo(Application application) {     AppDatabase db = AppDatabase.getDatabase(application);     mEventDao = db.eventDao();     allEvents = mEventDao.getEventsWithPersons();   }    public LiveData<List<EventWithPersons>> getAllEvents() {     return allEvents;   }    public void insert(Event event) {     AppDatabase.databaseWriteExecutor.execute(() -> mEventDao.insert(event));   }    public void insert(EventWithPersons event) {      event.participants.forEach(person -> {        EventToPerson eventToPerson = new EventToPerson();       eventToPerson.eventId = event.event.getId();       eventToPerson.personId = person.getId();       AppDatabase.databaseWriteExecutor.execute(() -> mEventDao.insert(eventToPerson));      });   } }  

The ViewModel:

   public class EventViewModel extends AndroidViewModel {    private EventRepo mRepository;    private LiveData<List<EventWithPersons>> mAllEvents;    public EventViewModel(Application application) {     super(application);     mRepository = new EventRepo(application);     mAllEvents = mRepository.getAllEvents();   }    public LiveData<List<EventWithPersons>> getAllEvents() {     return mAllEvents;   }    public void insert(Event event) {     mRepository.insert(event);   }    public void insert(EventWithPersons event) {     mRepository.insert(event);   }    public void update(EventWithPersons eventWithPersons) {     mRepository.insert(eventWithPersons);   } }  

And finally my MainActicity which provides some basic view elements for testing:

 public class MainActivity extends AppCompatActivity {    private EventViewModel eventViewModel;   TextView textView;   Button button;   EditText editText;    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      textView = findViewById(R.id.show_participants_text_view);     button = findViewById(R.id.button);     editText = findViewById(R.id.editText);      Person owner = new Person("OWNER");     Person participant1 = new Person("participant1");     Event event = new Event(owner, "Party", Instant.now());      button.setOnClickListener(v -> {       Log.d(this.getLocalClassName(), "Button pressed.");       Person p = new Person(editText.getText().toString());       eventViewModel.update(new EventWithPersons(event, p));     });      eventViewModel = new ViewModelProvider(this).get(EventViewModel.class);      eventViewModel.getAllEvents().observe(this, events -> {       setParticipants(events);       Log.d(this.getLocalClassName(), "number of events: " + eventViewModel.getAllEvents().getValue().size());     });      eventViewModel.insert(new EventWithPersons(event, participant1));    }    private void setParticipants(List<EventWithPersons> events) {     Log.d(this.getLocalClassName(), "Set events.");      String participants = events.get(0).participants.stream().map(Person::getName).collect(Collectors.joining(", "));     textView.setText(participants);   } }  

With this classes i alway run into this erros:

android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: EventToPerson.event_id, EventToPerson.person_id (code 1555) 

I think something is wrong with the DAO but I can’t find out. Can you help me set this up properly?

I used this tutorial: https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

You can find the whole project here:
https://github.com/jriegraf/room-test

Add Comment
0 Answer(s)

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.