killbill-aplcache

Blurp 3

11/9/2012 1:21:14 AM

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index 8de582c..7a84076 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -76,7 +76,7 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     @Override
     public List<AccountEmail> getEmails(final UUID accountId,
                                         final InternalTenantContext context) {
-        return accountEmailDao.getEmails(accountId, context);
+        return accountEmailDao.getByAccountId(accountId, context);
     }
 
     @Override
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index 31eec47..9869b97 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -151,16 +151,16 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
     @Override
     public List<AccountEmail> getEmails(final UUID accountId, final TenantContext context) {
-        return accountEmailDao.getEmails(accountId, internalCallContextFactory.createInternalTenantContext(context));
+        return accountEmailDao.getByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
-        accountEmailDao.addEmail(accountId, email, internalCallContextFactory.createInternalCallContext(accountId, context));
+    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) throws AccountApiException {
+        accountEmailDao.create(email, internalCallContextFactory.createInternalCallContext(accountId, context));
     }
 
     @Override
     public void removeEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
-        accountEmailDao.removeEmail(accountId, email, internalCallContextFactory.createInternalCallContext(accountId, context));
+        accountEmailDao.delete(email, internalCallContextFactory.createInternalCallContext(accountId, context));
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
index 5026e62..c7abebd 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
@@ -27,18 +27,7 @@ import com.ning.billing.util.entity.dao.EntityDao;
 
 public interface AccountEmailDao extends EntityDao<AccountEmail, AccountApiException> {
 
-    public List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);
+    public void delete(AccountEmail email, InternalCallContext context);
 
-    /**
-     * Add, remove or update the list of emails in this account.
-     *
-     * @param accountId id of the account
-     * @param emails    the final list of emails
-     * @param context   the call context
-     */
-    public void saveEmails(UUID accountId, List<AccountEmail> emails, InternalCallContext context);
-
-    public void addEmail(UUID accountId, AccountEmail email, InternalCallContext context);
-
-    public void removeEmail(UUID accountId, AccountEmail email, InternalCallContext context);
+    public List<AccountEmail> getByAccountId(UUID accountId, InternalTenantContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
index ab569d8..7beb12f 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailSqlDao.java
@@ -16,52 +16,30 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 
-import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.ObjectTypeBinder;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
 @EntitySqlDaoStringTemplate
 @RegisterMapper(AccountEmailMapper.class)
-public interface AccountEmailSqlDao extends UpdatableEntityCollectionSqlDao<AccountEmail>, Transactional<AccountEmailSqlDao> {
+public interface AccountEmailSqlDao extends EntitySqlDao<AccountEmail> {
 
-    @Override
-    @SqlBatch
-    public void insertFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
+    @SqlUpdate
+    public void delete(@BindBean final AccountEmail accountEmail,
+                       @BindBean final InternalCallContext context);
 
-    @Override
-    @SqlBatch
-    public void updateFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void deleteFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @AccountEmailBinder final Collection<AccountEmail> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void addHistoryFromTransaction(@Bind("objectId") final String objectId,
-                                          @ObjectTypeBinder final ObjectType objectType,
-                                          @AccountEmailHistoryBinder final List<EntityHistory<AccountEmail>> entities,
-                                          @InternalTenantContextBinder final InternalCallContext context);
+    @SqlQuery
+    public List<AccountEmail> getEmailByAccountId(@Bind("accountId") final UUID accountId,
+                                                  @BindBean final InternalTenantContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
index d3745b8..c0a911b 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
@@ -16,152 +16,54 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
-import com.ning.billing.ObjectType;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountEmail;
-import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.dao.AuditedCollectionDaoBase;
-import com.ning.billing.util.dao.TableName;
-import com.ning.billing.util.entity.EntityPersistenceException;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
-import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
-public class AuditedAccountEmailDao extends AuditedCollectionDaoBase<AccountEmail, AccountEmail> implements AccountEmailDao {
-
-    private final AccountEmailSqlDao accountEmailSqlDao;
+public class AuditedAccountEmailDao extends EntityDaoBase<AccountEmail, AccountApiException> implements AccountEmailDao {
 
     @Inject
-    public AuditedAccountEmailDao(final IDBI dbi, final Clock clock) {
-        super(clock);
-        this.accountEmailSqlDao = dbi.onDemand(AccountEmailSqlDao.class);
-    }
-
-    @Override
-    public void create(final AccountEmail entity, final InternalCallContext context) throws AccountApiException {
-        saveEmails(entity.getAccountId(), ImmutableList.<AccountEmail>of(entity), context);
-    }
-
-    @Override
-    public Long getRecordId(final UUID id, final InternalTenantContext context) {
-        return accountEmailSqlDao.getRecordId(id.toString(), context);
-    }
-
-    @Override
-    public AccountEmail getById(final UUID id, final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public AccountEmail getByRecordId(final Long recordId, final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
-    }
-
-
-    @Override
-    public List<AccountEmail> get(final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
+    public AuditedAccountEmailDao(final IDBI dbi) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi), AccountEmailSqlDao.class);
     }
 
     @Override
-    protected AccountEmail getEquivalenceObjectFor(final AccountEmail obj) {
-        return obj;
-    }
-
-    @Override
-    public List<AccountEmail> getEmails(final UUID accountId, final InternalTenantContext context) {
-        return new ArrayList<AccountEmail>(super.loadEntities(accountId, ObjectType.ACCOUNT_EMAIL, context).values());
-    }
-
-    @Override
-    public void saveEmails(final UUID accountId, final List<AccountEmail> emails, final InternalCallContext context) {
-        super.saveEntities(accountId, ObjectType.ACCOUNT_EMAIL, emails, context);
-    }
-
-    @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final InternalCallContext context) {
-        accountEmailSqlDao.inTransaction(new Transaction<Object, AccountEmailSqlDao>() {
+    public void delete(final AccountEmail email, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Object inTransaction(final AccountEmailSqlDao transactional, final TransactionStatus status) throws Exception {
-                // Compute the final list of emails by looking up the current ones and adding the new one
-                // We can use a simple set here as the supplied email may not have its id field populated
-                final List<AccountEmail> currentEmails = accountEmailSqlDao.load(accountId.toString(), ObjectType.ACCOUNT_EMAIL, context);
-                final Map<String, AccountEmail> newEmails = new HashMap<String, AccountEmail>();
-                for (final AccountEmail currentEmail : currentEmails) {
-                    newEmails.put(currentEmail.getEmail(), currentEmail);
-                }
-                // TODO Note the hack here... saveEntitiesFromTransaction() works by comparing objects in sets, so we
-                // have to use DefaultAccountEmail instances (email is likely to be an inner class from AccountEmailJson
-                // here and will confuse AuditedCollectionDaoBase).
-                newEmails.put(email.getEmail(), new DefaultAccountEmail(email.getAccountId(), email.getEmail()));
-
-                saveEntitiesFromTransaction(getSqlDao(context), accountId, ObjectType.ACCOUNT_EMAIL,
-                                            ImmutableList.<AccountEmail>copyOf(newEmails.values()), context);
-
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class).delete(email, context);
                 return null;
             }
         });
     }
 
     @Override
-    public void removeEmail(final UUID accountId, final AccountEmail email, final InternalCallContext context) {
-        accountEmailSqlDao.inTransaction(new Transaction<Object, AccountEmailSqlDao>() {
+    public List<AccountEmail> getByAccountId(final UUID accountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AccountEmail>>() {
             @Override
-            public Object inTransaction(final AccountEmailSqlDao transactional, final TransactionStatus status) throws Exception {
-                // Compute the final list of emails by looking up the current ones and removing the new one
-                // We can use a simple set here as the supplied email may not have its id field populated
-                final List<AccountEmail> currentEmails = accountEmailSqlDao.load(accountId.toString(), ObjectType.ACCOUNT_EMAIL, context);
-                final Map<String, AccountEmail> newEmails = new HashMap<String, AccountEmail>();
-                for (final AccountEmail currentEmail : currentEmails) {
-                    newEmails.put(currentEmail.getEmail(), currentEmail);
-                }
-                newEmails.remove(email.getEmail());
-
-                saveEntitiesFromTransaction(getSqlDao(context), accountId, ObjectType.ACCOUNT_EMAIL,
-                                            ImmutableList.<AccountEmail>copyOf(newEmails.values()), context);
-
-                return null;
+            public List<AccountEmail> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(AccountEmailSqlDao.class).getEmailByAccountId(accountId, context);
             }
         });
     }
 
     @Override
-    public String getKey(final AccountEmail entity, final InternalTenantContext context) {
-        return entity.getEmail();
-    }
-
-    @Override
-    public void test(final InternalTenantContext context) {
-        accountEmailSqlDao.test(context);
-    }
-
-    @Override
-    protected TableName getTableName(final InternalTenantContext context) {
-        return TableName.ACCOUNT_EMAIL_HISTORY;
-    }
-
-    @Override
-    protected UpdatableEntityCollectionSqlDao<AccountEmail> transmogrifyDao(final Transmogrifier transactionalDao, final InternalTenantContext context) {
-        return transactionalDao.become(AccountEmailSqlDao.class);
-    }
-
-    @Override
-    protected UpdatableEntityCollectionSqlDao<AccountEmail> getSqlDao(final InternalTenantContext context) {
-        return accountEmailSqlDao;
+    protected AccountApiException generateAlreadyExistsException(final AccountEmail entity, final InternalCallContext context) {
+        return new AccountApiException(ErrorCode.ACCOUNT_EMAIL_ALREADY_EXISTS, entity.getId());
     }
 }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
index fc11f5c..aab878c 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountEmailSqlDao.sql.stg
@@ -20,7 +20,16 @@ tableValues() ::= <<
 , :updatedDate
  >>
 
- historyTableName() ::= "account_email_history"
+historyTableName() ::= "account_email_history"
+
+getEmailByAccountId() ::= <<
+select
+<allTableFields()>
+from <tableName()>
+where account_id = :accountId
+<AND_CHECK_TENANT()>
+;
+>>
 
 updateFromTransaction() ::= <<
     UPDATE account_emails
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
index 01284ca..51111aa 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -115,20 +115,20 @@ public class TestDefaultAccountUserApi extends AccountTestSuite {
         final UUID accountId = UUID.randomUUID();
 
         // Verify the initial state
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 0);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 0);
 
         // Add the first email
         final String email1 = UUID.randomUUID().toString();
         accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 1);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 1);
 
         // Add a second one
         final String email2 = UUID.randomUUID().toString();
         accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email2), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 2);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 2);
 
         // Remove the first second one
         accountUserApi.removeEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, tenantContext).size(), 1);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 1);
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index c486939..f28c777 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -55,7 +55,7 @@ public abstract class AccountDaoTestBase extends AccountTestSuiteWithEmbeddedDB 
             // Health check test to make sure MySQL is setup properly
             accountDao.test(internalCallContext);
 
-            accountEmailDao = new AuditedAccountEmailDao(dbi, new DefaultClock());
+            accountEmailDao = new AuditedAccountEmailDao(dbi);
             // Health check test to make sure MySQL is setup properly
             accountEmailDao.test(internalCallContext);
         } catch (Throwable t) {
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
index 1ddc50d..aa4c401 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
@@ -37,7 +37,8 @@ public class MockAccountEmailDao implements AccountEmailDao {
 
     @Override
     public void create(final AccountEmail entity, final InternalCallContext context) throws AccountApiException {
-        saveEmails(entity.getAccountId(), ImmutableList.<AccountEmail>of(entity), context);
+        Set<AccountEmail> theSet = emails.get(entity.getAccountId());
+        theSet.add(entity);
     }
 
     @Override
@@ -56,7 +57,13 @@ public class MockAccountEmailDao implements AccountEmailDao {
     }
 
     @Override
-    public List<AccountEmail> getEmails(final UUID accountId, final InternalTenantContext context) {
+    public void delete(final AccountEmail email, final InternalCallContext context) {
+        Set<AccountEmail> theSet = emails.get(email.getAccountId());
+        theSet.remove(email);
+    }
+
+    @Override
+    public List<AccountEmail> getByAccountId(final UUID accountId, final InternalTenantContext context) {
         final Set<AccountEmail> accountEmails = emails.get(accountId);
         if (accountEmails == null) {
             return ImmutableList.<AccountEmail>of();
@@ -66,33 +73,6 @@ public class MockAccountEmailDao implements AccountEmailDao {
     }
 
     @Override
-    public void saveEmails(final UUID accountId, final List<AccountEmail> newEmails, final InternalCallContext context) {
-        if (emails.get(accountId) == null) {
-            emails.put(accountId, new HashSet<AccountEmail>());
-        }
-
-        emails.get(accountId).addAll(newEmails);
-    }
-
-    @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final InternalCallContext context) {
-        if (emails.get(accountId) == null) {
-            emails.put(accountId, new HashSet<AccountEmail>());
-        }
-
-        emails.get(accountId).add(email);
-    }
-
-    @Override
-    public void removeEmail(final UUID accountId, final AccountEmail email, final InternalCallContext context) {
-        if (emails.get(accountId) == null) {
-            emails.put(accountId, new HashSet<AccountEmail>());
-        }
-
-        emails.get(accountId).remove(email);
-    }
-
-    @Override
     public void test(final InternalTenantContext context) {
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index e6fc771..05168f9 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -423,20 +423,20 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testHandleDuplicateEmails() {
+    public void testHandleDuplicateEmails() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
         final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 0);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, internalCallContext).size(), 0);
 
-        accountEmailDao.addEmail(accountId, email, internalCallContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 1);
+        accountEmailDao.create(email, internalCallContext);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, internalCallContext).size(), 1);
 
-        accountEmailDao.addEmail(accountId, email, internalCallContext);
-        Assert.assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 1);
+        accountEmailDao.create(email, internalCallContext);
+        Assert.assertEquals(accountEmailDao.getByAccountId(accountId, internalCallContext).size(), 1);
     }
 
     @Test(groups = "slow")
-    public void testAccountEmail() {
+    public void testAccountEmail() throws AccountApiException {
         List<AccountEmail> emails = new ArrayList<AccountEmail>();
 
         // generate random account id
@@ -444,9 +444,8 @@ public class TestAccountDao extends AccountDaoTestBase {
 
         // add a new e-mail
         final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
-        emails.add(email);
-        accountEmailDao.saveEmails(accountId, emails, internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.create(email, internalCallContext);
+        emails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(emails.size(), 1);
 
         // verify that history and audit contain one entry
@@ -456,8 +455,8 @@ public class TestAccountDao extends AccountDaoTestBase {
         final AccountEmail updatedEmail = new DefaultAccountEmail(email, "test2@gmail.com");
         emails.clear();
         emails.add(updatedEmail);
-        accountEmailDao.saveEmails(accountId, emails, internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.create(updatedEmail, internalCallContext);
+        emails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(emails.size(), 1);
 
         // verify that history and audit contain three entries
@@ -465,8 +464,10 @@ public class TestAccountDao extends AccountDaoTestBase {
         verifyAccountEmailAuditAndHistoryCount(accountId, 3);
 
         // delete e-mail
-        accountEmailDao.saveEmails(accountId, new ArrayList<AccountEmail>(), internalCallContext);
-        emails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.delete(email, internalCallContext);
+        accountEmailDao.delete(updatedEmail, internalCallContext);
+
+        emails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(emails.size(), 0);
 
         // verify that history and audit contain four entries
@@ -474,26 +475,26 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testAddAndRemoveAccountEmail() {
+    public void testAddAndRemoveAccountEmail() throws AccountApiException {
         final UUID accountId = UUID.randomUUID();
         final String email1 = UUID.randomUUID().toString();
         final String email2 = UUID.randomUUID().toString();
 
         // Verify the original state
-        assertEquals(accountEmailDao.getEmails(accountId, internalCallContext).size(), 0);
+        assertEquals(accountEmailDao.getByAccountId(accountId, internalCallContext).size(), 0);
 
         // Add a new e-mail
         final AccountEmail accountEmail1 = new DefaultAccountEmail(accountId, email1);
-        accountEmailDao.addEmail(accountId, accountEmail1, internalCallContext);
-        final List<AccountEmail> firstEmails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.create(accountEmail1, internalCallContext);
+        final List<AccountEmail> firstEmails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(firstEmails.size(), 1);
         assertEquals(firstEmails.get(0).getAccountId(), accountId);
         assertEquals(firstEmails.get(0).getEmail(), email1);
 
         // Add a second e-mail
         final AccountEmail accountEmail2 = new DefaultAccountEmail(accountId, email2);
-        accountEmailDao.addEmail(accountId, accountEmail2, internalCallContext);
-        final List<AccountEmail> secondEmails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.create(accountEmail2, internalCallContext);
+        final List<AccountEmail> secondEmails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(secondEmails.size(), 2);
         assertTrue(secondEmails.get(0).getAccountId().equals(accountId));
         assertTrue(secondEmails.get(1).getAccountId().equals(accountId));
@@ -501,8 +502,8 @@ public class TestAccountDao extends AccountDaoTestBase {
         assertTrue(secondEmails.get(1).getEmail().equals(email1) || secondEmails.get(1).getEmail().equals(email2));
 
         // Delete the first e-mail
-        accountEmailDao.removeEmail(accountId, accountEmail1, internalCallContext);
-        final List<AccountEmail> thirdEmails = accountEmailDao.getEmails(accountId, internalCallContext);
+        accountEmailDao.delete(accountEmail1, internalCallContext);
+        final List<AccountEmail> thirdEmails = accountEmailDao.getByAccountId(accountId, internalCallContext);
         assertEquals(thirdEmails.size(), 1);
         assertEquals(thirdEmails.get(0).getAccountId(), accountId);
         assertEquals(thirdEmails.get(0).getEmail(), email2);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
index a38ba20..ccd29e0 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
@@ -86,7 +86,7 @@ public class TestBusinessTagRecorder extends AnalyticsTestSuiteWithEmbeddedDB {
         subscriptionTransitionTagSqlDao = dbi.onDemand(BusinessSubscriptionTransitionTagSqlDao.class);
         eventBus = new InMemoryInternalBus();
         final AccountDao accountDao = new AuditedAccountDao(dbi, eventBus, new InternalCallContextFactory(dbi, new ClockMock()));
-        final AccountEmailDao accountEmailDao = new AuditedAccountEmailDao(dbi, clock);
+        final AccountEmailDao accountEmailDao = new AuditedAccountEmailDao(dbi);
         callContextFactory = new DefaultCallContextFactory(clock);
         final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
         accountApi = new DefaultAccountInternalApi(accountDao, accountEmailDao);
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index da18c28..d21e392 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -131,7 +131,7 @@ public interface AccountUserApi {
      * @param email     the email to be added
      * @param context   the user context
      */
-    public void addEmail(UUID accountId, AccountEmail email, CallContext context);
+    public void addEmail(UUID accountId, AccountEmail email, CallContext context) throws AccountApiException;
 
     /**
      *
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 0230f96..b694ab2 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -156,6 +156,8 @@ public enum ErrorCode {
     ACCOUNT_CREATION_FAILED(3006, "Account creation failed."),
     ACCOUNT_UPDATE_FAILED(3007, "Account update failed."),
 
+    ACCOUNT_EMAIL_ALREADY_EXISTS(3500, "Account email already exists %s"),
+
     /*
     *
     * Range 3900: Tag definitions
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
index 3d8a7ab..85d01cc 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/api/BlockingAccountUserApi.java
@@ -101,7 +101,7 @@ public class BlockingAccountUserApi implements AccountUserApi {
     }
 
     @Override
-    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
+    public void addEmail(final UUID accountId, final AccountEmail email, final CallContext context) throws AccountApiException {
         userApi.addEmail(accountId, email, context);
     }