1. DateFuncs.dayOfWeek and dayOfMonth can now be used on Timestamp. +FromDate
variants exist for using on Date.
2. hour, minute, second, etc. are now typed Number to cope with differing
opinions on the part of the underlying SQL drivers as to whether to return
Double, Long, Integer or something else.
When we go to bind values in an update statement, we either have:
- field names and a pojo from which to extract them, in which case
transformation happens naturally when we extract the current value from the
pojo
- value expressions (like 1, or "bob") which we were previously just binding
directly to the statement, which was wrong because they need to first be
transformed in the case where the field in question has an @Transform
annotation; now we get the field marshaller for the field in question and
pass the raw value to it, so that it can do the necessary transformations in
writeToStatement()
- other expressions (like LiteralExp("true") or or IntervalExp or anything else
where the database is involved in computing the final value to be assigned to
the column).
This last case still holds the potential for badness with regard to transformed
fields. We can't magically transform the value to be stored into a field if the
database is computing the ultimate value. Say, for a contrived example, that
you had a record where you stored an int column as a String for shits and
giggles:
public class MyRecord {
@Transform(IntToStringTransformer.class)
public int someValue;
}
someValue will be a string column in the database, owing to the
IntToStringTransformer converting the int to a String.
Now you come along and think to yourself, "Hey, I want to store the current
minute in my 'int' field." and you write something like:
updatePartial(MyRecord.class, MyRecord.SOME_VALUE,
DateFuncs.minute(Exps.literal(new Timestamp(System.currentTimeMillis()))));
Well, that's going to end up trying to stuff an int-valued expression (computed
by the database) into a String field, and the shit will hit the fan.
I can't really think of a non-contrived situation where this is likely to bite
us in the ass, but I don't especially like grass covered pits like this lying
around, waiting for someone to unsuspectingly step into them.
I could fail if you try to update a transformed field with anything other than
a ValueExp (or the current value from a pojo), but that would prevent you from
doing something potentially useful and safe like copying one field to another,
which both use the same transformation.
non-persistent type), and transform in getFromSet/writeToStatement, rather than
operate on T (the persistent type) and transform in
getFromObject/writeToObject.
This makes the newly added test in TransformTest work, rather than fail, as it
did previously.
safely be stored, but if rehydrated into a Set then an EnumSet would
be created and that can't store nulls.
So, check to see if the encoded String has any nulls in it and
have the builder create a HashSet instead.
so that partially constructed queries can be reused without any special care
being taken by the caller. I also switched to the use of a functional list
internally to make this whole process more pleasant and efficient.
versions with the repository.
Why, Maven, do you not offer an option to not go searching numerous sources on
the Internet every time I run a build? (And then timing out when some of those
sources are behind firewalls and I'm not running vpn at the moment.) Indeed,
why would that not be the default?
pojos which hold the results of the query. The caller can decide if they want
to use the magic of selectInto(), and be careful not to screw up the types and
order of their pojo fields, or they can boil up some plates and create a
builder for their pojo, which will be type-checked, soup to nuts.
only on a matching public constructor. This version is not type-safe, but I'll
also be adding an additional mechanism to do selections like this that uses a
type-safe builder approach, which comes at the cost of a healthy dose of
boilerplate code.
This uncovered a lot of loose geese as far as the database was concerned, which
necessitated turning a bunch of functions which had specific type into
functions which have type Number.
In general, I made the Funcs/StringFuncs/MathFuncs factory methods provide an
expression typed Number and I left the underlying SQLExpression derivations
able to be more precisely typed. So if you know that your database does not
magically promote an integer column to long when calling sum() on it, you can
force a more specific type. But really, I don't see why you would go to the
trouble. You can just call intValue() on your result and get the int you were
looking for regardless of what the database thought it would be fun to provide.
I also fixed a few HSQLDB variants (average -> avg, round(a) -> round(a, 0),
etc.) and made notes about a few things that just plain don't work in HSQLDB.
Awesome! Fortunately they're things like log-base-10, rather than more critical
functionality.
exception. I'm tempted to reinstate the NoSuchElement exception for a load()
that attempts to project fields from the selected rows, but I think it makes
more sense for a load() which aggregates over zero rows to return null than to
throw a NoSuchElement exception.