1. ColumnExp is now parameterized on the type of the column it represents. This
enables item number two.
2. The beginnings of a very primitive implementation of individual field
selection, which looks like:
List<Tuple2<Integer,String>> results = from(FooRecord.class).where(
FooRecord.ID.in(someIds)).select(FooRecord.ID, FooRecord.NAME);
I don't advise using this new functionality just yet, because it's very
primitive and will probably break if you try to do anything fancy. Plus it
doesn't interact with the cache at all yet.
However, I wanted to get the typed ColumnExp bits in there so that I can
migrate all the existing projects over and get that painful business out of the
way. Note for the jumpy: migration in this case means eliminating a bunch of
unchecked type usage warnings, rather than behavioral changes. The existing
Depot functionality continues to work exactly as is. The types are not used
except for in the new individual field selection code.
One bit of code that will break is calls to updatePartial() which used to take
a Map<ColumnExp, ?> and now take a Map<ColumnExp<?>, ?>. Since those were
already in generics land, they won't just fail with an unchecked type warning,
the compiler will reject the old call (the calls are binary compatible, so
unrecompiled code will still work fine). Fortunately the Map-taking
updatePartial is not called that much outside of MSOY, which I have already
migrated to fully parameterized ColumnExp land.
having a separate deleteFrom(record).where(...).delete() builder, but
from(record).where(...).delete() was so much nicer that I decided that
preventing the specification of invalid clauses could be deferred from compile
to runtime.
I could do some fiddly things with return types and have from() return a
builder that then subsequently pigeon-holed you into deletability or not
depending on whether you called things like orderBy(), but to do so in Java
would require that I override every method to specialize its return type, which
presents something of a maintenance annoyance.
With this you can write queries like:
MaxPointsRecord max = from(MaxPointsRecord.class).
override(DailyScoreRecord.class),
where(DailyScoreRecord.KEY.eq(key)).load();
List<DailyLeaderRecord> top = from(DailyLeaderRecord.class).
override(DailyScoreRecord.class).
join(DailyScoreRecord.KNIGHT_ID, KnightRecord.KNIGHT_ID).
where(DailyScoreRecord.KEY.eq(key),
DailyScoreRecord.POINTS.eq(max.maxPoints)).
select();
After configuring your query, you can load(), you can select(), you can
selectKeys() and you can selectCount(). Other improvements include configuring
cache behavior (from(FooRecord.class).noCache().where(...).select()).
random() became randomOrder() because I think that seeing random() stuffed in
the middle of a query is insufficiently communicative.
Next, I'm going to look into extending the query builder to allow things like:
from(MemberRecord.class).
where(MemberRecord.CREATED.greaterThan(date)).
select(MemberRecord.AVATAR_ID, MemberRecord.HOME_SCENE_ID);
// yields List<Tuple2<Integer, Integer>>
which will require adding types to ColumnExp, which I hope to do in a
moderately backwards compatible way. If I can, I may use those types to enforce
type correctness and turn things like the following into a compiler error:
MemberRecord.MEMBER_ID.greaterThan("bob")
If I had a more sophisticated type system, I could even make an attempt to call
greaterThan() on a ColumnExp<String> a compilation error.
If the above doesn't turn out to be a rabbit hole extraordinairre (it probably
will), I'd like to look into obviating the need for most of the FieldOverride
and FromOverride fiddling, and support things like:
from(MemberRecord.class).
where(MemberRecord.CREATED.greaterThan(date)).
select(Exps.max(MemberRecord.SESSIONS));
// yields List<Integer>
which would take Depot in the direction of being a (somewhat) type safe wrapper
around SQL. This would go a long way toward bridging the conceptual gap between
"the SQL I know I want to write" and "how the fuck do I do this with this Java
API".
I don't think I'm going to be able to get too far with the type-safe wrapper
approach without propagating types all over the place, which may result in
backwards compatibility breakage. If things get really bad, I may just have to
pinch off the Depot 1.x series and venture into the green fields of Depot 2.x.
I'll see how far I can get before taking such drastic action, however.
various query clauses.
Something like this:
MaxPointsRecord max = load(MaxPointsRecord.class,
new FromOverride(DailyScoreRecord.class),
new Where(DailyScoreRecord.KEY.eq(key)));
List<DailyLeaderRecord> top = findAll(DailyLeaderRecord.class,
new FromOverride(DailyScoreRecord.class),
DailyScoreRecord.KNIGHT_ID.join(KnightRecord.KNIGHT_ID),
new Where(Ops.and(DailyScoreRecord.KEY.eq(key),
DailyScoreRecord.POINTS.eq(max.maxPoints))));
becomes this:
MaxPointsRecord max = load(MaxPointsRecord.class,
from(DailyScoreRecord.class),
where(DailyScoreRecord.KEY.eq(key)));
List<DailyLeaderRecord> top = findAll(DailyLeaderRecord.class,
from(DailyScoreRecord.class),
join(DailyScoreRecord.KNIGHT_ID, KnightRecord.KNIGHT_ID),
where(DailyScoreRecord.KEY.eq(key),
DailyScoreRecord.POINTS.eq(max.maxPoints)));
and the imports for Where and FromOverride go away. It's a modest improvement
in readability.
Both COL.join(OCOL) and join(COL, OCOL) are supported; one can choose based on
the aesthetic demands of an individual query.
sure what this is really testing. If the CRUD test with CustomType works, then
transforming field marshallers work, so why do we need to make sure they have a
specific class name?
the build, but other errors in class processing seem to lead to skipping, so I'm not rocking the
boat.
If @GeneratedValue is on a field without an @Id, depot creates the necessary indices to generate the
value, but it doesn't actually do the generation on insertion. Rather than making it work without a
good use case for it, I'm adding this check to keep people from stumbling into a broken design.
One test no longer works, because the StringArray transformer now inherits
the fromPersistent method from StringBase, and has a generic return type.
Perhaps we can update FieldMarshaller to deal with Types instead of classes
when validating... But for now I did what all bad programmers do when a test
fails: comment it out.