title: Android: SQLite one-to-many design
body: <p>Anyone has good advise on how to implement one-to-many mapping for <code>SQLite</code> using <code>ContentProvider</code>? If you look at <code>Uri ContentProvider#insert(Uri, ContentValues)</code> you can see that it has <code>ContentValues</code> param that contains data to insert. The problem is that in its current implementation <code>ContentValues</code> does not support <code>put(String, Object)</code> method and class is final so I cannot extend it. Why it is a problem? Here comes my design:</p>
  
  <p>I have 2 tables which are in one-to-many relationship. To represent these in code I have 2 model objects. 1st represents the main record and has a field that is a list of 2nd object instances. Now I have a helper method in the model object #1 which returns <code>ContentValues</code> generated off the current object. It's trivial to populate a primitive fields with <code>ContentValues#put</code> overloaded methods but I'm out of luck for the list. So currently since my 2nd table row is just a single String value I generate a comma delimited String which then I reparse to String[] inside <code>ContentProvider#insert</code>. That feels yucky, so maybe someone can hint how it can be done in cleaner fashion.</p>
  
  <p>Here's some code. First from the model class:</p>
  
  <pre><code>public ContentValues toContentValues() {
      ContentValues values = new ContentValues();
      values.put(ITEM_ID, itemId);
      values.put(NAME, name);
      values.put(TYPES, concat(types));
      return values;
  }
  
  private String concat(String[] values) { /* trivial */}
  </code></pre>
  
  <p>and here's slimmed down version of <code>ContentProvider#insert</code> method</p>
  
  <pre><code>public Uri insert(Uri uri, ContentValues values) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      db.beginTransaction();
      try {
          // populate types
          String[] types = ((String)values.get(Offer.TYPES)).split("|");
          // we no longer need it
          values.remove(Offer.TYPES);
          // first insert row into OFFERS
          final long rowId = db.insert("offers", Offer.NAME, values);
          if (rowId &gt; 0 &amp;&amp; types != null) {
              // now insert all types for the row
              for (String t : types) {
                  ContentValues type = new ContentValues(8);
                  type.put(Offer.OFFER_ID, rowId);
                  type.put(Offer.TYPE, t);
                  // insert values into second table
                  db.insert("types", Offer.TYPE, type);
              }
          }
          db.setTransactionSuccessful();
          return ContentUris.withAppendedId(Offer.CONTENT_URI, rowId);
      } catch (Exception e) {
          Log.e(TAG, "Failed to insert record", e);
      } finally {
          db.endTransaction();
      }
  
  }
  </code></pre>