Typesafe database interaction with Java 8
benjiweber.co.uk
benjiweber.co.uk
By default, a method reference or a lambda doesn't carry a reflective description of itself to be lightweight. This description is generated by the compiler, pass to the metafactory but once the method reference object is created the recipe of the method reference is lost. That is true but in one case, if the lambda or the method reference is Serializable, in that case, the recipe need to be kept in order that the runtime can deserialize the method reference. There are several ways to force a method reference to be serializable, the easiest way is to declare the functional interface that will receive the method reference as inheriting from Serializable. This means you can not use java.util.function.Function, you have to create your own interface that inherits from Serializable. Once you have done that, you just have to serialize the method reference to an array and deserialize it by hand to extract the recipe. So the only thing you need is a code that simulate de-serialization withtout using the classical ObjectInputStream of Java because it will not let you sneak at the recipe. The code here [1], do what you want.
cheers,
[1] https://github.com/forax/jayspec/blob/master/jayspec/src/com...
I wish Java would do something about getters and setters. It's very awkward that they are not incorporated into the language but so many libraries assume they exist that it's a defacto requirement to generate the same boilerplate constantly. I wish the language itself would provide default 'simple' getters/setters the way Groovy does and incorporate them throughout so I could use e.g.
.set(Person::firstName) //Reference a default setter method for the firstName member if one isn't explicitly defined
instead of .set(Person::setFirstName)public int Age { get; set; }
Code becomes less OO and more procedural filled with anaemic models / bags of state. Practices like Tell don't Ask, Information Hiding and Rich Models, slip away.
The issue, as I see it, isn't the ease of creating accessors; the issue is that students do not seem to learn about the practices you've mentioned.
I demonstrate "Tell, Don't Ask", "Information Hiding", and "Encapsulation" in the following presentation:
Frankly...good. If there is one thing Java desperately needs, it's less OO code.
Here, we are really just using code declaratively, then doing extra work to map, etc. Then, folding in CGLib too? And, given that the queries are written in code, modifications to columns manipulated and so forth require re-compilation anyway.
Accessing the DB is not rocket science. I am not sure why we find new ways to complicate it so much. It is primarily just grunt work when done by hand. So, I do understand the desire to avoid that part of it.
This being the case, I will stick with my homegrown code generation tools for DB interfacing. Over the years, new tools have come and gone, which simply seem to move the problems around (Hibernate, anyone?) And, during that time, I haven't found anything simpler or more straightforward than a tool I wrote over ten years ago to generate the (typesafe) base DAO layer from simple queries.
Now, this is really reminding me of EJB. Seriously, it's how we end up with these insane "standards", the embrace of which is probably at least partly responsible for Java's reputation as an inefficient language in which to develop.
And, there are certainly ways to approach code generation in a team environment, especially if the team was willing to put a fraction of the effort into such an approach, as they would put into learning and maintaining something like the OP. At the end of the day, you just need to get data in and out of the DB. Whether it is ten years old or homegrown shouldn't matter so much if it is clean, easily maintained, and efficient. Teams adopt project-specific patterns all the time for other parts of development. Why not for the DB access?
But, I just offered up my solution as a way to say that, in all of these years, I have yet to see a truly attractive alternative to something homegrown and "old". Many seem to agree as here we are with yet another proposal. But, things seem to be getting worse, as the OP is an example that borders on parody of previous offerings. Dynamic proxies and CGLib? Really?
When I read the title, I thought maybe someone had nailed it. But, we start with ugly, somewhat declarative code that surfaces SQL syntax, pass around some function names, throw in some mapping, get some dynamic proxies in the mix and, oh yeah, you're also gonna need CGLib, etc. I literally thought it was a joke. Then, I come here and the first comment is "This looks really nice".
So, aside from what I have decided to do to solve the problem, I would just ask why the OP approach should be considered a viable alternative and why we favor coalescing around such complicated anti-patterns instead of guiding our "teams" toward saner choices that work.
I've worked on a medium-sized 12-year-old "legacy" E-Banking system and it was just awesome to learn how the "old masters" had been coding so many years ago.
Nonetheless, JDBC is a low-level standard, JPA a high-level one. Both with their merits. However, there is room for a solution in between
> When I read the title, I thought maybe someone had nailed it.
If that's what you're after, then do have a look at jOOQ: http://www.jooq.org
Exactly. That is certainly part of it. There is a tendancy to replace one thing with another, simply because we can.
>However, there is room for a solution in between
Making improvements, abstracting ugly details, etc., can definitely be a good thing and there are certainly projects that do this well. What I have learned to dislike is "solutions" that make the problem worse and that abstract the problem rather than the solution. The data access layer seems to suffer disproportionately here. For instance, with the OP, we now we have ugly database code with added layers. Over-engineering at its worst. Not sure why we don't just ask the simple question: is this really the simplest, most efficient, maintainable, way to do x?
jOOQ: thanks for the pointer. I will have a closer look, but at first blush, it is quite a bit like what I wrote all of those years ago. Perhaps I should have just pushed to standardize it. edandersen would have it on his resume, and we wouldn't be having this discussion. ;)
Could be. The idea isn't novel. But no one has (publicly) gone as far as jOOQ before, from my marketing research. Here's what I mean by going "far":
http://blog.jooq.org/2013/05/03/sql-query-transformation-fun...
Actually, though, I believe you overstate the desire of stakeholders to hire unthinking lemmings who demonstrate same by having the latest inane tech on their resumes. I have also hired and managed developers. When pressed during interviews, many had no clue how the tech worked beyond the happy path and would be lost at sea when presented with the slightest edge case.
Thus, I (and other smart managers) always cared about how our devs thought and solved problems, whether they were efficient and productive, how they adapted to new domains, general intelligence, etc. When you have that, you have devs who can get any job done, even if it means learning some inane buzzword dujour tech demanded by some poor, gullible, semi-technical "stakeholder".
The better engineering companies have long recognized this. You're not going to get a gig with Google because you have Nhibernate on your resume. You are going to get the gig when you prove that you're adaptable and smart enough to think through any problem thrown at you.
So, while I know it's fun for you to write "unclebucknasty's homegrown DAO Layer", I believe it's your paradigm that may be a bit outdated.
Although waiting for Android to support the new features will still be a pain (it just got support for 7 with the release of KitKat).
Edit: The reason I bring this up (apart from being a little bit sore about it, personally), is that this means that Android is actually really far from Java 8 support, as these features were added in Java 7 at least partially to facilitate lambda support in Java 8 (see http://cr.openjdk.java.net/~briangoetz/lambda/lambda-transla...).
Now that there are new language features it may not be required.
For example (this uses SQL, I am not big on ORMs):
User user = database.queryForObject("SELECT * FROM user WHERE user_id=?", User.class, userId);External DSL = Actual target language, no simulation thereof, but String-based, no typesafety, detached bind variables, SQL injection, etc.
Internal DSL = Typesafe, compiled, but only a simulation of the real language, limited by the host language
OhmDB (http://www.ohmdb.com)
LambdaJ (https://code.google.com/p/lambdaj/)
JaQu (http://www.h2database.com/html/jaqu.html)
JIRM, I think, also played around with similar ideas (https://github.com/agentgt/jirm)
Comparing this approach with jOOQ (http://www.jooq.org), I think that method references might be somewhat limited in a SQL sense of thinking. How would you handle aliases? E.g. how would you perform a self-join, such as:
FROM person p1
JOIN person p2 ON p1.parent_id = p2.id $( colName ) is also unsafe in the sense that whatever
colName is will be thrown out directly to SQL. However,
$$( colName ) is a bit safer because it removes double
quotes and wraps the identifier
Gotta love that :-) It's obviously safer, given the amount of $. What does $$$( colName ) do? [A-Za-z_][A-Za-z_0-9]*
If not, then quote. Another reason to quote things is the madness around various SQL dialect's understanding of case-insensitivity. When quoted, case-sensitivity is enforced.