Index: bp-core/bp-core-component.php
--- bp-core/bp-core-component.php
+++ bp-core/bp-core-component.php
@@ -107,6 +107,24 @@
 	 */
 	public $root_slug = '';
 
+	/**
+	 * Metadata tables for the component (if applicable)
+	 *
+	 * @since BuddyPress (2.0.0)
+	 *
+	 * @var string
+	 */
+	public $meta_tables = array();
+
+	/**
+	 * Global tables for the component (if applicable)
+	 *
+	 * @since BuddyPress (2.0.0)
+	 *
+	 * @var string
+	 */
+	public $global_tables = array();
+
 	/** Methods ***************************************************************/
 
 	/**
@@ -197,7 +215,8 @@
 			'has_directory'         => false,
 			'notification_callback' => '',
 			'search_string'         => '',
-			'global_tables'         => ''
+			'global_tables'         => '',
+			'meta_tables'           => ''
 		) );
 
 		// Slug used for permalink URI chunk after root
@@ -215,16 +234,14 @@
 		// Notifications callback
 		$this->notification_callback = apply_filters( 'bp_' . $this->id . '_notification_callback', $r['notification_callback'] );
 
-		// Set up global table names
-		if ( !empty( $r['global_tables'] ) ) {
+		// Set the global table names, if applicable
+		if ( ! empty( $r['global_tables'] ) ) {
+			$this->register_global_tables( $r['global_tables'] );
+		}
 
-			// This filter allows for component-specific filtering of table names
-			// To filter *all* tables, use the 'bp_core_get_table_prefix' filter instead
-			$r['global_tables'] = apply_filters( 'bp_' . $this->id . '_global_tables', $r['global_tables'] );
-
-			foreach ( $r['global_tables'] as $global_name => $table_name ) {
-				$this->$global_name = $table_name;
-			}
+		// Set the metadata table, if applicable
+		if ( ! empty( $r['meta_tables'] ) ) {
+			$this->register_meta_tables( $r['meta_tables'] );
 		}
 
 		/** BuddyPress ********************************************************/
@@ -444,6 +461,68 @@
 	}
 
 	/**
+	 * Use this to register new global tables for your component, so that it
+	 * may use WordPress's database API.
+	 *
+	 * @since BuddyPress (2.0.0)
+	 *
+	 * @param array $tables
+	 */
+	public function register_global_tables( $tables = array() ) {
+
+		// This filter allows for component-specific filtering of table names
+		// To filter *all* tables, use the 'bp_core_get_table_prefix' filter instead
+		$tables = apply_filters( 'bp_' . $this->id . '_global_tables', $tables );
+
+		/**
+		 * Add the name of each global table to WPDB to allow BuddyPress
+		 * components to play nicely with the WordPress metadata API.
+		 */
+		if ( !empty( $tables ) && is_array( $tables ) ) {
+			foreach( $tables as $global_name => $table_name ) {
+				$this->$global_name = $table_name;
+			}
+
+			// Keep a record of the metadata tables in the component
+			$this->global_tables = $tables;
+		}
+
+		do_action( 'bp_' . $this->id . '_register_global_tables' );
+	}
+
+	/**
+	 * Use this to register new metadata tables for your component, so that it
+	 * may use WordPress's core metadata API.
+	 *
+	 * @since BuddyPress (2.0.0)
+	 *
+	 * @global object $wpdb
+	 * @param array $tables
+	 */
+	public function register_meta_tables( $tables = array() ) {
+		global $wpdb;
+
+		// This filter allows for component-specific filtering of table names
+		// To filter *all* tables, use the 'bp_core_get_table_prefix' filter instead
+		$tables = apply_filters( 'bp_' . $this->id . '_meta_tables', $tables );
+
+		/**
+		 * Add the name of each metadata table to WPDB to allow BuddyPress
+		 * components to play nicely with the WordPress metadata API.
+		 */
+		if ( !empty( $tables ) && is_array( $tables ) ) {
+			foreach( $tables as $meta_prefix => $table_name ) {
+				$wpdb->{$meta_prefix . 'meta'} = $table_name;
+			}
+
+			// Keep a record of the metadata tables in the component
+			$this->meta_tables = $tables;
+		}
+
+		do_action( 'bp_' . $this->id . '_register_meta_tables' );
+	}
+
+	/**
 	 * Set up the component post types.
 	 *
 	 * @since BuddyPress (1.5.0)
Index: bp-core/bp-core-filters.php
--- bp-core/bp-core-filters.php
+++ bp-core/bp-core-filters.php
@@ -529,3 +529,23 @@
 	return $menu_item;
 }
 add_filter( 'wp_setup_nav_menu_item', 'bp_setup_nav_menu_item', 10, 1 );
+
+/**
+ * Filter SQL query strings to swap out the 'meta_id' column.
+ *
+ * WordPress uses the meta_id column for commentmeta and postmeta, and so
+ * hardcodes the column name into its *_metadata() functions. BuddyPress, on
+ * the other hand, uses 'id' for the primary column. To make WP's functions
+ * usable for BuddyPress, we use this just-in-time filter on 'query' to swap
+ * 'meta_id' with 'id.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @access private Do not use.
+ *
+ * @param string $q SQL query.
+ * @return string
+ */
+function bp_filter_metaid_column_name( $q ) {
+	return str_replace( 'meta_id', 'id', $q );
+}
Index: bp-groups/bp-groups-functions.php
--- bp-groups/bp-groups-functions.php
+++ bp-groups/bp-groups-functions.php
@@ -956,94 +956,56 @@
 
 /*** Group Meta ****************************************************/
 
-function groups_delete_groupmeta( $group_id, $meta_key = false, $meta_value = false ) {
-	global $wpdb, $bp;
+function groups_delete_groupmeta( $group_id, $meta_key = false, $meta_value = false, $delete_all = false ) {
 
-	if ( !is_numeric( $group_id ) )
+	// Legacy - return false if non-int group ID
+	if ( ! is_numeric( $group_id ) ) {
 		return false;
+	}
 
+	// Legacy - Sanitize keys
 	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
 
-	if ( is_array( $meta_value ) || is_object( $meta_value ) )
-		$meta_value = serialize($meta_value);
+	add_filter( 'query', 'bp_filter_metaid_column_name' );
+	$retval = delete_metadata( 'group', $group_id, $meta_key, $meta_value, $delete_all );
+	remove_filter( 'query', 'bp_filter_metaid_column_name' );
 
-	$meta_value = trim( $meta_value );
-
-	if ( !$meta_key )
-		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d", $group_id ) );
-	else if ( $meta_value )
-		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s AND meta_value = %s", $group_id, $meta_key, $meta_value ) );
-	else
-		$wpdb->query( $wpdb->prepare( "DELETE FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key ) );
-
-	// Delete the cached object
-	wp_cache_delete( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, 'bp' );
-
-	return true;
+	return $retval;
 }
 
-function groups_get_groupmeta( $group_id, $meta_key = '') {
-	global $wpdb, $bp;
+function groups_get_groupmeta( $group_id, $meta_key = '', $single = true ) {
 
-	$group_id = (int) $group_id;
+	// Legacy - Sanitize keys
+	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
 
-	if ( !$group_id )
-		return false;
+	add_filter( 'query', 'bp_filter_metaid_column_name' );
+	$retval = get_metadata( 'group', $group_id, $meta_key, $single );
+	remove_filter( 'query', 'bp_filter_metaid_column_name' );
 
-	if ( !empty($meta_key) ) {
-		$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
-
-		$metas = wp_cache_get( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, 'bp' );
-		if ( false === $metas ) {
-			$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key ) );
-			wp_cache_set( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, $metas, 'bp' );
+	// Legacy - If fetching all meta for a group, just return values
+	if ( empty( $meta_key ) ) {
+		$values = array();
+		foreach ( (array) $retval as $r ) {
+			$values[] = array_pop( $r );
 		}
-	} else {
-		$metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d", $group_id ) );
+		$retval = $values;
 	}
 
-	if ( empty( $metas ) ) {
-		if ( empty( $meta_key ) )
-			return array();
-		else
-			return '';
-	}
-
-	$metas = array_map( 'maybe_unserialize', (array) $metas );
-
-	if ( 1 == count( $metas ) )
-		return $metas[0];
-	else
-		return $metas;
+	return $retval;
 }
 
 function groups_update_groupmeta( $group_id, $meta_key, $meta_value ) {
-	global $wpdb, $bp;
 
-	if ( !is_numeric( $group_id ) )
-		return false;
+	add_filter( 'query', 'bp_filter_metaid_column_name' );
+	$retval = update_metadata( 'group', $group_id, $meta_key, $meta_value );
+	remove_filter( 'query', 'bp_filter_metaid_column_name' );
 
-	$meta_key = preg_replace( '|[^a-z0-9_]|i', '', $meta_key );
-
-	if ( is_string( $meta_value ) ) {
-		$meta_value = stripslashes( $meta_value );
+	// Legacy - return true if we fall through to add_metadata()
+	if ( is_int( $retval ) ) {
+		$retval = true;
 	}
 
-	$meta_value = maybe_serialize( $meta_value );
-
-	$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM " . $bp->groups->table_name_groupmeta . " WHERE group_id = %d AND meta_key = %s", $group_id, $meta_key ) );
-
-	if ( !$cur )
-		$wpdb->query( $wpdb->prepare( "INSERT INTO " . $bp->groups->table_name_groupmeta . " ( group_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", $group_id, $meta_key, $meta_value ) );
-	else if ( $cur->meta_value != $meta_value )
-		$wpdb->query( $wpdb->prepare( "UPDATE " . $bp->groups->table_name_groupmeta . " SET meta_value = %s WHERE group_id = %d AND meta_key = %s", $meta_value, $group_id, $meta_key ) );
-	else
-		return false;
-
-	// Update the cached object and recache
-	wp_cache_set( 'bp_groups_groupmeta_' . $group_id . '_' . $meta_key, $meta_value, 'bp' );
-
-	return true;
+	return $retval;
 }
 
 /*** Group Cleanup Functions ****************************************************/
Index: bp-groups/bp-groups-loader.php
--- bp-groups/bp-groups-loader.php
+++ bp-groups/bp-groups-loader.php
@@ -136,6 +136,11 @@
 			'table_name_groupmeta' => $bp->table_prefix . 'bp_groups_groupmeta'
 		);
 
+		// Metadata tables for groups component
+		$meta_tables = array(
+			'group' => $bp->table_prefix . 'bp_groups_groupmeta'
+		);
+
 		// All globals for groups component.
 		// Note that global_tables is included in this array.
 		$args = array(
@@ -144,7 +149,8 @@
 			'has_directory'         => true,
 			'notification_callback' => 'groups_format_notifications',
 			'search_string'         => __( 'Search Groups...', 'buddypress' ),
-			'global_tables'         => $global_tables
+			'global_tables'         => $global_tables,
+			'meta_tables'           => $meta_tables
 		);
 
 		parent::setup_globals( $args );
