killbill-memoizeit

Changes

account/pom.xml 18(+17 -1)

Details

account/pom.xml 18(+17 -1)

diff --git a/account/pom.xml b/account/pom.xml
index f92d35b..b3516c5 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -72,7 +72,7 @@
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
         </dependency>
-       <dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -81,6 +81,22 @@
             <artifactId>testng</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management-dbfiles</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/account/src/main/java/com/ning/billing/account/api/Account.java b/account/src/main/java/com/ning/billing/account/api/Account.java
index 283f141..66f8c5d 100644
--- a/account/src/main/java/com/ning/billing/account/api/Account.java
+++ b/account/src/main/java/com/ning/billing/account/api/Account.java
@@ -16,27 +16,35 @@
 
 package com.ning.billing.account.api;
 
-import java.util.UUID;
-
+import com.ning.billing.account.dao.IAccountDao;
+import com.ning.billing.account.glue.InjectorMagic;
 import com.ning.billing.catalog.api.Currency;
 
+import java.util.UUID;
+
 public class Account implements IAccount {
+    private final FieldStore fields;
+    private static IAccountDao dao;
 
     private final UUID id;
-    private final String key;
-
-    public Account(String key) {
-        this(UUID.randomUUID(), key);
+    private String key;
+    private String email;
+    private String name;
+    private String phone;
+    private Currency currency;
+    private int billCycleDay;
+
+    public Account() {
+        this(UUID.randomUUID());
     }
 
-
-    public Account(UUID id, String key) {
-        super();
+    public Account(UUID id) {
         this.id = id;
-        this.key = key;
+        fields = FieldStore.create(getId(), getObjectName());
+        dao = InjectorMagic.getAccountDao();
     }
 
-    @Override
+   @Override
     public UUID getId() {
         return id;
     }
@@ -46,38 +54,130 @@ public class Account implements IAccount {
         return key;
     }
 
+    public Account withKey(String key) {
+        this.key = key;
+        return this;
+    }
+
     @Override
     public String getName() {
-        return null;
+        return name;
+    }
+
+    public Account withName(String name) {
+        this.name = name;
+        return this;
     }
 
     @Override
     public String getEmail() {
-        return null;
+        return email;
+    }
+
+    public Account withEmail(String email) {
+        this.email = email;
+        return this;
     }
 
     @Override
     public String getPhone() {
-        return null;
+        return phone;
     }
 
+    public Account withPhone(String phone) {
+        this.phone = phone;
+        return this;
+    }
 
     @Override
     public int getBillCycleDay() {
-        return 0;
+        return billCycleDay;
+    }
+
+    public Account withBillCycleDay(int billCycleDay) {
+        this.billCycleDay = billCycleDay;
+        return this;
     }
 
     @Override
     public Currency getCurrency() {
-        return null;
+        return currency;
+    }
+
+    public Account withCurrency(Currency currency) {
+        this.currency = currency;
+        return this;
+    }
+
+    public static Account create() {
+        return new Account();
+    }
+
+    public static Account create(UUID id) {
+        return new Account(id);
+    }
+
+    public static Account loadAccount(UUID id) {
+        Account account = (Account) dao.getAccountById(id);
+        if (account != null) {
+            account.loadCustomFields();
+        }
+        return account;
+    }
+
+    public static Account loadAccount(String key) {
+        Account account = (Account) dao.getAccountByKey(key);
+        if (account != null) {
+            account.loadCustomFields();
+        }
+        return account;
+    }
+
+    @Override
+    public String getFieldValue(String fieldName) {
+        return fields.getValue(fieldName);
+    }
+
+    @Override
+    public void setFieldValue(String fieldName, String fieldValue) {
+        fields.setValue(fieldName, fieldValue);
     }
 
     @Override
-    public void setPrivate(String name, String value) {
+    public void save() {
+        saveObject();
+        saveCustomFields();
     }
 
     @Override
-    public String getPrivate(String name) {
-        return null;
+    public void load() {
+        loadObject();
+        loadCustomFields();
+    }
+
+    private void saveCustomFields() {
+        fields.save();
+    }
+
+    protected void loadCustomFields() {
+        fields.load();
+    }
+
+    public String getObjectName() {
+        return "Account";
+    }
+
+    private void saveObject() {
+        dao.save(this);
+    }
+
+    private void loadObject() {
+        IAccount that = dao.getAccountById(id);
+        this.key = that.getKey();
+        this.email = that.getEmail();
+        this.name = that.getName();
+        this.phone = that.getPhone();
+        this.currency = that.getCurrency();
+        this.billCycleDay = that.getBillCycleDay();
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index d488eb1..d236c5f 100644
--- a/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -16,12 +16,12 @@
 
 package com.ning.billing.account.api;
 
-import java.util.List;
-import java.util.UUID;
-
 import com.google.inject.Inject;
 import com.ning.billing.account.dao.IAccountDao;
 
+import java.util.List;
+import java.util.UUID;
+
 public class AccountUserApi implements IAccountUserApi {
 
     private final IAccountDao dao;
@@ -43,7 +43,7 @@ public class AccountUserApi implements IAccountUserApi {
 
     @Override
     public IAccount getAccountFromId(UUID uid) {
-        return dao.getAccountFromId(uid);
+        return dao.getAccountById(uid);
     }
 
     @Override
diff --git a/account/src/main/java/com/ning/billing/account/api/CustomField.java b/account/src/main/java/com/ning/billing/account/api/CustomField.java
new file mode 100644
index 0000000..bae8d86
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/CustomField.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+import java.util.UUID;
+
+public class CustomField implements ICustomField {
+    private UUID id;
+    private String name;
+    private String value;
+    private boolean isNew;
+
+    public CustomField(String name, String value) {
+        this(UUID.randomUUID(), name, value);
+        this.isNew = true;
+    }
+
+    public CustomField(UUID id, String name, String value) {
+        this.id = id;
+        this.name = name;
+        this.value = value;
+        this.isNew = false;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    public String getIdAsString() {
+        return id.toString();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isNew() {
+        return this.isNew;
+    }
+
+    @Override
+    public void setAsSaved() {
+        this.isNew = false;
+    }
+
+    @Override
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/FieldStore.java b/account/src/main/java/com/ning/billing/account/api/FieldStore.java
new file mode 100644
index 0000000..2d1da7f
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/FieldStore.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+import com.ning.billing.account.dao.IFieldStoreDao;
+import com.ning.billing.account.glue.InjectorMagic;
+
+import java.util.*;
+
+public class FieldStore implements IFieldStore {
+    private Map<String, ICustomField> fields = new HashMap<String, ICustomField>();
+    private final UUID objectId;
+    private final String objectType;
+
+    public FieldStore(UUID objectId, String objectType) {
+        this.objectId = objectId;
+        this.objectType = objectType;
+    }
+
+    public static FieldStore create(UUID objectId, String objectType) {
+        return new FieldStore(objectId, objectType);
+    }
+
+    public void setValue(String fieldName, String fieldValue) {
+        if (fields.containsKey(fieldName)) {
+            fields.get(fieldName).setValue(fieldValue);
+        } else {
+            fields.put(fieldName, new CustomField(fieldName, fieldValue));
+        }
+    }
+
+    public String getValue(String fieldName) {
+        if (fields.containsKey(fieldName)) {
+            return fields.get(fieldName).getValue();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public List<ICustomField> getNewFields() {
+        List<ICustomField> newFields = new ArrayList<ICustomField>();
+        for (ICustomField field : fields.values()) {
+            if (field.isNew()) {
+                newFields.add(field);
+            }
+        }
+
+        return newFields;
+    }
+
+    @Override
+    public List<ICustomField> getUpdatedFields() {
+        List<ICustomField> updatedFields = new ArrayList<ICustomField>();
+        for (ICustomField field : fields.values()) {
+            if (!field.isNew()) {
+                updatedFields.add(field);
+            }
+        }
+
+        return updatedFields;
+    }
+
+    public void save() {
+        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
+
+        List<ICustomField> newFields = getNewFields();
+        dao.createFields(objectId.toString(), objectType, newFields);
+        for (ICustomField field : newFields) {
+            field.setAsSaved();
+        }
+
+        dao.saveFields(objectId.toString(), objectType, getUpdatedFields());
+    }
+
+    public void load() {
+        IFieldStoreDao dao = InjectorMagic.getFieldStoreDao();
+        List<ICustomField> fields = dao.getFields(objectId.toString(), objectType);
+        this.fields.clear();
+        if (fields != null) {
+            for (ICustomField field : fields) {
+                this.fields.put(field.getName(), field);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/ICustomField.java b/account/src/main/java/com/ning/billing/account/api/ICustomField.java
new file mode 100644
index 0000000..fa13e42
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/ICustomField.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+import java.util.UUID;
+
+public interface ICustomField {
+    public UUID getId();
+
+    public String getName();
+
+    public String getValue();
+
+    public void setValue(String value);
+
+    public boolean isNew();
+
+    public void setAsSaved();
+}
diff --git a/account/src/main/java/com/ning/billing/account/api/IFieldStore.java b/account/src/main/java/com/ning/billing/account/api/IFieldStore.java
new file mode 100644
index 0000000..5a7ba2a
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/api/IFieldStore.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.api;
+
+import java.util.List;
+
+public interface IFieldStore {
+    void setValue(String fieldName, String fieldValue);
+
+    String getValue(String fieldName);
+
+    List<ICustomField> getNewFields();
+
+    List<ICustomField> getUpdatedFields();
+
+    void save();
+
+    void load();
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index acc2fb1..15bf60f 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -16,29 +16,27 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.List;
-import java.util.UUID;
-
-import org.skife.jdbi.v2.DBI;
-import org.skife.jdbi.v2.Transaction;
-
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.api.IAccountData;
+import org.skife.jdbi.v2.IDBI;
+
+import java.util.List;
+import java.util.UUID;
 
 public class AccountDao implements IAccountDao {
 
     private final IAccountDaoSql dao;
 
     @Inject
-    public AccountDao(DBI dbi) {
+    public AccountDao(IDBI dbi) {
         this.dao = dbi.onDemand(IAccountDaoSql.class);
     }
 
     @Override
     public IAccount createAccount(IAccountData input) {
-        IAccount result = new Account(input.getKey());
+        IAccount result = new Account().withKey(input.getKey());
         dao.insertAccount(result);
         return result;
     }
@@ -49,7 +47,7 @@ public class AccountDao implements IAccountDao {
     }
 
     @Override
-    public IAccount getAccountFromId(UUID uid) {
+    public IAccount getAccountById(UUID uid) {
         return dao.getAccountFromId(uid.toString());
     }
 
@@ -57,4 +55,14 @@ public class AccountDao implements IAccountDao {
     public List<IAccount> getAccounts() {
         return dao.getAccounts();
     }
+
+    @Override
+    public void test() {
+        dao.test();
+    }
+
+    @Override
+    public void save(IAccount account) {
+        dao.insertAccount(account);
+    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java b/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java
new file mode 100644
index 0000000..e608f5e
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/FieldStoreDao.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.ICustomField;
+import org.skife.jdbi.v2.IDBI;
+
+import java.util.List;
+
+public class FieldStoreDao implements IFieldStoreDao {
+    private final IFieldStoreDao dao;
+
+    @Inject
+    public FieldStoreDao(IDBI dbi) {
+        dao = dbi.onDemand(IFieldStoreDao.class);
+    }
+
+    @Override
+    public void saveFields(String objectId, String objectType, List<ICustomField> fields) {
+        dao.saveFields(objectId, objectType, fields);
+    }
+
+    @Override
+    public void createFields(String objectId, String objectType,  List<ICustomField> fields) {
+        dao.createFields(objectId, objectType, fields);
+    }
+
+    @Override
+    public List<ICustomField> getFields(String objectId, String objectType) {
+        return dao.getFields(objectId, objectType);
+    }
+
+    @Override
+    public void test() {
+        dao.test();
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
index e044e3b..e23fbfa 100644
--- a/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/IAccountDao.java
@@ -16,20 +16,23 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.List;
-import java.util.UUID;
-
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.api.IAccountData;
 
+import java.util.List;
+import java.util.UUID;
+
 public interface IAccountDao {
 
     public IAccount createAccount(IAccountData account);
 
-    public IAccount getAccountByKey(String key);
+    public IAccount getAccountById(UUID uid);
 
-    public IAccount getAccountFromId(UUID uid);
+    public IAccount getAccountByKey(String key);
 
     public List<IAccount> getAccounts();
 
+    public void test();
+
+    public void save(IAccount account);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java b/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
index 1c032aa..7cb9eda 100644
--- a/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
+++ b/account/src/main/java/com/ning/billing/account/dao/IAccountDaoSql.java
@@ -16,11 +16,8 @@
 
 package com.ning.billing.account.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.UUID;
-
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.IAccount;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -33,8 +30,10 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.IAccount;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
 
 @ExternalizedSqlViaStringTemplate3()
 public interface IAccountDaoSql extends Transactional<IAccountDaoSql>, CloseMe {
@@ -54,11 +53,13 @@ public interface IAccountDaoSql extends Transactional<IAccountDaoSql>, CloseMe {
     @Mapper(IAccountSqlMapper.class)
     public List<IAccount> getAccounts();
 
+    @SqlUpdate
+    public void test();
 
     public static class IAccountSqlBinder implements Binder<Bind, IAccount> {
 
         @Override
-        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, IAccount account) {
+        public void bind(SQLStatement stmt, Bind bind, IAccount account) {
             stmt.bind("id", account.getId().toString());
             stmt.bind("key_name", account.getKey());
         }
@@ -71,7 +72,7 @@ public interface IAccountDaoSql extends Transactional<IAccountDaoSql>, CloseMe {
                 throws SQLException {
             UUID id = UUID.fromString(r.getString("id"));
             String key = r.getString("key_name");
-            return new Account(id, key);
+            return new Account(id).withKey(key);
         }
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java b/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java
new file mode 100644
index 0000000..56d6c04
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/IFieldStoreDao.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.CustomField;
+import com.ning.billing.account.api.ICustomField;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.*;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3
+public interface IFieldStoreDao {
+    @SqlBatch
+    public void saveFields(@Bind("objectId") final String objectId,
+                           @Bind("objectType") final String objectType,
+                           @BindBean final List<ICustomField> fields);
+
+    @SqlBatch
+    public void createFields(@Bind("objectId") final String objectId,
+                            @Bind("objectType") final String objectType,
+                            @BindBean final List<ICustomField> fields);
+
+    @SqlQuery
+    @RegisterMapper(CustomFieldMapper.class)
+    public List<ICustomField> getFields(@Bind("objectId") final String objectId,
+                                        @Bind("objectType") final String objectType);
+
+    @SqlUpdate
+    public void test();
+
+    public static class CustomFieldBinder implements Binder<Bind, ICustomField> {
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, ICustomField field) {
+            stmt.bind("id", field.getId().toString());
+            stmt.bind("fieldName", field.getName());
+            stmt.bind("fieldValue", field.getValue());
+        }
+    }
+
+    public static class CustomFieldMapper implements ResultSetMapper<ICustomField> {
+        @Override
+        public ICustomField map(int index, ResultSet result, StatementContext context)
+                throws SQLException {
+            UUID id = UUID.fromString(result.getString("id"));
+            String fieldName = result.getString("field_name");
+            String fieldValue = result.getString("field_value");
+            return new CustomField(id, fieldName, fieldValue);
+        }
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
index ce44014..24ca0a3 100644
--- a/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/AccountModule.java
@@ -16,15 +16,16 @@
 
 package com.ning.billing.account.glue;
 
-import org.skife.config.ConfigurationObjectFactory;
-
 import com.google.inject.AbstractModule;
 import com.ning.billing.account.api.AccountService;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.IAccountService;
 import com.ning.billing.account.api.IAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.account.dao.FieldStoreDao;
 import com.ning.billing.account.dao.IAccountDao;
+import com.ning.billing.account.dao.IFieldStoreDao;
+import org.skife.config.ConfigurationObjectFactory;
 
 public class AccountModule extends AbstractModule {
 
@@ -35,22 +36,28 @@ public class AccountModule extends AbstractModule {
 
     protected void installAccountDao() {
         bind(IAccountDao.class).to(AccountDao.class).asEagerSingleton();
+//        bind(IAccountDaoSql.class).to(IAccountDaoSql.class).asEagerSingleton();
     }
 
     protected void installAccountUserApi() {
         bind(IAccountUserApi.class).to(AccountUserApi.class).asEagerSingleton();
     }
 
-    protected void installAcountService() {
+    protected void installAccountService() {
         bind(IAccountService.class).to(AccountService.class).asEagerSingleton();
     }
 
+    protected void installFieldStore() {
+        bind(IFieldStoreDao.class).to(FieldStoreDao.class).asEagerSingleton();
+    }
+
     @Override
     protected void configure() {
         installConfig();
         installAccountDao();
         installAccountUserApi();
-        installAcountService();
+        installAccountService();
+        installFieldStore();
     }
 
 }
diff --git a/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java b/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java
new file mode 100644
index 0000000..6e00bf8
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/glue/InjectorMagic.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.account.glue;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.ning.billing.account.dao.IAccountDao;
+import com.ning.billing.account.dao.IFieldStoreDao;
+
+public class InjectorMagic {
+    public static InjectorMagic instance;
+
+    private final Injector injector;
+
+    @Inject
+    public InjectorMagic(Injector injector) {
+        this.injector = injector;
+        synchronized(InjectorMagic.class) {
+            if (instance == null) {
+                instance = this;
+            }
+        }
+    }
+
+    public static Injector get() {
+        if (instance == null) {
+            throw new RuntimeException("Trying to retrieve injector too early");
+        }
+        return instance.getInjector();
+    }
+
+    private Injector getInjector() {
+        return injector;
+    }
+
+    public static IFieldStoreDao getFieldStoreDao() {
+        return InjectorMagic.get().getInstance(IFieldStoreDao.class);
+    }
+
+    public static IAccountDao getAccountDao() {
+        return InjectorMagic.get().getInstance(IAccountDao.class);
+    }
+}
diff --git a/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
index b15fc2d..c7fa4fa 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/IAccountDaoSql.sql.stg
@@ -30,6 +30,9 @@ getAccountFromId(id) ::= <<
     ;
 >>
 
+test() ::= <<
+    select 1 from accounts;
+>>
 
 getAccounts() ::= <<
     select
@@ -38,3 +41,4 @@ getAccounts() ::= <<
     from accounts
     ;
 >>
+;
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg
new file mode 100644
index 0000000..a34f815
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/IFieldStoreDao.sql.stg
@@ -0,0 +1,24 @@
+group IFieldStoreDao;
+
+createFields() ::= <<
+  INSERT INTO custom_fields(id, object_id, object_type, field_name, field_value)
+  VALUES (:idAsString, :objectId, :objectType, :name, :value);
+>>
+
+saveFields() ::= <<
+    UPDATE custom_fields
+    SET object_type = :objectType, object_id = :objectId, field_name = :name, field_value = :value
+    WHERE id = :id;
+>>
+
+getFields() ::= <<
+    SELECT id, field_name, field_value
+    FROM custom_fields
+    WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
+test() ::= <<
+  SELECT 1 FROM custom_fields;
+>>
+;
+
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index 3769513..92f6bd0 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -2,5 +2,18 @@ DROP TABLE IF EXISTS accounts;
 CREATE TABLE accounts (
     id char(36) NOT NULL,
     key_name varchar(128) NOT NULL,
+
     PRIMARY KEY(id)
 ) ENGINE=innodb;
+
+DROP TABLE IF EXISTS custom_fields;
+CREATE TABLE custom_fields (
+  id char(36) NOT NULL,
+  object_id char(36) NOT NULL,
+  object_type varchar(30) NOT NULL,
+  field_name varchar(30) NOT NULL,
+  field_value varchar(255) NOT NULL,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+
+CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
\ No newline at end of file
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index b47f8ad..3b4a687 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -22,59 +22,52 @@ import com.google.inject.Stage;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.glue.AccountModuleMock;
-
+import com.ning.billing.account.glue.InjectorMagic;
+import org.apache.commons.io.IOUtils;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import java.io.IOException;
-import java.net.URL;
 import java.util.List;
+import java.util.UUID;
 
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.*;
 
+@Test(groups = {"Account", "Account-DAO"})
 public class TestSimpleAccountDao {
-
-
     private IAccountDao dao;
+    private InjectorMagic injectorMagic;
 
-    public static void loadSystemPropertiesFromClasspath( final String resource) {
-        final URL url = TestSimpleAccountDao.class.getResource(resource);
-        assertNotNull(url);
+    @BeforeClass(alwaysRun = true)
+    private void setup() throws IOException {
+        AccountModuleMock module = new AccountModuleMock();
+        final String ddl = IOUtils.toString(IAccountDaoSql.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        module.createDb(ddl);
 
+        // Healthcheck test to make sure MySQL is setup properly
         try {
-            System.getProperties().load( url.openStream() );
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-
-    private  Injector getInjector() {
-        return Guice.createInjector(Stage.DEVELOPMENT, new AccountModuleMock());
-    }
-
-
-    @BeforeClass(groups={"setup"})
-    public void setup() {
-        //loadSystemPropertiesFromClasspath("/account.properties");
-        final Injector g = getInjector();
+            final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
 
-        dao = g.getInstance(IAccountDao.class);
+            injectorMagic = injector.getInstance(InjectorMagic.class);
+            dao = injector.getInstance(IAccountDao.class);
+            dao.test();
+        }
+        catch (Throwable t) {
+            fail(t.toString());
+        }
     }
 
-    @Test(enabled=true, groups={"sql"})
+    @Test(enabled=true, groups={"Account-DAO"})
     public void testBasic() {
 
-        IAccount a = new Account("foo");
+        IAccount a = new Account().withKey("foo");
         dao.createAccount(a);
 
         IAccount r = dao.getAccountByKey("foo");
         assertNotNull(r);
         assertEquals(r.getKey(), a.getKey());
 
-        r = dao.getAccountFromId(r.getId());
+        r = dao.getAccountById(r.getId());
         assertNotNull(r);
         assertEquals(r.getKey(), a.getKey());
 
@@ -83,5 +76,35 @@ public class TestSimpleAccountDao {
         assertTrue(all.size() >= 1);
     }
 
+    @Test
+    public void testGetById() {
+        String key = "test1234";
+
+        IAccount account = Account.create().withKey(key);
+        UUID id = account.getId();
+
+        account.save();
+
+        account = Account.loadAccount(id);
+        assertNotNull(account);
+        assertEquals(account.getId(), id);
+        assertEquals(account.getKey(), key);
+    }
+
+    @Test
+    public void testCustomFields() {
+        String key = "test45678";
+        IAccount account = Account.create().withKey(key);
+
+        String fieldName = "testField1";
+        String fieldValue = "testField1_value";
+        account.setFieldValue(fieldName, fieldValue);
 
+        account.save();
+
+        account = Account.loadAccount(key);
+        assertNotNull(account);
+        assertEquals(account.getKey(), key);
+        assertEquals(account.getFieldValue(fieldName), fieldValue);
+    }
 }
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleMock.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleMock.java
index 422a159..7f92ad2 100644
--- a/account/src/test/java/com/ning/billing/account/glue/AccountModuleMock.java
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleMock.java
@@ -16,24 +16,22 @@
 
 package com.ning.billing.account.glue;
 
-import org.skife.config.ConfigurationObjectFactory;
-import org.skife.jdbi.v2.DBI;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import org.skife.jdbi.v2.IDBI;
 
-import com.ning.billing.dbi.DBIProvider;
-import com.ning.billing.dbi.DbiConfig;
+import java.io.IOException;
 
 public class AccountModuleMock extends AccountModule {
+    private final MysqlTestingHelper helper = new MysqlTestingHelper();
 
-    protected void installDBI() {
-        bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
-        final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
-        bind(DbiConfig.class).toInstance(config);
+    public void createDb(String ddl) throws IOException {
+        helper.startMysql();
+        helper.initDb(ddl);
     }
 
     @Override
     protected void configure() {
-        installDBI();
+        bind(IDBI.class).toInstance(helper.getDBI());
         super.configure();
-
     }
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccount.java b/api/src/main/java/com/ning/billing/account/api/IAccount.java
index 3af1bcf..41908bb 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccount.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccount.java
@@ -16,19 +16,16 @@
 
 package com.ning.billing.account.api;
 
-import com.ning.billing.catalog.api.Currency;
-
 import java.util.UUID;
 
 public interface IAccount extends IAccountData {
-
     public UUID getId();
 
-    public int getBillCycleDay();
+    public void load();
 
-    public Currency getCurrency();
+    public void save();
 
-    public void setPrivate(String name, String value);
+    public String getFieldValue(String fieldName);
 
-    public String getPrivate(String name);
+    public void setFieldValue(String fieldName, String fieldValue);
 }
diff --git a/api/src/main/java/com/ning/billing/account/api/IAccountData.java b/api/src/main/java/com/ning/billing/account/api/IAccountData.java
index 7565964..5e2104d 100644
--- a/api/src/main/java/com/ning/billing/account/api/IAccountData.java
+++ b/api/src/main/java/com/ning/billing/account/api/IAccountData.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.account.api;
 
+import com.ning.billing.catalog.api.Currency;
+
 public interface IAccountData {
 
     public String getKey();
@@ -25,4 +27,8 @@ public interface IAccountData {
     public String getEmail();
 
     public String getPhone();
+
+    public int getBillCycleDay();
+
+    public Currency getCurrency();
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index 0ee8570..d97756f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -16,42 +16,10 @@
 
 package com.ning.billing.entitlement.api.user;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.assertFalse;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeSuite;
-import org.testng.annotations.BeforeTest;
-
 import com.google.inject.Injector;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.catalog.CatalogService;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.ICatalog;
-import com.ning.billing.catalog.api.ICatalogService;
-import com.ning.billing.catalog.api.IDuration;
-import com.ning.billing.catalog.api.TimeUnit;
+import com.ning.billing.catalog.api.*;
 import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.api.ApiTestListener;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
@@ -68,6 +36,23 @@ import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.lifecycle.IService.ServiceException;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.clock.IClock;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static org.testng.Assert.*;
 
 public abstract class TestUserApiBase {
 
@@ -309,17 +294,25 @@ public abstract class TestUserApiBase {
             public Currency getCurrency() {
                 return Currency.USD;
             }
+
             @Override
-            public void setPrivate(String name, String value) {
+            public UUID getId() {
+                return UUID.randomUUID();
             }
+
             @Override
-            public String getPrivate(String name) {
+            public void load() {}
+
+            @Override
+            public void save() {}
+
+            @Override
+            public String getFieldValue(String fieldName) {
                 return null;
             }
+
             @Override
-            public UUID getId() {
-                return UUID.randomUUID();
-            }
+            public void setFieldValue(String fieldName, String fieldValue) {}
         };
         return account;
     }