Hi
When reading the documentation, I was surprised to see the migration scripts for flyway were all sql files and not java code, yet flyway provides such hooks as well (http://flywaydb.org/documentation/migration/java.html).
Is there a reason for sticking with sql files ?
Still, I've rewritten the "Using jOOQ with Flyway" part of the documentation http://www.jooq.org/doc/3.7/manual-single-page/#jooq-with-flyway with the java based migration and jOOQ createTable statements and the like. I could contribute it back if you wish so, let me know.
Still, while working on it and thinking how we currently do migrations at work, with sql files, I felt like the approach taken in the documentation was a bit "incomplete": each time a new migration is added, the model is generated again and then one should look for compilation error. Don't get me wrong, it's already pretty and for sure needed. Yet the complete disparition of the previous model bothers me. At work we do like this and from times to times it bits us back. How was it before? Why did we change this stuff this way? Is it really the correct name for the old Foo columns?
As such, I tried the following approach:
- a model package, containing mostly empty classes subclassing the latest generated version
- a package per previous version, named currently model.previous.vX. This contains the generated code "at the time" and is used for the migration scripts.
When some change is to be done, I :
- create a new java migration, with just strings for the new stuff and using the latest version model for the current state
- run the new migration
- generate the new model
- copy/paste the new model in model.previous.vX+1
- update the migration script with the freshly generated model constants
- change the classes in the model package to extends this new model contents
The non migration code only uses the classes in the model package.
In the end:
- history is preserved,
- the migration scripts are typesafe,
- non migration code isn't burdened by migration management.
Does it look like a good approach?
--
You received this message because you are subscribed to the Google Groups "jOOQ User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jooq-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Still, I've rewritten the "Using jOOQ with Flyway" part of the documentation http://www.jooq.org/doc/3.7/manual-single-page/#jooq-with-flyway with the java based migration and jOOQ createTable statements and the like. I could contribute it back if you wish so, let me know.Sure, that would be very useful! Thank you very much for offering this.I would need you to sign the contributor agreement first:
Then, you can contribute your changes in any way you find suitable. We currently don't host the manual sources on GitHub (yet), so a pull request is currently not possible, I'm afraid.
Still, while working on it and thinking how we currently do migrations at work, with sql files, I felt like the approach taken in the documentation was a bit "incomplete": each time a new migration is added, the model is generated again and then one should look for compilation error. Don't get me wrong, it's already pretty and for sure needed. Yet the complete disparition of the previous model bothers me. At work we do like this and from times to times it bits us back. How was it before? Why did we change this stuff this way? Is it really the correct name for the old Foo columns?That's true. It would be useful to maintain versions of the schema also in the "current" Java code, not only in version control. Sure, version controlling generated sources is one way to tackle the problem (there are some discussions around that on this list). But having all versions at runtime in some form might also be useful. We have a pending feature request for this, but haven't thought this through yet:
It looks as though we can certainly learn from you here.
As such, I tried the following approach:
- a model package, containing mostly empty classes subclassing the latest generated versionInteresting, could you give an example about this subclassing that you're doing?
(...)
Does it look like a good approach?
It does indeed.
And I'd like to learn more about how you're doing this.
Specifically, if you find anything missing from jOOQ's DDL support, I'd love to know - there's still lots of room for improvement!
What I think would be very useful for the community would be a blog post with more details, in case you do write blogs. I think you've found a great solution to something that many people are thinking about, and that neither jOOQ nor Flyway currently offer an out-of-the-box solution for.
Sure, but actually I stumbled upon the Corporate Contributor Information part: my employer indeed works in software but has no policy I know of regarding Open Source and my contract is totally free of such restriction. I'll try to get some answer internally but I don't expect much (nor fast)...
well, actually I changed the way migration are triggered to have them done from a main method. It feels closer from "real life" to me, yet it makes the example a bit more complex. Is still fine for you ?
Still, while working on it and thinking how we currently do migrations at work, with sql files, I felt like the approach taken in the documentation was a bit "incomplete": each time a new migration is added, the model is generated again and then one should look for compilation error. Don't get me wrong, it's already pretty and for sure needed. Yet the complete disparition of the previous model bothers me. At work we do like this and from times to times it bits us back. How was it before? Why did we change this stuff this way? Is it really the correct name for the old Foo columns?That's true. It would be useful to maintain versions of the schema also in the "current" Java code, not only in version control. Sure, version controlling generated sources is one way to tackle the problem (there are some discussions around that on this list). But having all versions at runtime in some form might also be useful. We have a pending feature request for this, but haven't thought this through yet:
Well, I'm unsure about annotation there. What if I deleted some columns, what to annotate? And changes could be subtler, like some type change, and then how to reflect this ? What if multiple changes were done over time ?
Not so fast ;)
Actually we have the issue currently in our production code : changes over time are only visible in the migration files.
Which has two main drawbacks :
- migrations aren't typesafe (and in 10+ years of ongoing dev, typo were made, actually they happen something like twice a year)
- in case of issue, figuring out what was before isn't always easy (especially when some migration actions were done, like altering data to fit into the new schema)
Interesting, could you give an example about this subclassing that you're doing?
Well, that's pretty simple, for example:
package org.jooq.example.model.tables;
public class Author extends org.jooq.example.model.previous.v3.tables.Author {
}
AUTHOR.as("t");
I'm thinking of doing it for every stuff from the generated model which is used at least once outside of the migration steps.
So when a new migration is done, I simply have to change the extend to v4 and then typesafety kicks in and tells me of any issue.
Actually while doing it I was wondering about the verbosity of this approach. I end up with all tables/sequences/whatnot defined whereas only the delta really matters. That's why I'm unsure about it. On the other hand, type safe migration rocks :)
Specifically, if you find anything missing from jOOQ's DDL support, I'd love to know - there's still lots of room for improvement!
Well, one little issue was the lack of unique support at table creation, I had to add the constraint later on:
create.createTable(AUTHOR)
.column(AUTHOR.ID, SQLDataType.INTEGER.nullable(false))
.column(AUTHOR.FIRST_NAME, SQLDataType.VARCHAR.length(50))
.column(AUTHOR.LAST_NAME, SQLDataType.VARCHAR.length(50).nullable(false))
.column(AUTHOR.DATE_OF_BIRTH, SQLDataType.DATE)
.column(AUTHOR.YEAR_OF_BIRTH, SQLDataType.INTEGER)
.column(AUTHOR.ADDRESS, SQLDataType.VARCHAR.length(50))
.execute();
create.alterTable(AUTHOR).add(constraint("PK_T_AUTHOR").primaryKey(AUTHOR.ID)).execute();
On the way I spotted as well that the constraint name isn't put in the generated stuff (at least as far as I've seen), which I guess could be an issue sometime (when changing it mainly, by dropping/create). Having it as well in the generated class would be sweet.
Or, you could write a blog post, in case of which the content belongs to you and we don't have to transfer it. That might be even better, because we won't need to review formatting, etc...
well, actually I changed the way migration are triggered to have them done from a main method. It feels closer from "real life" to me, yet it makes the example a bit more complex. Is still fine for you ?Ehm, I don't know :-)
Hmm, I see. That's certainly an interesting approach, although I suspect that you will be missing out on a couple of features, then. For instance, what happens if you want to reference or alias such a table? You'll get a reference to o.j.e.m.p.v3.tables.Author:
In the other thread that we're having in parallel, I was discussing why it might be a good idea (from our point of view) to make all generated classes "final":The assumptions that you make right now when you subtype Author may break any time in a future release, even in a patch release, because you implicitly depend on internal API. It certainly works right now, but I'd be wary of this solution, and had better solved it more thoroughly, with an out-of-the-box feature.
How about actually referencing v4 from all of your code, and simply search-replace package names upon migration?
The effect would be the same, although the diff would be much bigger. Another option: Generate a "latest" package and always refer to that from code, adding version numbers only to historic versions.
Let me know if you come to think of another missing feature.
Ok, there you're :
https://github.com/cluelessjoe/jOOQ-java-flyway-example
It's pretty rough around the edges, and not only the edges ^^ Hopefully you'll still manage to make sense out of it.
So, what do we have there ? An ApplicationRunner as the production entry point, starting an Application, in which it all starts by some migrations. These migrations are written in Java.
As I'm not a big fan of maven install's time stuff generation, I also switched to Java model generation in the test resources, namely in ModelGenerator
No model versioning there, and actually I encountered a funny issue : inserting data actually requires the model to be there already AFAIK. So actually I'll have to add an extra migration step : in the third I update the schema, then generate the model, then in a fourth step I can data into the DB. Unless I'm missing something for sure.
Let me know if you come to think of another missing feature.
That's all for now, but I'm only prototyping, no real production code yet.
By the way, a side question: do you speak at conference ? I'm actually member of the ElsassJug, in Strasbourg, so next door to you so to say. If ever you had something to say/share, I would happily organize an event for you. What do you think of it ?
--