Added migration info to README.md, and added ability to insert default categories into existing perfin profiles.
This commit is contained in:
parent
0fe451029d
commit
396fd122a8
27
README.md
27
README.md
|
@ -37,3 +37,30 @@ to set the version everywhere that it needs to be.
|
|||
|
||||
Once that's done, the workflow will start, and you should see a release appear
|
||||
in the next few minutes.
|
||||
|
||||
## Migration Procedure
|
||||
|
||||
Because this application relies on a structured relational database schema,
|
||||
changes to the schema must be handled with care to avoid destroying users' data.
|
||||
Specifically, when changes are made to the schema, a *migration* must be defined
|
||||
which provides instructions for Perfin to safely apply changes to an old schema.
|
||||
|
||||
The database schema is versioned using whole-number versions (1, 2, 3, ...), and
|
||||
a migration is defined for each transition from version to version, such that
|
||||
any older version can be incrementally upgraded, step by step, to the latest
|
||||
schema version.
|
||||
|
||||
Perfin only supports the latest schema version, as defined by `JdbcDataSourceFactory.SCHEMA_VERSION`.
|
||||
When the app loads a profile, it'll check that profile's schema version by
|
||||
reading a `.jdbc-schema-version.txt` file in the profile's main directory. If
|
||||
the profile's schema version is **less than** the current, Perfin will
|
||||
ask the user if they want to upgrade. If the profile's schema version is
|
||||
**greater than** the current, Perfin will tell the user that it can't load a
|
||||
schema from a newer version, and will prompt the user to upgrade.
|
||||
|
||||
### Writing a Migration
|
||||
|
||||
1. Write your migration. This can be plain SQL (placed in `resources/sql/migration`), or Java code.
|
||||
2. Add your migration to `com.andrewlalis.perfin.data.impl.migration.Migrations#getMigrations()`.
|
||||
3. Increment the schema version defined in `JdbcDataSourceFactory`.
|
||||
4. Test the migration yourself on a profile with data.
|
||||
|
|
|
@ -2,6 +2,9 @@ package com.andrewlalis.perfin.control;
|
|||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.data.TransactionCategoryRepository;
|
||||
import com.andrewlalis.perfin.data.impl.JdbcDataSource;
|
||||
import com.andrewlalis.perfin.data.impl.JdbcDataSourceFactory;
|
||||
import com.andrewlalis.perfin.data.util.DbUtil;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.BindingUtil;
|
||||
import com.andrewlalis.perfin.view.component.CategoryTile;
|
||||
|
@ -11,6 +14,9 @@ import javafx.collections.ObservableList;
|
|||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import static com.andrewlalis.perfin.PerfinApp.router;
|
||||
|
||||
public class CategoriesViewController implements RouteSelectionListener {
|
||||
|
@ -36,4 +42,22 @@ public class CategoriesViewController implements RouteSelectionListener {
|
|||
TransactionCategoryRepository::findTree
|
||||
).thenAccept(nodes -> Platform.runLater(() -> categoryTreeNodes.setAll(nodes)));
|
||||
}
|
||||
|
||||
@FXML public void addDefaultCategories() {
|
||||
boolean confirm = Popups.confirm(categoriesVBox, "Are you sure you want to add all of Perfin's default categories to your profile? This might interfere with existing categories of the same name.");
|
||||
if (!confirm) return;
|
||||
JdbcDataSource ds = (JdbcDataSource) Profile.getCurrent().dataSource();
|
||||
try (var conn = ds.getConnection()) {
|
||||
DbUtil.doTransaction(conn, () -> {
|
||||
try {
|
||||
new JdbcDataSourceFactory().insertDefaultCategories(conn);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
refreshCategories();
|
||||
} catch (Exception e) {
|
||||
Popups.error(categoriesVBox, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,18 @@ public class JdbcDataSourceFactory implements DataSourceFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private void insertDefaultData(Connection conn) throws IOException, SQLException {
|
||||
/**
|
||||
* Inserts all default data into the database, using static content found in
|
||||
* various locations on the classpath.
|
||||
* @param conn The connection to use to insert data.
|
||||
* @throws IOException If resources couldn't be read.
|
||||
* @throws SQLException If SQL fails.
|
||||
*/
|
||||
public void insertDefaultData(Connection conn) throws IOException, SQLException {
|
||||
insertDefaultCategories(conn);
|
||||
}
|
||||
|
||||
public void insertDefaultCategories(Connection conn) throws IOException, SQLException {
|
||||
try (
|
||||
var categoriesIn = JdbcDataSourceFactory.class.getResourceAsStream("/sql/data/default-categories.json");
|
||||
var stmt = conn.prepareStatement(
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
</StyledText>
|
||||
<HBox styleClass="std-padding, std-spacing" VBox.vgrow="NEVER">
|
||||
<Button text="Add Category" onAction="#addCategory"/>
|
||||
<Button text="Add Default Categories" onAction="#addDefaultCategories"/>
|
||||
</HBox>
|
||||
<ScrollPane styleClass="tile-container-scroll" VBox.vgrow="ALWAYS">
|
||||
<VBox fx:id="categoriesVBox" styleClass="tile-container"/>
|
||||
|
|
Loading…
Reference in New Issue