diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 7d07e6775d9b087b44465917a32dfefc0e308fcf..46e944a70226381bad9a24e2a8dc373d6a3c5835 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -105,10 +105,6 @@ interface Database<T> {
 	 */
 	void addSubscription(T txn, GroupId g) throws DbException;
 
-	// FIXME: Replace these two methods with a setSubscriptions() method
-	void addSubscription(T txn, ContactId c, GroupId g) throws DbException;
-	void clearSubscriptions(T txn, ContactId c) throws DbException;
-
 	/**
 	 * Returns true iff the database contains the given contact.
 	 * <p>
@@ -314,6 +310,14 @@ interface Database<T> {
 	 */
 	void setStatus(T txn, ContactId c, MessageId m, Status s) throws DbException;
 
+	/**
+	 * Sets the subscriptions for the given contact, replacing any existing
+	 * subscriptions.
+	 * <p>
+	 * Locking: contacts read, subscriptions write.
+	 */
+	void setSubscriptions(T txn, ContactId c, Set<GroupId> subs) throws DbException;
+
 	/**
 	 * Sets the local transport details, replacing any existing transport
 	 * details.
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 7a973296c96f937d57122d6829f27b3be4312e12..adbc6a6c3101b8433888455ebe390fe14d8657b6 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -526,43 +526,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public void addSubscription(Connection txn, ContactId c, GroupId g)
-	throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "INSERT INTO contactSubscriptions"
-				+ " (contactId, groupId)"
-				+ " VALUES (?, ?)";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			ps.setBytes(2, g.getBytes());
-			int rowsAffected = ps.executeUpdate();
-			assert rowsAffected == 1;
-			ps.close();
-		} catch(SQLException e) {
-			tryToClose(ps);
-			tryToClose(txn);
-			throw new DbException(e);
-		}
-	}
-
-	public void clearSubscriptions(Connection txn, ContactId c)
-	throws DbException {
-		PreparedStatement ps = null;
-		try {
-			String sql = "DELETE FROM contactSubscriptions"
-				+ " WHERE contactId = ?";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			ps.executeUpdate();
-			ps.close();
-		} catch(SQLException e) {
-			tryToClose(ps);
-			tryToClose(txn);
-			throw new DbException(e);
-		}
-	}
-
 	public boolean containsContact(Connection txn, ContactId c)
 	throws DbException {
 		PreparedStatement ps = null;
@@ -1327,6 +1290,38 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public void setSubscriptions(Connection txn, ContactId c, Set<GroupId> subs)
+	throws DbException {
+		PreparedStatement ps = null;
+		try {
+			// Delete any existing subscriptions
+			String sql = "DELETE FROM contactSubscriptions WHERE contactId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			ps.executeUpdate();
+			ps.close();
+			// Store the new subscriptions
+			sql = "INSERT INTO contactSubscriptions (contactId, groupId)"
+				+ " VALUES (?, ?)";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			for(GroupId g : subs) {
+				ps.setBytes(2, g.getBytes());
+				ps.addBatch();
+			}
+			int[] rowsAffectedArray = ps.executeBatch();
+			assert rowsAffectedArray.length == subs.size();
+			for(int i = 0; i < rowsAffectedArray.length; i++) {
+				assert rowsAffectedArray[i] == 1;
+			}
+			ps.close();
+		} catch(SQLException e) {
+			tryToClose(ps);
+			tryToClose(txn);
+			throw new DbException(e);
+		}
+	}
+
 	public void setTransports(Connection txn, Map<String, String> transports)
 	throws DbException {
 		PreparedStatement ps = null;
diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
index 7d6100d0b58161fea83398fc8d81843f5fd0f496..a31f5c5a8ed2bb21a361882d92af7649d8181840 100644
--- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
+++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
@@ -473,10 +473,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			try {
 				Txn txn = db.startTransaction();
 				try {
-					// FIXME: Replace clearSubs and addSub with setSubs
-					db.clearSubscriptions(txn, c);
 					Set<GroupId> subs = h.getSubscriptions();
-					for(GroupId sub : subs) db.addSubscription(txn, c, sub);
+					db.setSubscriptions(txn, c, subs);
 					if(LOG.isLoggable(Level.FINE))
 						LOG.fine("Received " + subs.size() + " subscriptions");
 					db.commitTransaction(txn);
diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
index 403dc153637e2a519ba9323b6cc0be10c3943af3..5e115e9d95b8e56cb8231d1244999ab34f846bb0 100644
--- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
+++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java
@@ -354,10 +354,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			synchronized(subscriptionLock) {
 				Txn txn = db.startTransaction();
 				try {
-					// FIXME: Replace clearSubs and addSub with setSubs
-					db.clearSubscriptions(txn, c);
 					Set<GroupId> subs = h.getSubscriptions();
-					for(GroupId sub : subs) db.addSubscription(txn, c, sub);
+					db.setSubscriptions(txn, c, subs);
 					if(LOG.isLoggable(Level.FINE))
 						LOG.fine("Received " + subs.size() + " subscriptions");
 					db.commitTransaction(txn);
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 298bace357d9c06c67f93c6ed1a49ba0e614d811..17f92cebb4f916890c55dbf6ded339143bd6a87e 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -548,10 +548,9 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(acks));
 			oneOf(database).removeAckedBatch(txn, contactId, batchId);
 			// Subscriptions
-			oneOf(database).clearSubscriptions(txn, contactId);
 			oneOf(header).getSubscriptions();
 			will(returnValue(subs));
-			oneOf(database).addSubscription(txn, contactId, groupId);
+			oneOf(database).setSubscriptions(txn, contactId, subs);
 			// Transports
 			oneOf(header).getTransports();
 			will(returnValue(transports));
diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java
index 4408ab6791fc32f242c8ec1f7d7415fbec4cd477..c830a6a2b9116b876748390ee0b4aa50163917e7 100644
--- a/test/net/sf/briar/db/H2DatabaseTest.java
+++ b/test/net/sf/briar/db/H2DatabaseTest.java
@@ -211,7 +211,7 @@ public class H2DatabaseTest extends TestCase {
 		Connection txn = db.startTransaction();
 		assertEquals(contactId, db.addContact(txn, null));
 		db.addSubscription(txn, groupId);
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		db.addMessage(txn, message);
 		db.setStatus(txn, contactId, messageId, Status.NEW);
 		db.commitTransaction(txn);
@@ -253,7 +253,7 @@ public class H2DatabaseTest extends TestCase {
 		Connection txn = db.startTransaction();
 		assertEquals(contactId, db.addContact(txn, null));
 		db.addSubscription(txn, groupId);
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		db.addMessage(txn, message);
 		db.setSendability(txn, messageId, 1);
 		db.commitTransaction(txn);
@@ -315,7 +315,7 @@ public class H2DatabaseTest extends TestCase {
 
 		// The contact subscribing should make the message sendable
 		txn = db.startTransaction();
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
 		assertTrue(it.hasNext());
 		assertEquals(messageId, it.next());
@@ -323,7 +323,7 @@ public class H2DatabaseTest extends TestCase {
 
 		// The contact unsubscribing should make the message unsendable
 		txn = db.startTransaction();
-		db.clearSubscriptions(txn, contactId);
+		db.setSubscriptions(txn, contactId, Collections.<GroupId>emptySet());
 		it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
 		assertFalse(it.hasNext());
 		db.commitTransaction(txn);
@@ -342,7 +342,7 @@ public class H2DatabaseTest extends TestCase {
 		Connection txn = db.startTransaction();
 		assertEquals(contactId, db.addContact(txn, null));
 		db.addSubscription(txn, groupId);
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		db.addMessage(txn, message);
 		db.setSendability(txn, messageId, 1);
 		db.setStatus(txn, contactId, messageId, Status.NEW);
@@ -408,7 +408,7 @@ public class H2DatabaseTest extends TestCase {
 		Connection txn = db.startTransaction();
 		assertEquals(contactId, db.addContact(txn, null));
 		db.addSubscription(txn, groupId);
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		db.addMessage(txn, message);
 		db.setSendability(txn, messageId, 1);
 		db.setStatus(txn, contactId, messageId, Status.NEW);
@@ -451,7 +451,7 @@ public class H2DatabaseTest extends TestCase {
 		Connection txn = db.startTransaction();
 		assertEquals(contactId, db.addContact(txn, null));
 		db.addSubscription(txn, groupId);
-		db.addSubscription(txn, contactId, groupId);
+		db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
 		db.addMessage(txn, message);
 		db.setSendability(txn, messageId, 1);
 		db.setStatus(txn, contactId, messageId, Status.NEW);