Honestly I’m still shocked that the Java world just accepts that Hibernate doesn’t offer a way to have safe and still uncomplicated automatic schema migrations without paying for something like Liquibase and using their unreliable Maven plugin. Django has first-class schema migrations built-in, and they just work. Java devs just live with the fact that they have to remember to also write an SQL script for every change they make to an entity class.
Hibernate does automatic schema migration if you have it turned on, but it can’t do something like detect a column name change and rename the column in the sql. It’ll pretty much treat it as a new column and leave the old one with all the data inaccessible. The way to do it correctly is with an explicit migration script, but it’ll only be relevant for existing dbs that have the old schema, new dbs will start with the new schema. The migration should deal with both cases, or do nothing in case the column is already correct. Not easy to do a column rename conditionally (I don’t know how off the top). So all in all, it’s much easier to do the explicit scripts to avoid this (every db follows the same migrations rather than potentially different paths to the same schema.
I’m curious how django’s orm handles this situation if you have time to respond. Will do some research later because hibernate + flyway has not been super fun for migrations
So it doesn’t handle schema migrations. The create.sql feature is nowhere near full migration support in my book (or am I missing something?).
Django will diff your current models as they are in the code against the projection of applying all past migrations in order. It will then create a new migration file containing the changes necessary to make the virtual DB state match the models. Trivial cases like adding new columns, dropping columns etc. will be generated automatically. For renamed single columns it will ask if those should be a rename or if you removed one column and added another (i.e. drop + add). If multiple fields of the same model were renamed or for any other ambiguities, it will warn you and ask you to write a custom migration. And you can always invoke a command to create a bare migration file for you to fill out with more complex migration logic (custom order of steps, custom SQL, even custom Python).
All of that is DB-agnostic (except for the SQL you write yourself, though you can add multiple versions for different dialects) and defined in pure Python. It never hits a DB until you apply migrations. Migrations are basically an ordered list of Python objects describing changes, with Django providing predefined steps for all common building blocks like adding a column, creating an index etc..
That migration will have an ID and point to the previous ID, which makes them independent of filenames and allows squashing multiple migrations into one when the history becomes expensively long (it will even consolidate counteracting/obsolete changes into no-ops).
It also provides automatic inversion for non-destructive changes and allows you to define custom backwards migrations for destructive changes and custom Python/SQL, so that a well-maintained Django history is always fully reversible and allows jumping to arbitrary past versions.
Last time I checked, native Hibernate doesn’t even cover half of that. People commonly use additional tooling, but Flyway doesn’t support diffing at all and Liquibase needs a (poorly maintained) Maven plugin for that, and even then it will only diff against a reference DB, not a virtual state reconstructed from your migration history. So you have to make sure the DB matches the migration state you’re expecting. Support for custom naming strategies is also quite bad, I still remember I had to fall back to explicit column/index naming a few times because it just wouldn’t apply the naming strategy to the migration file. Support for @Embedded was very lackluster, too. It simply wouldn’t correctly apply column lengths for string columns in embedded types. Not to mention, XML as a migration format sucks and Flyway is just applying arbitrary SQL scripts, so it can’t possibly provide the same level of diffing/analysis/safeguards without solving the halting problem or running all your scripts against a throwaway DB.
Granted, I think Django doesn’t even support the embedded pattern, but my point is: Django ORM provides one coherent developer experience whereas my options in the Java world feel like I have to glue a set of tools with varying features together and hope my use case is covered by their common denominator. Which is odd, given the whole idea of Spring and similar projects was to provide a coherent and battle-tested experience.
1
u/JimmyM_1 7d ago
Haven't worked with either, but that sounds interesting.
I will look them up.