I try to use the iciql builtin support for database and table versioning.
I read http://iciql.com/table_versioning.html but I want to supply a feedback. My main goal of this post is to validate my approach.
The problem:
- I want to add a new property
Date createdDate
to my User
entity
I understand from documentation that I must add the IQVersion
annotation on User
class definition. My User
class now looks like:
@Iciql.IQVersion(1)
public class User extends IciqlEntity {
// old properties annotated with @IQColumn
@Iciql.IQColumn
private Date createdDate;
// getters and setters
}
Also I understand that I must create a DbUpgrader
. So I created EntitiesUpgrader
:
@Iciql.IQVersion(1)
public class EntitiesUpgrader implements DbUpgrader {
@Override
public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) {
System.out.println("EntitiesUpgrader.upgradeDatabase");
System.out.println("fromVersion = " + fromVersion);
System.out.println("toVersion = " + toVersion);
return false;
}
@Override
public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) {
System.out.println("EntitiesUpgrader.upgradeTable");
return false;
}
}
When I run my application I see in console:
EntitiesUpgrader.upgradeDatabase
fromVersion = 0
toVersion = 1
My first question is:
- when is called
upgradeTable
method?
I understand that I must create a custom dao V1Upgrade
that contains all my modifications and use this class in my DbUpgrader.upgradeDatabase
with if (toVersion == 1) _do_my_modifications_
.
public interface V1Upgrade extends Dao {
@SqlStatement("ALTER TABLE USER ADD COLUMN CREATEDDATE DATE")
void updateUserTable();
}
I use h2
db so maybe it's an option to add a default value in updateUserTable
.
Again, I prefer the freedom of Dao from JDBI:
public interface MyDao {
@SqlUpdate("create table something (id int primary key, name varchar(100))")
void createSomethingTable();
@SqlUpdate("insert into something (id, name) values (:id, :name)")
void insert(@Bind("id") int id, @Bind("name") String name);
@SqlQuery("select name from something where id = :id")
String findNameById(@Bind("id") int id);
}
or
public interface SomeQueries {
@SqlQuery("select name from something where id = :id")
String findName(@Bind("id") int id);
@SqlQuery("select name from something where id > :from and id < :to")
List<String> findNamesBetween(@Bind("from") int from, @Bind("to") int to);
@SqlQuery("select name from something")
Iterator<String> findAllNames();
}
I don't want to be forced to extend a Crud/Generic Dao
. The Crud
operations are useful in some contexts but in the DbUpgrader context I don't see the utility. This is only an observation, maybe it's more clear for me and it's not a real issue for other people.
In the past, I implemented a similar concept for one project and I can say that this feature from Iciql
is very useful.
I wrote a iciql
module for my proof of concept generic dao project that try to use open session in view pattern. I don't know if it's a correct approach to use Iciql
.
In one of my project (web project using Pippo) I used the above generic dao with success. All I need to do is to supply a IciqlDbFactory that contains a single method Db getDb()
.
So my concrete implementation is:
public class IciqlDao<T extends Entity> extends IciqlEntityDao<T> {
public IciqlDao() {
this(DbUtils::getDb);
}
}
public class DbUtils {
// private static final String url = "jdbc:h2:mem:matilda";
private static final String url = "jdbc:h2:./data/matilda";
private static final String username = "";
private static final String password = "";
private static DataSource dataSource;
private static Db genericDb;
static {
dataSource = JdbcConnectionPool.create(url, username, password);
}
public static Db getDb() {
RouteContext routeContext = getRouteContext();
if (routeContext == null) {
// no http request response cycle
if (genericDb == null) {
log.debug("Create generic Db instance");
genericDb = createDb();
}
return genericDb;
}
Db db = routeContext.getLocal("db");
if (db == null) {
log.debug("Create request Db instance");
db = createDb();
// share db instance using local storage
routeContext.setLocal("db", db);
}
return db;
}
public static void closeDb() {
RouteContext routeContext = getRouteContext();
if (routeContext == null) {
if (genericDb != null) {
log.debug("Close generic Db instance");
genericDb.close();
}
} else {
Db db = routeContext.removeLocal("db");
if (db != null) {
log.debug("Close request Db instance");
db.close();
}
}
}
private static RouteContext getRouteContext() {
return RouteDispatcher.getRouteContext();
}
private static Db createDb() {
Db db = Db.open(dataSource);
db.setDbUpgrader(new EntitiesUpgrader());
return db;
}
}
Any observation is welcome. Is my approach a good one?