diff --git a/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java b/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java
index 45362237f01e714f25b16d0378960bd4729e47de..36ab65fa605b0fa1e9fef89cc14307326907e851 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java
@@ -1,6 +1,7 @@
 package de.deadlocker8.budgetmaster.charts;
 
 import com.google.gson.annotations.Expose;
+import de.deadlocker8.budgetmaster.utils.ProvidesID;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -10,7 +11,7 @@ import javax.validation.constraints.Size;
 import java.util.Objects;
 
 @Entity
-public class Chart
+public class Chart implements ProvidesID
 {
 	@Id
 	@Expose
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a010dd42249b4cafb56801bd1ecb49ef4f1afdd5
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java
@@ -0,0 +1,79 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.accounts.Account;
+import de.deadlocker8.budgetmaster.accounts.AccountRepository;
+import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch;
+import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList;
+import de.deadlocker8.budgetmaster.icon.Icon;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountImporter extends ItemImporter<Account>
+{
+	private static final Logger LOGGER = LoggerFactory.getLogger(AccountImporter.class);
+
+	public AccountImporter(AccountRepository accountRepository)
+	{
+		super(accountRepository, EntityType.ACCOUNT, false);
+	}
+
+	public ImportResultItem importItems(List<Account> accounts, AccountMatchList accountMatchList)
+	{
+		LOGGER.debug(MessageFormat.format("Importing {0} {1}...", accountMatchList.getAccountMatches().size(), entityType.getAllItemsName()));
+		final List<String> collectedErrorMessages = new ArrayList<>();
+		int numberOfImportedItems = 0;
+
+		for(AccountMatch accountMatch : accountMatchList.getAccountMatches())
+		{
+			LOGGER.debug(MessageFormat.format("Importing account {0} -> {1}", accountMatch.getAccountSource().getName(), accountMatch.getAccountDestination().getName()));
+
+			try
+			{
+				final Account sourceAccount = accounts.stream()
+						.filter(account -> account.getID().equals(accountMatch.getAccountSource().getID()))
+						.findFirst()
+						.orElseThrow();
+
+				final Account destinationAccount = repository.findById(accountMatch.getAccountDestination().getID()).orElseThrow();
+
+				final Icon sourceIcon = sourceAccount.getIconReference();
+				if(sourceIcon != null)
+				{
+					LOGGER.debug("Overwriting destination account icon");
+					destinationAccount.setIconReference(sourceIcon);
+					repository.save(destinationAccount);
+				}
+
+				sourceAccount.setID(destinationAccount.getID());
+				numberOfImportedItems++;
+			}
+			catch(Exception e)
+			{
+				final String errorMessage = MessageFormat.format("Error while importing {0} \"{1}\"", entityType.getSingleItemName(), getNameForItem(accountMatch.getAccountSource()));
+				LOGGER.error(errorMessage, e);
+				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
+			}
+		}
+
+		LOGGER.debug(MessageFormat.format("Importing accounts DONE ({0}/{1})", numberOfImportedItems, accountMatchList.getAccountMatches().size()));
+		return new ImportResultItem(entityType, numberOfImportedItems, accountMatchList.getAccountMatches().size(), collectedErrorMessages);
+	}
+
+	@Override
+	protected int importSingleItem(Account account)
+	{
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	protected String getNameForItem(Account item)
+	{
+		return item.getName();
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e1ef2e6b5fc94dfa1471afdca3d58ad958896cf
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java
@@ -0,0 +1,67 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.categories.Category;
+import de.deadlocker8.budgetmaster.categories.CategoryRepository;
+import de.deadlocker8.budgetmaster.categories.CategoryType;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.MessageFormat;
+
+public class CategoryImporter extends ItemImporter<Category>
+{
+	private static final Logger LOGGER = LoggerFactory.getLogger(CategoryImporter.class);
+
+	public CategoryImporter(CategoryRepository categoryRepository)
+	{
+		super(categoryRepository, EntityType.CATEGORY, false);
+	}
+
+	@Override
+	protected int importSingleItem(Category category)
+	{
+		if(!(repository instanceof CategoryRepository repository))
+		{
+			throw new IllegalArgumentException("Invalid repository type");
+		}
+
+		LOGGER.debug(MessageFormat.format("Importing category {0}", category.getName()));
+
+		Category existingCategory;
+		if(category.getType().equals(CategoryType.NONE) || category.getType().equals(CategoryType.REST))
+		{
+			existingCategory = repository.findByType(category.getType());
+		}
+		else
+		{
+			existingCategory = repository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType());
+		}
+
+		int newCategoryID;
+		if(existingCategory == null)
+		{
+			//category does not exist --> create it
+			LOGGER.debug(MessageFormat.format("No matching category found for category \"{0}\". Creating new one...", category.getName()));
+
+			Category categoryToCreate = new Category(category.getName(), category.getColor(), category.getType(), category.getIconReference());
+			repository.save(categoryToCreate);
+
+			Category newCategory = repository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType());
+			newCategoryID = newCategory.getID();
+		}
+		else
+		{
+			//category already exists
+			newCategoryID = existingCategory.getID();
+			LOGGER.debug(MessageFormat.format("Found matching category with ID: {0} for category \"{1}\".", newCategoryID, category.getName()));
+		}
+		return newCategoryID;
+	}
+
+	@Override
+	protected String getNameForItem(Category item)
+	{
+		return item.getName();
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6be82d801128fd7654f202c05a394ad5ce16671e
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java
@@ -0,0 +1,39 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.charts.Chart;
+import de.deadlocker8.budgetmaster.charts.ChartRepository;
+import de.deadlocker8.budgetmaster.charts.ChartService;
+import de.deadlocker8.budgetmaster.services.EntityType;
+
+public class ChartImporter extends ItemImporter<Chart>
+{
+	private final ChartService chartService;
+
+	public ChartImporter(ChartService chartService)
+	{
+		super(chartService.getRepository(), EntityType.CHART, true);
+		this.chartService = chartService;
+	}
+
+	@Override
+	protected int importSingleItem(Chart chart)
+	{
+		if(!(repository instanceof ChartRepository repository))
+		{
+			throw new IllegalArgumentException("Invalid repository type");
+		}
+
+		final int highestUsedID = chartService.getHighestUsedID();
+		chart.setID(highestUsedID + 1);
+
+		repository.save(chart);
+
+		return chart.getID();
+	}
+
+	@Override
+	protected String getNameForItem(Chart item)
+	{
+		return item.getName();
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9b818147850e1bc44e896e520422d618fc4d592
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java
@@ -0,0 +1,36 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.icon.Icon;
+import de.deadlocker8.budgetmaster.icon.IconRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+
+public class IconImporter extends ItemImporter<Icon>
+{
+	public IconImporter(IconRepository iconRepository)
+	{
+		super(iconRepository, EntityType.ICON, true);
+	}
+
+	@Override
+	protected int importSingleItem(Icon icon)
+	{
+		if(!(repository instanceof IconRepository repository))
+		{
+			throw new IllegalArgumentException("Invalid repository type");
+		}
+
+		final Icon iconToCreate = new Icon();
+		iconToCreate.setImage(icon.getImage());
+		iconToCreate.setBuiltinIdentifier(icon.getBuiltinIdentifier());
+
+		final Icon savedIcon = repository.save(iconToCreate);
+
+		return savedIcon.getID();
+	}
+
+	@Override
+	protected String getNameForItem(Icon item)
+	{
+		return String.valueOf(item.getID());
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ccce6413253917d1498930df8126aaf0c8346dc2
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java
@@ -0,0 +1,33 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.images.Image;
+import de.deadlocker8.budgetmaster.images.ImageRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+
+public class ImageImporter extends ItemImporter<Image>
+{
+	public ImageImporter(ImageRepository imageRepository)
+	{
+		super(imageRepository, EntityType.IMAGE, true);
+	}
+
+	@Override
+	protected int importSingleItem(Image image)
+	{
+		if(!(repository instanceof ImageRepository repository))
+		{
+			throw new IllegalArgumentException("Invalid repository type");
+		}
+
+		Image imageToCreate = new Image(image.getImage(), image.getFileName(), image.getFileExtension());
+
+		final Image savedImage = repository.save(imageToCreate);
+		return savedImage.getID();
+	}
+
+	@Override
+	protected String getNameForItem(Image item)
+	{
+		return String.valueOf(item.getID());
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4059f1aeef8578120633bb31879999908d7fac9
--- /dev/null
+++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java
@@ -0,0 +1,74 @@
+package de.deadlocker8.budgetmaster.database.importer;
+
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import de.deadlocker8.budgetmaster.utils.ProvidesID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ItemImporter<T extends ProvidesID>
+{
+	private static final Logger LOGGER = LoggerFactory.getLogger(ItemImporter.class);
+
+	protected final JpaRepository<T, Integer> repository;
+	protected final EntityType entityType;
+	protected final boolean alwaysCreateNewInstance;
+
+	protected ItemImporter(JpaRepository<T, Integer> repository, EntityType entityType, boolean alwaysCreateNewInstance)
+	{
+		this.repository = repository;
+		this.entityType = entityType;
+		this.alwaysCreateNewInstance = alwaysCreateNewInstance;
+	}
+
+	public ImportResultItem importItems(List<T> items)
+	{
+		LOGGER.debug(MessageFormat.format("Importing {0} {1}...", items.size(), entityType.getAllItemsName()));
+		final List<String> collectedErrorMessages = new ArrayList<>();
+		int numberOfImportedItems = 0;
+
+		for(int i = 0; i < items.size(); i++)
+		{
+			T item = items.get(i);
+			LOGGER.debug(MessageFormat.format("Importing {0} {1}/{2} (ID: {3})", entityType.getSingleItemName(), i + 1, items.size(), item.getID()));
+
+			try
+			{
+				int oldID = item.getID();
+				int newID = importSingleItem(item);
+
+				if(oldID == newID && !alwaysCreateNewInstance)
+				{
+					numberOfImportedItems++;
+					continue;
+				}
+
+				item.setID(newID);
+				numberOfImportedItems++;
+			}
+			catch(Exception e)
+			{
+				final String errorMessage = MessageFormat.format("Error while importing {0} \"{1}\"", entityType.getSingleItemName(), getNameForItem(item));
+				LOGGER.error(errorMessage, e);
+				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
+			}
+		}
+
+		LOGGER.debug(MessageFormat.format("Importing {0} DONE ({1}/{2})", entityType.getAllItemsName(), numberOfImportedItems, items.size()));
+		return new ImportResultItem(entityType, numberOfImportedItems, items.size(), collectedErrorMessages);
+	}
+
+	protected abstract int importSingleItem(T item);
+
+	protected abstract String getNameForItem(T item);
+
+	protected String formatErrorMessage(String errorMessage, Exception e)
+	{
+		return MessageFormat.format("{0}: {1} ({2})", errorMessage, e.getClass().getName(), e.getMessage());
+	}
+}
diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java b/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java
index ee0a10e30fc6eabb8a441c69fc7db4e2c3de97fc..8947051c050eaa082ab23118dabb912558142b3d 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java
@@ -4,20 +4,21 @@ import de.deadlocker8.budgetmaster.utils.LocalizedEnum;
 
 public enum EntityType implements LocalizedEnum
 {
-	HOME("home", "background-blue", ImportRequired.NONE),
-	ACCOUNT("account_balance", "background-red", ImportRequired.REQUIRED),
-	TRANSACTION("list", "background-blue-baby", ImportRequired.REQUIRED),
-	TEMPLATE("file_copy", "background-orange-dark", ImportRequired.OPTIONAL),
-	CHART("show_chart", "background-purple", ImportRequired.OPTIONAL),
-	REPORT("description", "background-green", ImportRequired.NONE),
-	CATEGORY("label", "background-orange", ImportRequired.REQUIRED),
-	TAGS("local_offer", "background-grey", ImportRequired.NONE),
-	STATISTICS("insert_chart", "background-grey", ImportRequired.NONE),
-	SETTINGS("settings", "background-red", ImportRequired.NONE),
-	IMAGE("image", "background-grey", ImportRequired.REQUIRED),
-	HOTKEYS("keyboard", "background-grey", ImportRequired.NONE),
-	ABOUT("info", "background-grey", ImportRequired.NONE),
-	TEMPLATE_GROUP("folder", "background-orange-dark", ImportRequired.OPTIONAL);
+	HOME("home", "background-blue", ImportRequired.NONE, "categories", "category"),
+	ACCOUNT("account_balance", "background-red", ImportRequired.REQUIRED, "accounts", "account"),
+	TRANSACTION("list", "background-blue-baby", ImportRequired.REQUIRED, "transactions", "transaction"),
+	TEMPLATE("file_copy", "background-orange-dark", ImportRequired.OPTIONAL, "templates", "template"),
+	CHART("show_chart", "background-purple", ImportRequired.OPTIONAL, "charts", "chart"),
+	REPORT("description", "background-green", ImportRequired.NONE, null, null),
+	CATEGORY("label", "background-orange", ImportRequired.REQUIRED, "categories", "category"),
+	TAGS("local_offer", "background-grey", ImportRequired.NONE, null, null),
+	STATISTICS("insert_chart", "background-grey", ImportRequired.NONE, null, null),
+	SETTINGS("settings", "background-red", ImportRequired.NONE, null, null),
+	IMAGE("image", "background-grey", ImportRequired.REQUIRED, "images", "image"),
+	HOTKEYS("keyboard", "background-grey", ImportRequired.NONE, null, null),
+	ABOUT("info", "background-grey", ImportRequired.NONE, null, null),
+	TEMPLATE_GROUP("folder", "background-orange-dark", ImportRequired.OPTIONAL, "template groups", "template group"),
+	ICON("icon", "background-grey", ImportRequired.NONE, "icons", "icon");
 
 
 	public enum ImportRequired
@@ -30,12 +31,16 @@ public enum EntityType implements LocalizedEnum
 	private final String icon;
 	private final String color;
 	private final ImportRequired importRequired;
+	private final String allItemsName;
+	private final String singleItemName;
 
-	EntityType(String icon, String color, ImportRequired importRequired)
+	EntityType(String icon, String color, ImportRequired importRequired, String allItemsName, String singleItemName)
 	{
 		this.icon = icon;
 		this.color = color;
 		this.importRequired = importRequired;
+		this.allItemsName = allItemsName;
+		this.singleItemName = singleItemName;
 	}
 
 	public String getIcon()
@@ -53,6 +58,16 @@ public enum EntityType implements LocalizedEnum
 		return importRequired;
 	}
 
+	public String getAllItemsName()
+	{
+		return allItemsName;
+	}
+
+	public String getSingleItemName()
+	{
+		return singleItemName;
+	}
+
 	public String getColorAsTextColor()
 	{
 		return color.replace("background", "text");
diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java b/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java
index 37b60483228c2d7093f902123e7a4ca3e9090e11..d766c489148c09af5d9d68241630c382ac1f7268 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java
@@ -1,5 +1,6 @@
 package de.deadlocker8.budgetmaster.services;
 
+import java.util.List;
 import java.util.Objects;
 
 public class ImportResultItem
@@ -7,12 +8,15 @@ public class ImportResultItem
 	private final EntityType entityType;
 	private final int numberOfImportedItems;
 	private final int numberOfAvailableItems;
+	private final List<String> collectedErrorMessages;
 
-	public ImportResultItem(EntityType entityType, int numberOfImportedItems, int numberOfAvailableItems)
+
+	public ImportResultItem(EntityType entityType, int numberOfImportedItems, int numberOfAvailableItems, List<String> collectedErrorMessages)
 	{
 		this.entityType = entityType;
 		this.numberOfImportedItems = numberOfImportedItems;
 		this.numberOfAvailableItems = numberOfAvailableItems;
+		this.collectedErrorMessages = collectedErrorMessages;
 	}
 
 	public EntityType getEntityType()
@@ -30,19 +34,25 @@ public class ImportResultItem
 		return numberOfAvailableItems;
 	}
 
+	public List<String> getCollectedErrorMessages()
+	{
+		return collectedErrorMessages;
+	}
+
 	@Override
 	public boolean equals(Object o)
 	{
+
 		if(this == o) return true;
 		if(o == null || getClass() != o.getClass()) return false;
 		ImportResultItem that = (ImportResultItem) o;
-		return numberOfImportedItems == that.numberOfImportedItems && numberOfAvailableItems == that.numberOfAvailableItems && entityType == that.entityType;
+		return numberOfImportedItems == that.numberOfImportedItems && numberOfAvailableItems == that.numberOfAvailableItems && entityType == that.entityType && Objects.equals(collectedErrorMessages, that.collectedErrorMessages);
 	}
 
 	@Override
 	public int hashCode()
 	{
-		return Objects.hash(entityType, numberOfImportedItems, numberOfAvailableItems);
+		return Objects.hash(entityType, numberOfImportedItems, numberOfAvailableItems, collectedErrorMessages);
 	}
 
 	@Override
@@ -52,6 +62,7 @@ public class ImportResultItem
 				"entityType=" + entityType +
 				", numberOfImportedItems=" + numberOfImportedItems +
 				", numberOfAvailableItems=" + numberOfAvailableItems +
+				", collectedErrorMessages=" + collectedErrorMessages +
 				'}';
 	}
 }
diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java
index 3e723c17a910163d6f84f46606c0e810d65493a5..1dad5b5ae5125a9bfcee8560ecb741ff930051f1 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java
@@ -2,18 +2,19 @@ package de.deadlocker8.budgetmaster.services;
 
 import de.deadlocker8.budgetmaster.accounts.Account;
 import de.deadlocker8.budgetmaster.accounts.AccountRepository;
-import de.deadlocker8.budgetmaster.categories.Category;
 import de.deadlocker8.budgetmaster.categories.CategoryRepository;
-import de.deadlocker8.budgetmaster.categories.CategoryType;
 import de.deadlocker8.budgetmaster.charts.Chart;
 import de.deadlocker8.budgetmaster.charts.ChartService;
 import de.deadlocker8.budgetmaster.database.InternalDatabase;
 import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch;
 import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList;
+import de.deadlocker8.budgetmaster.database.importer.*;
 import de.deadlocker8.budgetmaster.icon.Icon;
+import de.deadlocker8.budgetmaster.icon.IconRepository;
 import de.deadlocker8.budgetmaster.icon.IconService;
 import de.deadlocker8.budgetmaster.icon.Iconizable;
 import de.deadlocker8.budgetmaster.images.Image;
+import de.deadlocker8.budgetmaster.images.ImageRepository;
 import de.deadlocker8.budgetmaster.images.ImageService;
 import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater;
 import de.deadlocker8.budgetmaster.tags.Tag;
@@ -48,10 +49,10 @@ public class ImportService
 	private final TemplateRepository templateRepository;
 	private final TagRepository tagRepository;
 	private final ChartService chartService;
-	private final ImageService imageService;
+	private final ImageRepository imageRepository;
 	private final RepeatingTransactionUpdater repeatingTransactionUpdater;
 	private final AccountRepository accountRepository;
-	private final IconService iconService;
+	private final IconRepository iconRepository;
 
 
 	private InternalDatabase database;
@@ -59,7 +60,7 @@ public class ImportService
 
 	@Autowired
 	public ImportService(CategoryRepository categoryRepository, TransactionRepository transactionRepository, TemplateGroupRepository templateGroupRepository, TemplateRepository templateRepository,
-						 TagRepository tagRepository, ChartService chartService, ImageService imageService, RepeatingTransactionUpdater repeatingTransactionUpdater, AccountRepository accountRepository, IconService iconService)
+						 TagRepository tagRepository, ChartService chartService, ImageRepository imageRepository, RepeatingTransactionUpdater repeatingTransactionUpdater, AccountRepository accountRepository, IconRepository iconRepository)
 	{
 		this.categoryRepository = categoryRepository;
 		this.transactionRepository = transactionRepository;
@@ -67,10 +68,10 @@ public class ImportService
 		this.templateRepository = templateRepository;
 		this.tagRepository = tagRepository;
 		this.chartService = chartService;
-		this.imageService = imageService;
+		this.imageRepository = imageRepository;
 		this.repeatingTransactionUpdater = repeatingTransactionUpdater;
 		this.accountRepository = accountRepository;
-		this.iconService = iconService;
+		this.iconRepository = iconRepository;
 	}
 
 	public List<ImportResultItem> importDatabase(InternalDatabase database, AccountMatchList accountMatchList, Boolean importTemplateGroups, Boolean importTemplates, Boolean importCharts)
@@ -81,10 +82,10 @@ public class ImportService
 		final List<ImportResultItem> importResultItems = new ArrayList<>();
 
 		LOGGER.debug("Importing database...");
-		importResultItems.add(importImages());
-		importIcons();
-		importResultItems.add(importCategories());
-		importResultItems.add(importAccounts(accountMatchList));
+		importResultItems.add(new ImageImporter(imageRepository).importItems(database.getImages()));
+		new IconImporter(iconRepository).importItems(database.getIcons());
+		importResultItems.add(new CategoryImporter(categoryRepository).importItems(database.getCategories()));
+		importResultItems.add(new AccountImporter(accountRepository).importItems(database.getAccounts(), accountMatchList));
 		importResultItems.add(importTransactions());
 
 		if(importTemplateGroups)
@@ -93,7 +94,7 @@ public class ImportService
 		}
 		else
 		{
-			importResultItems.add(new ImportResultItem(EntityType.TEMPLATE_GROUP, 0, 0));
+			importResultItems.add(new ImportResultItem(EntityType.TEMPLATE_GROUP, 0, 0, collectedErrorMessages));
 		}
 
 		if(importTemplates)
@@ -102,16 +103,16 @@ public class ImportService
 		}
 		else
 		{
-			importResultItems.add(new ImportResultItem(EntityType.TEMPLATE, 0, 0));
+			importResultItems.add(new ImportResultItem(EntityType.TEMPLATE, 0, 0, collectedErrorMessages));
 		}
 
 		if(importCharts)
 		{
-			importResultItems.add(importCharts());
+			importResultItems.add(new ChartImporter(chartService).importItems(database.getCharts()));
 		}
 		else
 		{
-			importResultItems.add(new ImportResultItem(EntityType.CHART, 0, 0));
+			importResultItems.add(new ImportResultItem(EntityType.CHART, 0, 0, collectedErrorMessages));
 		}
 
 		LOGGER.debug("Updating repeating transactions...");
@@ -136,209 +137,6 @@ public class ImportService
 		return MessageFormat.format("{0}: {1} ({2})", errorMessage, e.getClass().getName(), e.getMessage());
 	}
 
-	private ImportResultItem importCategories()
-	{
-		List<Category> categories = database.getCategories();
-		LOGGER.debug(MessageFormat.format("Importing {0} categories...", categories.size()));
-		List<TransactionBase> alreadyUpdatedTransactions = new ArrayList<>();
-		List<TransactionBase> alreadyUpdatedTemplates = new ArrayList<>();
-		int numberOfImportedCategories = 0;
-
-		for(Category category : categories)
-		{
-			LOGGER.debug(MessageFormat.format("Importing category {0}", category.getName()));
-
-			try
-			{
-				int oldCategoryID = category.getID();
-				int newCategoryID = importCategory(category);
-
-				if(oldCategoryID == newCategoryID)
-				{
-					numberOfImportedCategories++;
-					continue;
-				}
-
-				List<TransactionBase> transactions = new ArrayList<>(database.getTransactions());
-				transactions.removeAll(alreadyUpdatedTransactions);
-				alreadyUpdatedTransactions.addAll(updateCategoriesForItems(transactions, oldCategoryID, newCategoryID));
-
-				List<TransactionBase> templates = new ArrayList<>(database.getTemplates());
-				templates.removeAll(alreadyUpdatedTemplates);
-				alreadyUpdatedTemplates.addAll(updateCategoriesForItems(templates, oldCategoryID, newCategoryID));
-
-				numberOfImportedCategories++;
-			}
-			catch(Exception e)
-			{
-				final String errorMessage = MessageFormat.format("Error while importing category with name \"{0}\"", category.getName());
-				LOGGER.error(errorMessage, e);
-				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
-			}
-		}
-
-		LOGGER.debug(MessageFormat.format("Importing categories DONE ({0}/{1})", numberOfImportedCategories, categories.size()));
-		return new ImportResultItem(EntityType.CATEGORY, numberOfImportedCategories, categories.size());
-	}
-
-	private int importCategory(Category category)
-	{
-		Category existingCategory;
-		if(category.getType().equals(CategoryType.NONE) || category.getType().equals(CategoryType.REST))
-		{
-			existingCategory = categoryRepository.findByType(category.getType());
-		}
-		else
-		{
-			existingCategory = categoryRepository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType());
-		}
-
-		int newCategoryID;
-		if(existingCategory == null)
-		{
-			//category does not exist --> create it
-			Category categoryToCreate = new Category(category.getName(), category.getColor(), category.getType(), category.getIconReference());
-			categoryRepository.save(categoryToCreate);
-
-			Category newCategory = categoryRepository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType());
-			newCategoryID = newCategory.getID();
-		}
-		else
-		{
-			//category already exists
-			newCategoryID = existingCategory.getID();
-		}
-		return newCategoryID;
-	}
-
-	public List<TransactionBase> updateCategoriesForItems(List<TransactionBase> items, int oldCategoryID, int newCategoryID)
-	{
-		List<TransactionBase> updatedItems = new ArrayList<>();
-		for(TransactionBase item : items)
-		{
-			final Category category = item.getCategory();
-			if(category == null)
-			{
-				continue;
-			}
-
-			if(category.getID() == oldCategoryID)
-			{
-				category.setID(newCategoryID);
-				updatedItems.add(item);
-			}
-		}
-
-		return updatedItems;
-	}
-
-	private ImportResultItem importAccounts(AccountMatchList accountMatchList)
-	{
-		LOGGER.debug(MessageFormat.format("Importing {0} accounts...", accountMatchList.getAccountMatches().size()));
-		List<TransactionBase> alreadyUpdatedTransactions = new ArrayList<>();
-		List<TransactionBase> alreadyUpdatedTransferTransactions = new ArrayList<>();
-		List<TransactionBase> alreadyUpdatedTemplates = new ArrayList<>();
-		List<TransactionBase> alreadyUpdatedTransferTemplates = new ArrayList<>();
-
-		int numberOfImportedAccounts = 0;
-
-		for(AccountMatch accountMatch : accountMatchList.getAccountMatches())
-		{
-			LOGGER.debug(MessageFormat.format("Importing account {0} -> {1}", accountMatch.getAccountSource().getName(), accountMatch.getAccountDestination().getName()));
-
-			try
-			{
-				Account sourceAccount = database.getAccounts().stream()
-						.filter(account -> account.getID().equals(accountMatch.getAccountSource().getID()))
-						.findFirst()
-						.orElseThrow();
-
-				Account destinationAccount = accountRepository.findById(accountMatch.getAccountDestination().getID()).orElseThrow();
-
-				Icon sourceIcon = sourceAccount.getIconReference();
-				if(sourceIcon != null)
-				{
-					LOGGER.debug("Overwriting destination account icon");
-					destinationAccount.setIconReference(sourceIcon);
-					accountRepository.save(destinationAccount);
-				}
-
-				List<TransactionBase> transactions = new ArrayList<>(database.getTransactions());
-				transactions.removeAll(alreadyUpdatedTransactions);
-				alreadyUpdatedTransactions.addAll(updateAccountsForItems(transactions, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination()));
-
-				List<TransactionBase> transferTransactions = new ArrayList<>(database.getTransactions());
-				transferTransactions.removeAll(alreadyUpdatedTransferTransactions);
-				alreadyUpdatedTransferTransactions.addAll(updateTransferAccountsForItems(transferTransactions, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination()));
-
-				List<TransactionBase> templates = new ArrayList<>(database.getTemplates());
-				templates.removeAll(alreadyUpdatedTemplates);
-				alreadyUpdatedTemplates.addAll(updateAccountsForItems(templates, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination()));
-
-				List<TransactionBase> transferTemplates = new ArrayList<>(database.getTemplates());
-				transferTemplates.removeAll(alreadyUpdatedTransferTemplates);
-				alreadyUpdatedTransferTemplates.addAll(updateTransferAccountsForItems(transferTemplates, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination()));
-
-				numberOfImportedAccounts++;
-			}
-			catch(Exception e)
-			{
-				final String errorMessage = MessageFormat.format("Error while importing account with name \"{0}\"", accountMatch.getAccountSource().getName());
-				LOGGER.error(errorMessage, e);
-				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
-			}
-		}
-
-		LOGGER.debug(MessageFormat.format("Importing accounts DONE ({0}/{1})", numberOfImportedAccounts, accountMatchList.getAccountMatches().size()));
-		return new ImportResultItem(EntityType.ACCOUNT, numberOfImportedAccounts, accountMatchList.getAccountMatches().size());
-	}
-
-	public List<TransactionBase> updateAccountsForItems(List<TransactionBase> items, int oldAccountID, Account newAccount)
-	{
-		List<TransactionBase> updatedTransactions = new ArrayList<>();
-		for(TransactionBase item : items)
-		{
-			// legacy database
-			if(oldAccountID == -1)
-			{
-				item.setAccount(newAccount);
-				updatedTransactions.add(item);
-				continue;
-			}
-
-			if(item.getAccount() == null)
-			{
-				continue;
-			}
-
-			if(item.getAccount().getID() != oldAccountID)
-			{
-				continue;
-			}
-
-			// account needs to be updated
-			item.setAccount(newAccount);
-			updatedTransactions.add(item);
-		}
-
-		return updatedTransactions;
-	}
-
-	public List<TransactionBase> updateTransferAccountsForItems(List<TransactionBase> transactions, int oldAccountID, Account newAccount)
-	{
-		List<TransactionBase> updatedTransactions = new ArrayList<>();
-		for(TransactionBase transaction : transactions)
-		{
-			if(transaction.getTransferAccount() != null && transaction.getTransferAccount().getID() == oldAccountID)
-			{
-				transaction.setTransferAccount(newAccount);
-				updatedTransactions.add(transaction);
-			}
-		}
-
-		return updatedTransactions;
-	}
-
 	private ImportResultItem importTransactions()
 	{
 		List<Transaction> transactions = database.getTransactions();
@@ -367,7 +165,7 @@ public class ImportService
 		}
 
 		LOGGER.debug(MessageFormat.format("Importing transactions DONE ({0}/{1})", numberOfImportedTransactions, transactions.size()));
-		return new ImportResultItem(EntityType.TRANSACTION, numberOfImportedTransactions, transactions.size());
+		return new ImportResultItem(EntityType.TRANSACTION, numberOfImportedTransactions, transactions.size(), collectedErrorMessages);
 	}
 
 	public void updateTagsForItem(TransactionBase item)
@@ -426,7 +224,7 @@ public class ImportService
 		}
 
 		LOGGER.debug(MessageFormat.format("Importing template groups DONE ({0}/{1})", numberOfImportedTemplateGroups, templateGroups.size()));
-		return new ImportResultItem(EntityType.TEMPLATE_GROUP, numberOfImportedTemplateGroups, templateGroups.size());
+		return new ImportResultItem(EntityType.TEMPLATE_GROUP, numberOfImportedTemplateGroups, templateGroups.size(), collectedErrorMessages);
 	}
 
 	private int importTemplateGroup(TemplateGroup templateGroup)
@@ -513,173 +311,6 @@ public class ImportService
 		}
 
 		LOGGER.debug(MessageFormat.format("Importing templates DONE ({0}/{1})", numberOfImportedTemplates, templates.size()));
-		return new ImportResultItem(EntityType.TEMPLATE, numberOfImportedTemplates, templates.size());
-	}
-
-	private ImportResultItem importCharts()
-	{
-		List<Chart> charts = database.getCharts();
-		LOGGER.debug(MessageFormat.format("Importing {0} charts...", charts.size()));
-
-		int numberOfImportedCharts = 0;
-
-		for(int i = 0; i < charts.size(); i++)
-		{
-			Chart chart = charts.get(i);
-			try
-			{
-				LOGGER.debug(MessageFormat.format("Importing chart {0}/{1} (name: {2})", i + 1, charts.size(), chart.getName()));
-
-				final int highestUsedID = chartService.getHighestUsedID();
-				chart.setID(highestUsedID + 1);
-
-				chartService.getRepository().save(chart);
-
-				numberOfImportedCharts++;
-			}
-			catch(Exception e)
-			{
-				final String errorMessage = MessageFormat.format("Error while importing chart with name \"{0}\"", chart.getName());
-				LOGGER.error(errorMessage, e);
-				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
-			}
-		}
-
-		LOGGER.debug(MessageFormat.format("Importing charts DONE ({0}/{1})", numberOfImportedCharts, charts.size()));
-		return new ImportResultItem(EntityType.CHART, numberOfImportedCharts, charts.size());
-
-	}
-
-	private ImportResultItem importImages()
-	{
-		List<Image> images = database.getImages();
-		LOGGER.debug(MessageFormat.format("Importing {0} images...", images.size()));
-		List<Icon> alreadyUpdatedIcons = new ArrayList<>();
-
-		int numberOfImportedImages = 0;
-
-		for(int i = 0; i < images.size(); i++)
-		{
-			Image image = images.get(i);
-			try
-			{
-				LOGGER.debug(MessageFormat.format("Importing image {0}/{1} (ID: {2})", i + 1, images.size(), image.getID()));
-
-				// always create new image
-				int oldImageID = image.getID();
-				Image imageToCreate = new Image(image.getImage(), image.getFileName(), image.getFileExtension());
-
-				final Image savedImage = imageService.getRepository().save(imageToCreate);
-				int newImageID = savedImage.getID();
-
-				List<Icon> icons = new ArrayList<>(database.getIcons());
-				icons.removeAll(alreadyUpdatedIcons);
-				alreadyUpdatedIcons.addAll(updateImagesForIcons(icons, oldImageID, newImageID));
-
-				numberOfImportedImages++;
-			}
-			catch(Exception e)
-			{
-				final String errorMessage = MessageFormat.format("Error while importing image with ID {0}", image.getID());
-				LOGGER.error(errorMessage, e);
-				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
-			}
-		}
-
-		LOGGER.debug(MessageFormat.format("Importing images DONE ({0}/{1})", numberOfImportedImages, images.size()));
-		return new ImportResultItem(EntityType.IMAGE, numberOfImportedImages, images.size());
-	}
-
-	public List<Icon> updateImagesForIcons(List<Icon> items, int oldImageId, int newImageID)
-	{
-		List<Icon> updatedItems = new ArrayList<>();
-		for(Icon item : items)
-		{
-			final Image image = item.getImage();
-			if(image == null)
-			{
-				continue;
-			}
-
-			if(image.getID() == oldImageId)
-			{
-				image.setID(newImageID);
-				updatedItems.add(item);
-			}
-		}
-
-		return updatedItems;
-	}
-
-	private void importIcons()
-	{
-		List<Icon> icons = database.getIcons();
-		LOGGER.debug(MessageFormat.format("Importing {0} icons...", icons.size()));
-		List<Iconizable> alreadyUpdatedAccounts = new ArrayList<>();
-		List<Iconizable> alreadyUpdatedTemplates = new ArrayList<>();
-		List<Iconizable> alreadyUpdatedCategories = new ArrayList<>();
-
-		int numberOfImportedIcons = 0;
-
-		for(int i = 0; i < icons.size(); i++)
-		{
-			Icon icon = icons.get(i);
-			try
-			{
-				LOGGER.debug(MessageFormat.format("Importing icon {0}/{1} (ID: {2})", i + 1, icons.size(), icon.getID()));
-
-				// always create new icon
-				int oldIconID = icon.getID();
-				Icon iconToCreate = new Icon();
-				iconToCreate.setImage(icon.getImage());
-				iconToCreate.setBuiltinIdentifier(icon.getBuiltinIdentifier());
-
-				final Icon savedIcon = iconService.getRepository().save(iconToCreate);
-				int newIconID = savedIcon.getID();
-
-				List<Iconizable> accounts = new ArrayList<>(database.getAccounts());
-				accounts.removeAll(alreadyUpdatedAccounts);
-				alreadyUpdatedAccounts.addAll(updateIconsForItems(accounts, oldIconID, newIconID));
-
-				List<Iconizable> templates = new ArrayList<>(database.getTemplates());
-				templates.removeAll(alreadyUpdatedTemplates);
-				alreadyUpdatedTemplates.addAll(updateIconsForItems(templates, oldIconID, newIconID));
-
-				List<Iconizable> categories = new ArrayList<>(database.getCategories());
-				categories.removeAll(alreadyUpdatedCategories);
-				alreadyUpdatedCategories.addAll(updateIconsForItems(categories, oldIconID, newIconID));
-
-				numberOfImportedIcons++;
-			}
-			catch(Exception e)
-			{
-				final String errorMessage = MessageFormat.format("Error while importing icon with ID {0}", icon.getID());
-				LOGGER.error(errorMessage, e);
-				collectedErrorMessages.add(formatErrorMessage(errorMessage, e));
-			}
-		}
-
-		LOGGER.debug(MessageFormat.format("Importing icon DONE ({0}/{1})", numberOfImportedIcons, icons.size()));
-	}
-
-	public List<Iconizable> updateIconsForItems(List<? extends Iconizable> items, int oldIconID, int newIconID)
-	{
-		List<Iconizable> updatedItems = new ArrayList<>();
-		for(Iconizable item : items)
-		{
-			final Icon iconReference = item.getIconReference();
-			if(iconReference == null)
-			{
-				continue;
-			}
-
-			if(iconReference.getID() == oldIconID)
-			{
-				iconReference.setID(newIconID);
-				updatedItems.add(item);
-			}
-		}
-
-		return updatedItems;
+		return new ImportResultItem(EntityType.TEMPLATE, numberOfImportedTemplates, templates.size(), collectedErrorMessages);
 	}
 }
diff --git a/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java b/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java
index 783961088ae17a25321a68fbc4b99717609d88ab..6f9853f7694b66c89ca10cc1a92b25113ff63a34 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java
@@ -3,4 +3,6 @@ package de.deadlocker8.budgetmaster.utils;
 public interface ProvidesID
 {
 	Integer getID();
+
+	void setID(Integer ID);
 }
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java
index af645034eaf6843618f31c247453634dc39e6578..f74b0da6d31499cf836d58b7f3b42ea3590c9b8d 100644
--- a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java
@@ -16,14 +16,11 @@ import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList;
 import de.deadlocker8.budgetmaster.icon.Icon;
 import de.deadlocker8.budgetmaster.icon.IconRepository;
 import de.deadlocker8.budgetmaster.icon.IconService;
-import de.deadlocker8.budgetmaster.icon.Iconizable;
 import de.deadlocker8.budgetmaster.images.Image;
 import de.deadlocker8.budgetmaster.images.ImageFileExtension;
 import de.deadlocker8.budgetmaster.images.ImageRepository;
 import de.deadlocker8.budgetmaster.images.ImageService;
 import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater;
-import de.deadlocker8.budgetmaster.services.EntityType;
-import de.deadlocker8.budgetmaster.services.ImportResultItem;
 import de.deadlocker8.budgetmaster.services.ImportService;
 import de.deadlocker8.budgetmaster.tags.Tag;
 import de.deadlocker8.budgetmaster.tags.TagRepository;
@@ -33,7 +30,6 @@ import de.deadlocker8.budgetmaster.templategroup.TemplateGroupType;
 import de.deadlocker8.budgetmaster.templates.Template;
 import de.deadlocker8.budgetmaster.templates.TemplateRepository;
 import de.deadlocker8.budgetmaster.transactions.Transaction;
-import de.deadlocker8.budgetmaster.transactions.TransactionBase;
 import de.deadlocker8.budgetmaster.transactions.TransactionRepository;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -85,235 +81,6 @@ class ImportServiceTest
 	@InjectMocks
 	private ImportService importService;
 
-	@Test
-	void test_updateCategoriesForTransactions()
-	{
-		Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
-		category1.setID(3);
-
-		Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM);
-		category2.setID(4);
-
-		List<TransactionBase> transactionList = new ArrayList<>();
-		Transaction transaction1 = new Transaction();
-		transaction1.setName("Test");
-		transaction1.setAmount(200);
-		transaction1.setDate(LocalDate.of(2018, 10, 3));
-		transaction1.setCategory(category1);
-		transactionList.add(transaction1);
-
-		Transaction transaction2 = new Transaction();
-		transaction2.setName("Test_2");
-		transaction2.setAmount(-525);
-		transaction2.setDate(LocalDate.of(2018, 10, 3));
-		transaction2.setCategory(category2);
-		transactionList.add(transaction2);
-
-		List<TransactionBase> updatedTransactions = importService.updateCategoriesForItems(transactionList, 3, 5);
-		assertThat(updatedTransactions).hasSize(1);
-		assertThat(updatedTransactions.get(0).getCategory()).hasFieldOrPropertyWithValue("ID", 5);
-	}
-
-	@Test
-	void test_updateCategoriesForTemplates()
-	{
-		Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
-		category1.setID(3);
-
-		Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM);
-		category2.setID(4);
-
-		Template template1 = new Template();
-		template1.setTemplateName("MyTemplate");
-		template1.setCategory(category1);
-		template1.setAmount(200);
-		template1.setName("Test");
-		template1.setTags(new ArrayList<>());
-
-		Template template2 = new Template();
-		template2.setTemplateName("MyTemplate_2");
-		template2.setCategory(category2);
-		template2.setAmount(-525);
-		template2.setName("Test_2");
-		template2.setTags(new ArrayList<>());
-
-		List<TransactionBase> templateList = new ArrayList<>();
-		templateList.add(template1);
-		templateList.add(template2);
-
-		List<TransactionBase> updatedTemplates = importService.updateCategoriesForItems(templateList, 3, 5);
-		assertThat(updatedTemplates).hasSize(1);
-		assertThat(updatedTemplates.get(0).getCategory()).hasFieldOrPropertyWithValue("ID", 5);
-	}
-
-	@Test
-	void test_removeAlreadyUpdatedTransactions()
-	{
-		Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
-		category1.setID(3);
-
-		Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM);
-		category2.setID(4);
-
-		List<Transaction> transactionList = new ArrayList<>();
-		Transaction transaction1 = new Transaction();
-		transaction1.setName("Test");
-		transaction1.setAmount(200);
-		transaction1.setDate(LocalDate.of(2018, 10, 3));
-		transaction1.setCategory(category1);
-		transaction1.setAccount(new Account("Account", AccountType.CUSTOM));
-		transactionList.add(transaction1);
-
-		Transaction transaction2 = new Transaction();
-		transaction2.setName("Test_2");
-		transaction2.setAmount(-525);
-		transaction2.setDate(LocalDate.of(2018, 10, 3));
-		transaction2.setCategory(category2);
-		transaction2.setAccount(new Account("Account", AccountType.CUSTOM));
-		transactionList.add(transaction2);
-
-		List<Transaction> alreadyUpdatedTransactions = new ArrayList<>();
-		transaction1.setCategory(category2);
-		alreadyUpdatedTransactions.add(transaction1);
-
-		transactionList.removeAll(alreadyUpdatedTransactions);
-		assertThat(transactionList)
-				.hasSize(1)
-				.contains(transaction2);
-	}
-
-	@Test
-	void test_updateAccountsForTransactions()
-	{
-		Account account1 = new Account("Account_1", AccountType.CUSTOM);
-		account1.setID(2);
-		Account account2 = new Account("Account_2", AccountType.CUSTOM);
-		account2.setID(3);
-
-		Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM);
-		destinationAccount.setID(5);
-
-		List<TransactionBase> transactionList = new ArrayList<>();
-		Transaction transaction1 = new Transaction();
-		transaction1.setAccount(account1);
-		transaction1.setName("ShouldGoInAccount_1");
-		transaction1.setAmount(200);
-		transaction1.setDate(LocalDate.of(2018, 10, 3));
-		transactionList.add(transaction1);
-
-		Transaction transaction2 = new Transaction();
-		transaction2.setAccount(account2);
-		transaction2.setName("ImPartOfAccount_2");
-		transaction2.setAmount(-525);
-		transaction2.setDate(LocalDate.of(2018, 10, 3));
-		transactionList.add(transaction2);
-
-		List<TransactionBase> updatedTransactions = importService.updateAccountsForItems(transactionList, account1.getID(), destinationAccount);
-		assertThat(updatedTransactions).hasSize(1);
-		assertThat(updatedTransactions.get(0).getAccount().getID()).isEqualTo(5);
-	}
-
-	@Test
-	void test_updateTransferAccountsForTransactions()
-	{
-		Account account1 = new Account("Account_1", AccountType.CUSTOM);
-		account1.setID(2);
-		Account transferAccount = new Account("TransferAccount", AccountType.CUSTOM);
-		transferAccount.setID(3);
-
-		Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM);
-		destinationAccount.setID(5);
-
-		List<TransactionBase> transactionList = new ArrayList<>();
-		Transaction transaction = new Transaction();
-		transaction.setAccount(account1);
-		transaction.setTransferAccount(transferAccount);
-		transaction.setName("Whatever");
-		transaction.setAmount(-525);
-		transaction.setDate(LocalDate.of(2018, 10, 3));
-		transactionList.add(transaction);
-
-		// expected
-		Transaction expectedTransaction = new Transaction();
-		expectedTransaction.setAccount(account1);
-		expectedTransaction.setTransferAccount(destinationAccount);
-		expectedTransaction.setName("Whatever");
-		expectedTransaction.setAmount(-525);
-		expectedTransaction.setDate(LocalDate.of(2018, 10, 3));
-
-		assertThat(importService.updateTransferAccountsForItems(transactionList, transferAccount.getID(), destinationAccount))
-				.hasSize(1)
-				.contains(expectedTransaction);
-	}
-
-	@Test
-	void test_updateAccountsForTemplates()
-	{
-		Account account1 = new Account("Account_1", AccountType.CUSTOM);
-		account1.setID(2);
-		Account account2 = new Account("Account_2", AccountType.CUSTOM);
-		account2.setID(3);
-
-		Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM);
-		destinationAccount.setID(5);
-
-		Template template1 = new Template();
-		template1.setAccount(account1);
-		template1.setTemplateName("ShouldGoInAccount_1");
-		template1.setAmount(200);
-		template1.setName("Test");
-		template1.setTags(new ArrayList<>());
-
-		Template template2 = new Template();
-		template2.setAccount(account2);
-		template2.setTemplateName("ImPartOfAccount_2");
-		template2.setAmount(-525);
-		template2.setName("Test_2");
-		template2.setTags(new ArrayList<>());
-
-		List<TransactionBase> templateList = new ArrayList<>();
-		templateList.add(template1);
-		templateList.add(template2);
-
-		List<TransactionBase> updatedTransactions = importService.updateAccountsForItems(templateList, account1.getID(), destinationAccount);
-		assertThat(updatedTransactions).hasSize(1);
-		assertThat(updatedTransactions.get(0).getAccount().getID()).isEqualTo(5);
-	}
-
-	@Test
-	void test_updateTransferAccountsForTemplates()
-	{
-		Account account1 = new Account("Account_1", AccountType.CUSTOM);
-		account1.setID(2);
-		Account transferAccount = new Account("TransferAccount", AccountType.CUSTOM);
-		transferAccount.setID(3);
-
-		Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM);
-		destinationAccount.setID(5);
-
-		Template template1 = new Template();
-		template1.setAccount(account1);
-		template1.setTemplateName("ShouldGoInAccount_1");
-		template1.setAmount(200);
-		template1.setName("Test");
-		template1.setTags(new ArrayList<>());
-
-		Template template2 = new Template();
-		template2.setAccount(account1);
-		template2.setTemplateName("ImPartOfAccount_2");
-		template2.setAmount(-525);
-		template2.setName("Test_2");
-		template2.setTransferAccount(transferAccount);
-		template2.setTags(new ArrayList<>());
-
-		List<TransactionBase> templateList = new ArrayList<>();
-		templateList.add(template1);
-		templateList.add(template2);
-
-		List<TransactionBase> updatedTemplates = importService.updateTransferAccountsForItems(templateList, transferAccount.getID(), destinationAccount);
-		assertThat(updatedTemplates).hasSize(1);
-		assertThat(updatedTemplates.get(0).getTransferAccount().getID()).isEqualTo(5);
-	}
 
 	@Test
 	void test_updateTagsForItem_ExistingTag()
@@ -396,6 +163,11 @@ class ImportServiceTest
 		List<Tag> tags = new ArrayList<>();
 		tags.add(tag1);
 
+		// categories
+		Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
+		Category category2 = new Category("Category2", "#00ffff", CategoryType.CUSTOM);
+		List<Category> categories = List.of(category1, category2);
+
 		// transactions
 		List<Transaction> transactions = new ArrayList<>();
 		Transaction transaction1 = new Transaction();
@@ -404,16 +176,27 @@ class ImportServiceTest
 		transaction1.setAmount(200);
 		transaction1.setDate(LocalDate.of(2018, 10, 3));
 		transaction1.setTags(tags);
+		transaction1.setCategory(category1);
 		transactions.add(transaction1);
 
 		Transaction transaction2 = new Transaction();
-		transaction2.setAccount(sourceAccount2);
-		transaction2.setName("ImPartOfAccount_2");
-		transaction2.setAmount(-525);
+		transaction2.setAccount(sourceAccount1);
+		transaction2.setName("ShouldGoInAccount_1_Too");
+		transaction2.setAmount(100);
 		transaction2.setDate(LocalDate.of(2018, 10, 3));
-		transaction2.setTags(new ArrayList<>());
+		transaction2.setTags(tags);
+		transaction2.setCategory(category1);
 		transactions.add(transaction2);
 
+		Transaction transaction3 = new Transaction();
+		transaction3.setAccount(sourceAccount2);
+		transaction3.setName("ImPartOfAccount_2");
+		transaction3.setAmount(-525);
+		transaction3.setDate(LocalDate.of(2018, 10, 3));
+		transaction3.setTags(new ArrayList<>());
+		transaction3.setCategory(category2);
+		transactions.add(transaction3);
+
 		// template group
 		TemplateGroup templateGroup = new TemplateGroup(1, "My Template Group", TemplateGroupType.CUSTOM);
 
@@ -423,6 +206,7 @@ class ImportServiceTest
 		template1.setAmount(1500);
 		template1.setAccount(sourceAccount1);
 		template1.setName("Transaction from Template");
+		template1.setCategory(category1);
 		List<Tag> tags2 = new ArrayList<>();
 		tags2.add(tag1);
 		template1.setTags(tags2);
@@ -434,11 +218,18 @@ class ImportServiceTest
 		Template template2 = new Template();
 		template2.setTemplateName("MyTemplate2");
 		template2.setTransferAccount(sourceAccount2);
+		template2.setCategory(category1);
 		template2.setTags(new ArrayList<>());
 
+		Template template3 = new Template();
+		template3.setTemplateName("MyTemplate3");
+		template3.setTransferAccount(sourceAccount1);
+		template3.setTags(new ArrayList<>());
+
 		List<Template> templates = new ArrayList<>();
 		templates.add(template1);
 		templates.add(template2);
+		templates.add(template3);
 
 		// charts
 		Chart chart = new Chart();
@@ -449,7 +240,7 @@ class ImportServiceTest
 		chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n    x: [],\r\n    y: [],\r\n    type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n    showSendToCloud: false,\r\n    displaylogo: false,\r\n    showLink: false,\r\n    responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n");
 
 		// database
-		InternalDatabase database = new InternalDatabase(new ArrayList<>(), accounts, transactions, List.of(templateGroup), templates, List.of(chart), List.of(), List.of(icon1));
+		InternalDatabase database = new InternalDatabase(categories, accounts, transactions, List.of(templateGroup), templates, List.of(chart), List.of(), List.of(icon1));
 
 		// account matches
 		AccountMatch match1 = new AccountMatch(sourceAccount1);
@@ -465,6 +256,11 @@ class ImportServiceTest
 		AccountMatchList accountMatchList = new AccountMatchList(matches);
 
 		// expected
+		Category expectedCategory1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
+		expectedCategory1.setID(1);
+		Category expectedCategory2 = new Category("Category2", "#00ffff", CategoryType.CUSTOM);
+		expectedCategory2.setID(2);
+
 		Transaction expectedTransaction1 = new Transaction();
 		expectedTransaction1.setAccount(destAccount1);
 		expectedTransaction1.setName("ShouldGoInAccount_1");
@@ -473,11 +269,18 @@ class ImportServiceTest
 		expectedTransaction1.setTags(tags);
 
 		Transaction expectedTransaction2 = new Transaction();
-		expectedTransaction2.setAccount(destAccount2);
-		expectedTransaction2.setName("ImPartOfAccount_2");
-		expectedTransaction2.setAmount(-525);
+		expectedTransaction2.setAccount(destAccount1);
+		expectedTransaction2.setName("ShouldGoInAccount_1_Too");
+		expectedTransaction2.setAmount(100);
 		expectedTransaction2.setDate(LocalDate.of(2018, 10, 3));
-		expectedTransaction2.setTags(new ArrayList<>());
+		expectedTransaction2.setTags(tags);
+
+		Transaction expectedTransaction3 = new Transaction();
+		expectedTransaction3.setAccount(destAccount2);
+		expectedTransaction3.setName("ImPartOfAccount_2");
+		expectedTransaction3.setAmount(-525);
+		expectedTransaction3.setDate(LocalDate.of(2018, 10, 3));
+		expectedTransaction3.setTags(new ArrayList<>());
 
 		Template expectedTemplate1 = new Template();
 		expectedTemplate1.setTemplateName("MyTemplate");
@@ -497,9 +300,17 @@ class ImportServiceTest
 		expectedTemplate2.setTransferAccount(destAccount2);
 		expectedTemplate2.setTags(new ArrayList<>());
 
+		Template expectedTemplate3 = new Template();
+		expectedTemplate3.setTemplateName("MyTemplate3");
+		expectedTemplate3.setTransferAccount(destAccount1);
+		expectedTemplate3.setTags(new ArrayList<>());
+
 		TemplateGroup expectedTemplateGroup = new TemplateGroup(5, "My Template Group", TemplateGroupType.CUSTOM);
 
 		// act
+		Mockito.when(categoryRepository.save(category1)).thenReturn(expectedCategory1);
+		Mockito.when(categoryRepository.save(category2)).thenReturn(expectedCategory2);
+
 		Mockito.when(tagRepository.save(Mockito.any(Tag.class))).thenReturn(tag1);
 
 		Mockito.when(chartService.getHighestUsedID()).thenReturn(8);
@@ -519,147 +330,24 @@ class ImportServiceTest
 		InternalDatabase databaseResult = importService.getDatabase();
 
 		// assert
-		assertThat(databaseResult.getTransactions())
+		assertThat(databaseResult.getCategories())
 				.hasSize(2)
-				.contains(expectedTransaction1, expectedTransaction2);
+				.contains(expectedCategory1, expectedCategory2);
+		assertThat(databaseResult.getTransactions())
+				.hasSize(3)
+				.contains(expectedTransaction1, expectedTransaction2, expectedTransaction3);
 		assertThat(databaseResult.getTemplateGroups())
 				.hasSize(1)
 				.contains(templateGroup);
 		assertThat(databaseResult.getTemplates())
-				.hasSize(2)
-				.contains(expectedTemplate1, expectedTemplate2);
+				.hasSize(3)
+				.contains(expectedTemplate1, expectedTemplate2, expectedTemplate3);
 		assertThat(databaseResult.getCharts())
 				.hasSize(1)
 				.contains(chart);
 		assertThat(importService.getCollectedErrorMessages()).isEmpty();
 	}
 
-	@Test
-	void test_chartId()
-	{
-		Chart chart = new Chart();
-		chart.setID(9);
-		chart.setName("The best chart");
-		chart.setType(ChartType.CUSTOM);
-		chart.setVersion(7);
-		chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n    x: [],\r\n    y: [],\r\n    type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n    showSendToCloud: false,\r\n    displaylogo: false,\r\n    showLink: false,\r\n    responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n");
-
-		// database
-		InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(chart), List.of(), List.of());
-
-		// act
-		int highestUsedID = 22;
-		Mockito.when(chartService.getHighestUsedID()).thenReturn(highestUsedID);
-		final ChartRepository chartRepositoryMock = Mockito.mock(ChartRepository.class);
-		Mockito.when(chartService.getRepository()).thenReturn(chartRepositoryMock);
-
-		importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true);
-		InternalDatabase databaseResult = importService.getDatabase();
-
-		// assert
-		assertThat(databaseResult.getCharts().get(0))
-				.hasFieldOrPropertyWithValue("ID", highestUsedID + 1);
-	}
-
-	@Test
-	void test_updateImagesForIcons()
-	{
-		Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		image1.setID(3);
-
-		Image image2 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.JPG);
-		image2.setID(4);
-
-		Icon icon1 = new Icon(image1);
-		Icon icon2 = new Icon(image2);
-
-		final List<Icon> iconList = List.of(icon1, icon2);
-
-		List<Icon> updatedIcons = importService.updateImagesForIcons(iconList, 3, 5);
-		assertThat(updatedIcons).hasSize(1);
-		final Image icon = updatedIcons.get(0).getImage();
-		assertThat(icon.getBase64EncodedImage()).isEqualTo("data:image/png;base64,");
-		assertThat(icon.getID()).isEqualTo(5);
-	}
-
-	@Test
-	void test_importImages_notExisting()
-	{
-		Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		image.setID(3);
-
-		Image newImage = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		newImage.setID(5);
-
-		final ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class);
-		Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock);
-		Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(newImage);
-
-		InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of());
-		importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true);
-
-		Image expectedImage = new Image(image.getImage(), image.getFileName(), image.getFileExtension());
-		Mockito.verify(imageRepositoryMock, Mockito.atLeast(1)).save(expectedImage);
-	}
-
-	@Test
-	void test_importImages_alreadyExisting()
-	{
-		Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		image.setID(3);
-
-		Image newImage = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		newImage.setID(5);
-
-		final ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class);
-		Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock);
-		Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(newImage);
-
-		InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of());
-		importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true);
-
-		Image expectedImage = new Image(image.getImage(), image.getFileName(), image.getFileExtension());
-		Mockito.verify(imageRepositoryMock, Mockito.atLeast(1)).save(expectedImage);
-	}
-
-	@Test
-	void test_importAccounts_icon()
-	{
-		Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		image.setID(3);
-		Icon icon = new Icon(image);
-		icon.setID(15);
-
-		Account accountSource = new Account("my account with icon", AccountType.CUSTOM, icon);
-		accountSource.setID(1);
-		Account accountDestination = new Account("destination", AccountType.CUSTOM);
-		accountDestination.setID(15);
-
-		InternalDatabase database = new InternalDatabase(List.of(), List.of(accountSource), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of(icon));
-		AccountMatch accountMatch = new AccountMatch(accountSource);
-		accountMatch.setAccountDestination(accountDestination);
-
-		Icon expectedIcon = new Icon(image);
-		expectedIcon.setID(1);
-
-		Account expectedAccount = new Account("destination", AccountType.CUSTOM, expectedIcon);
-		expectedAccount.setID(15);
-		Mockito.when(accountRepository.save(Mockito.any())).thenReturn(expectedAccount);
-		Mockito.when(accountRepository.findById(15)).thenReturn(Optional.of(accountDestination));
-
-		ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class);
-		Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock);
-		Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(image);
-
-		IconRepository iconRepositoryMock = Mockito.mock(IconRepository.class);
-		Mockito.when(iconService.getRepository()).thenReturn(iconRepositoryMock);
-		Mockito.when(iconRepositoryMock.save(Mockito.any())).thenReturn(expectedIcon);
-
-		importService.importDatabase(database, new AccountMatchList(List.of(accountMatch)), true, true, true);
-
-		Mockito.verify(accountRepository, Mockito.atLeast(1)).save(expectedAccount);
-	}
-
 	@Test
 	void test_skipTemplates()
 	{
@@ -710,7 +398,7 @@ class ImportServiceTest
 	}
 
 	@Test
-	void test_skipCarts()
+	void test_skipCharts()
 	{
 		Chart chart = new Chart();
 		chart.setID(9);
@@ -732,59 +420,6 @@ class ImportServiceTest
 		Mockito.verify(chartRepositoryMock, Mockito.never()).save(Mockito.any());
 	}
 
-	@Test
-	void test_errorWhileImportingCategory_shouldBeCollected()
-	{
-		Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
-		category1.setID(3);
-
-		Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM);
-		category2.setID(4);
-
-		// raise exception
-		Mockito.when(categoryRepository.findByNameAndColorAndType(Mockito.eq("Category1"), Mockito.any(), Mockito.any())).thenThrow(new NullPointerException());
-		Mockito.when(categoryRepository.findByNameAndColorAndType(Mockito.eq("Category2"), Mockito.any(), Mockito.any())).thenReturn(category2);
-
-		InternalDatabase database = new InternalDatabase(List.of(category1, category2), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of());
-		final List<ImportResultItem> importResultItems = importService.importDatabase(database, new AccountMatchList(List.of()), false, false, false);
-
-		assertThat(importResultItems).hasSize(7)
-				.contains(new ImportResultItem(EntityType.CATEGORY, 1, 2));
-		assertThat(importService.getCollectedErrorMessages()).hasSize(1)
-				.contains("Error while importing category with name \"Category1\": java.lang.NullPointerException (null)");
-	}
-
-	@Test
-	void test_updateIconsForAccounts()
-	{
-		Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
-		image1.setID(3);
-
-		Icon icon1 = new Icon(image1);
-		icon1.setID(3);
-
-		Icon icon2 = new Icon("fas fa-icons");
-		icon2.setID(4);
-
-		Account account1 = new Account("account with image icon", AccountType.CUSTOM, icon1);
-		Account account2 = new Account("account with built-in icon", AccountType.CUSTOM, icon2);
-		List<Account> accounts = List.of(account1, account2);
-
-		List<Iconizable> updatedAccounts = importService.updateIconsForItems(accounts, 3, 5);
-		assertThat(updatedAccounts).hasSize(1);
-		assertThat(updatedAccounts.get(0).getIconReference())
-				.hasFieldOrPropertyWithValue("ID", 5)
-				.hasFieldOrPropertyWithValue("image", image1)
-				.hasFieldOrPropertyWithValue("builtinIdentifier", null);
-
-		updatedAccounts = importService.updateIconsForItems(accounts, 4, 6);
-		assertThat(updatedAccounts).hasSize(1);
-		assertThat(updatedAccounts.get(0).getIconReference())
-				.hasFieldOrPropertyWithValue("ID", 6)
-				.hasFieldOrPropertyWithValue("image", null)
-				.hasFieldOrPropertyWithValue("builtinIdentifier", "fas fa-icons");
-	}
-
 	@Test
 	void test_updateTemplateGroupsForTemplates()
 	{
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8a26f1c17fcbf69e3b94bf2d5c32ce48eb03e8e
--- /dev/null
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java
@@ -0,0 +1,96 @@
+package de.deadlocker8.budgetmaster.unit.database.importer;
+
+import de.deadlocker8.budgetmaster.accounts.Account;
+import de.deadlocker8.budgetmaster.accounts.AccountRepository;
+import de.deadlocker8.budgetmaster.accounts.AccountType;
+import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch;
+import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList;
+import de.deadlocker8.budgetmaster.database.importer.AccountImporter;
+import de.deadlocker8.budgetmaster.database.importer.IconImporter;
+import de.deadlocker8.budgetmaster.icon.Icon;
+import de.deadlocker8.budgetmaster.icon.IconRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class AccountImporterTest
+{
+	@Autowired
+	private IconRepository iconRepository;
+
+	@Autowired
+	private AccountRepository accountRepository;
+
+	@Test
+	void test_importAccounts()
+	{
+		final Account sourceAccount = new Account("SourceAccount", AccountType.CUSTOM);
+		sourceAccount.setID(2);
+
+		final Account sourceAccount2 = new Account("SourceAccount 2", AccountType.CUSTOM);
+		sourceAccount2.setID(7);
+
+		final Account destinationAccount = new Account("DestinationAccount", AccountType.CUSTOM);
+		destinationAccount.setID(1);
+		accountRepository.save(destinationAccount);
+
+		final Account destinationAccount2 = new Account("DestinationAccount 2", AccountType.CUSTOM);
+		destinationAccount.setID(2);
+		accountRepository.save(destinationAccount2);
+
+		final AccountMatch accountMatch = new AccountMatch(sourceAccount);
+		accountMatch.setAccountDestination(destinationAccount);
+
+		final AccountMatch accountMatch2 = new AccountMatch(sourceAccount2);
+		accountMatch2.setAccountDestination(destinationAccount2);
+		final AccountMatchList accountMatchList = new AccountMatchList(List.of(accountMatch, accountMatch2));
+
+		final AccountImporter importer = new AccountImporter(accountRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(sourceAccount, sourceAccount2), accountMatchList);
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.ACCOUNT, 2, 2, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(sourceAccount).hasFieldOrPropertyWithValue("ID", destinationAccount.getID());
+		assertThat(sourceAccount2).hasFieldOrPropertyWithValue("ID", destinationAccount2.getID());
+	}
+
+	@Test
+	void test_importAccounts_updateIcon()
+	{
+		Icon icon = new Icon("fas fa-icons");
+		icon = iconRepository.save(icon);
+
+		final Account sourceAccount = new Account("SourceAccount", AccountType.CUSTOM);
+		sourceAccount.setID(2);
+		sourceAccount.setIconReference(icon);
+
+		Account destinationAccount = new Account("DestinationAccount", AccountType.CUSTOM);
+		destinationAccount.setID(1);
+		destinationAccount = accountRepository.save(destinationAccount);
+
+		final AccountMatch accountMatch = new AccountMatch(sourceAccount);
+		accountMatch.setAccountDestination(destinationAccount);
+
+		final AccountMatchList accountMatchList = new AccountMatchList(List.of(accountMatch));
+
+		final AccountImporter importer = new AccountImporter(accountRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(sourceAccount), accountMatchList);
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.ACCOUNT, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(sourceAccount).hasFieldOrPropertyWithValue("ID", destinationAccount.getID());
+		assertThat(accountRepository.getById(destinationAccount.getID())).hasFieldOrPropertyWithValue("iconReference", icon);
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7603d31be4684926cd15559ce02c64a32470479
--- /dev/null
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java
@@ -0,0 +1,109 @@
+package de.deadlocker8.budgetmaster.unit.database.importer;
+
+import de.deadlocker8.budgetmaster.categories.Category;
+import de.deadlocker8.budgetmaster.categories.CategoryRepository;
+import de.deadlocker8.budgetmaster.categories.CategoryType;
+import de.deadlocker8.budgetmaster.database.importer.CategoryImporter;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class CategoryImporterTest
+{
+	@Autowired
+	private CategoryRepository categoryRepository;
+
+	@Test
+	void test_importCategories()
+	{
+		final Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
+		category1.setID(2);
+
+		final Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM);
+		category2.setID(3);
+
+		final CategoryImporter importer = new CategoryImporter(categoryRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(category1, category2));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 2, 2, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(category1).hasFieldOrPropertyWithValue("ID", 1);
+		assertThat(category2).hasFieldOrPropertyWithValue("ID", 2);
+	}
+
+	@Test
+	void test_importCategories_skipExisting_defaultNone()
+	{
+		final Category category = new Category("No category", "#ff0000", CategoryType.NONE);
+		category.setID(1);
+
+		categoryRepository.save(category);
+
+		final CategoryImporter importer = new CategoryImporter(categoryRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(category));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(category).hasFieldOrPropertyWithValue("ID", 1);
+	}
+
+	@Test
+	void test_importCategories_skipExisting_defaultRest()
+	{
+		final Category category = new Category("Rest", "#ff0000", CategoryType.REST);
+		category.setID(1);
+
+		categoryRepository.save(category);
+
+		final CategoryImporter importer = new CategoryImporter(categoryRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(category));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(category).hasFieldOrPropertyWithValue("ID", 1);
+	}
+
+	@Test
+	void test_importCategories_skipExisting_custom()
+	{
+		final Category category = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
+		category.setID(3);
+
+		categoryRepository.save(new Category("existing category 1", "#ffffff", CategoryType.CUSTOM));
+		categoryRepository.save(new Category("existing category 2", "#ffffff", CategoryType.CUSTOM));
+		categoryRepository.save(category);
+
+		final CategoryImporter importer = new CategoryImporter(categoryRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(category));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(category).hasFieldOrPropertyWithValue("ID", 3);
+	}
+
+	@Test
+	void test_importCategories_errorIsCollected()
+	{
+		final Category category = new Category("Category1", "#ff0000", CategoryType.CUSTOM);
+
+		final CategoryImporter importer = new CategoryImporter(categoryRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(category));
+
+		assertThat(resultItem.getNumberOfImportedItems())
+				.isZero();
+		assertThat(resultItem.getCollectedErrorMessages())
+				.hasSize(1);
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..129d1b3bdd134e16217c6873e09d07db9cd444be
--- /dev/null
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java
@@ -0,0 +1,51 @@
+package de.deadlocker8.budgetmaster.unit.database.importer;
+
+import de.deadlocker8.budgetmaster.Main;
+import de.deadlocker8.budgetmaster.charts.Chart;
+import de.deadlocker8.budgetmaster.charts.ChartService;
+import de.deadlocker8.budgetmaster.charts.ChartType;
+import de.deadlocker8.budgetmaster.database.importer.ChartImporter;
+import de.deadlocker8.budgetmaster.database.importer.ImageImporter;
+import de.deadlocker8.budgetmaster.images.Image;
+import de.deadlocker8.budgetmaster.images.ImageFileExtension;
+import de.deadlocker8.budgetmaster.images.ImageRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(classes = Main.class)
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class ChartImporterTest
+{
+	@Autowired
+	private ChartService chartService;
+
+	@Test
+	void test_importCharts()
+	{
+		final Chart chart = new Chart();
+		chart.setID(9);
+		chart.setName("The best chart");
+		chart.setType(ChartType.CUSTOM);
+		chart.setVersion(7);
+		chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n    x: [],\r\n    y: [],\r\n    type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n    showSendToCloud: false,\r\n    displaylogo: false,\r\n    showLink: false,\r\n    responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n");
+
+		final ChartImporter importer = new ChartImporter(chartService);
+		final ImportResultItem resultItem = importer.importItems(List.of(chart));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.CHART, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+
+		assertThat(chart).hasFieldOrPropertyWithValue("ID", chartService.getRepository().findAll().size());
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7faaf516b79f0a8e50f5ba7c6700eae3341723e
--- /dev/null
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java
@@ -0,0 +1,68 @@
+package de.deadlocker8.budgetmaster.unit.database.importer;
+
+import de.deadlocker8.budgetmaster.database.importer.IconImporter;
+import de.deadlocker8.budgetmaster.icon.Icon;
+import de.deadlocker8.budgetmaster.icon.IconRepository;
+import de.deadlocker8.budgetmaster.images.Image;
+import de.deadlocker8.budgetmaster.images.ImageFileExtension;
+import de.deadlocker8.budgetmaster.images.ImageRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class IconImporterTest
+{
+	@Autowired
+	private ImageRepository imageRepository;
+
+	@Autowired
+	private IconRepository iconRepository;
+
+	@Test
+	void test_importIcons()
+	{
+		Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
+		image1 = imageRepository.save(image1);
+
+		final Icon icon1 = new Icon(image1);
+		icon1.setID(3);
+
+		final Icon icon2 = new Icon("fas fa-icons");
+		icon2.setID(4);
+
+		final IconImporter importer = new IconImporter(iconRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(icon1, icon2));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.ICON, 2, 2, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(icon1).hasFieldOrPropertyWithValue("ID", 1);
+		assertThat(icon2).hasFieldOrPropertyWithValue("ID", 2);
+	}
+
+	@Test
+	void test_importIcons_alreadyExisting()
+	{
+		final Icon icon = new Icon("fas fa-icons");
+		icon.setID(1);
+		iconRepository.save(icon);
+
+		final IconImporter importer = new IconImporter(iconRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(icon));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.ICON, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(icon).hasFieldOrPropertyWithValue("ID", 2);
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d0dd1398d03bd2f8a599d6a906d868e289d5adb
--- /dev/null
+++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java
@@ -0,0 +1,60 @@
+package de.deadlocker8.budgetmaster.unit.database.importer;
+
+import de.deadlocker8.budgetmaster.database.importer.ImageImporter;
+import de.deadlocker8.budgetmaster.images.Image;
+import de.deadlocker8.budgetmaster.images.ImageFileExtension;
+import de.deadlocker8.budgetmaster.images.ImageRepository;
+import de.deadlocker8.budgetmaster.services.EntityType;
+import de.deadlocker8.budgetmaster.services.ImportResultItem;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(SpringExtension.class)
+@DataJpaTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+class ImageImporterTest
+{
+	@Autowired
+	private ImageRepository imageRepository;
+
+	@Test
+	void test_importImages()
+	{
+		final Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
+		image1.setID(3);
+
+		final Image image2 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.JPG);
+		image2.setID(4);
+
+		final ImageImporter importer = new ImageImporter(imageRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(image1, image2));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.IMAGE, 2, 2, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(image1).hasFieldOrPropertyWithValue("ID", 1);
+		assertThat(image2).hasFieldOrPropertyWithValue("ID", 2);
+	}
+
+	@Test
+	void test_importImages_alreadyExisting()
+	{
+		final Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG);
+		image.setID(1);
+		imageRepository.save(image);
+
+		final ImageImporter importer = new ImageImporter(imageRepository);
+		final ImportResultItem resultItem = importer.importItems(List.of(image));
+
+		final ImportResultItem expected = new ImportResultItem(EntityType.IMAGE, 1, 1, List.of());
+		assertThat(resultItem).isEqualTo(expected);
+		assertThat(image).hasFieldOrPropertyWithValue("ID", 2);
+	}
+}
\ No newline at end of file