Can jOOQ automatically add an alias prefix for all columns of a table in the select clause?
Can jOOQ also help with the 128 byte/character name length limitation of databases?
The reason for the questions are that SimpleFlatMapper is used for the mapping.
SimpleFlatMapper requires the fetched database column names to map to the model.
Model example (really getter/setter are used):
class Head {
public Integer id;
public List<Position> positions;
...
}
class Position {
public Integer id;
public Integer headId;
...
}
The naming can be done individually:
ResultSet resultSet = dsl.select(..., POSITION.ID.as("positions_id"), ...)
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
List<Head> headers = JdbcMapperFactory.newInstance()
.ignorePropertyNotFound()
.newMapper(Head.class)
.stream(resultSet)
.collect(Collectors.toList())
However if it is a complicated model with several joins/columns it is a bit tedious.
The only solution I found was to program a function manually. Have I however maybe overlooked something and jOOQ can do it by itself or help?
Something the likes of this would be very nice (probably not the best naming, I couldn't come up with something better on the spot):
ResultSet resultSet = dsl.select()
.from(HEAD)
.join(POSITION.as("positions").prefixColumns()).onKey()
.fetchResultSet();
Or:
ResultSet resultSet = dsl.selectWithTableAlias()
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
Resulting in the following SQL:
SELECT head.id, head.***, positions.id AS positions_id, positions.headid AS positions_headid, positions.***
FROM head JOIN position AS positions ON head.id = positions.headid
Furthermore databases like Oracle and MSSQL have a limitation of 128 bytes/characters for names.
In very rare complex scenarios the alias names might reach that limit because of the needed nesting.
jOOQ cannot offer a workaround for this in some form or can it? So basically define a name that is used in the SQL and a name for the actual resulting object.
I know, very niche requirements. But they would help a lot with the mapping.
Can jOOQ automatically add an alias prefix for all columns of a table in the select clause?
There's no such feature, because the possible sets of desired auto-prefix algorithms is quite big, and jOOQ ultimately wouldn't do exactly what you want. Maybe, there's room for an SPI to help you do this, in the future. But there isn't one available yet: https://github.com/jOOQ/jOOQ/issues/11545
However, you can easily do this yourself, because every jOOQ query is just a dynamically constructed expression tree, even if you're not always using jOOQ for dynamic SQL.
You can easily write a utility and use that everywhere:
public static List<Field<?>> autoPrefix(Field<?>... fields) {
return autoprefix(Arrays.asList(fields));
}
public static List<Field<?>> autoPrefix(Field<?>... fields) {
Stream.of(fields).map(f -> f.as(myPrefixLogic(f))).collect(toList());
}
This can now be used explicitly on all DSLContext.select() calls, e.g.
dsl.select(autoPrefix(..., POSITION.ID.as("positions_id"), ...))
.from(HEAD)
.join(POSITION.as("positions")).onKey()
.fetchResultSet();
Alternatively, you can wrap your query in a derived table:
public ResultSet fetchResultSetWithPrefix(Select<?> select) {
Table<?> table = select.asTable("t");
return
dsl.select(autoPrefix(table.fields()))
.from(table)
.fetchResultSet();
}
And now, instead of calling fetchResultSet(), you call your auxiliary function:
try (ResultSet rs = fetchResultSetWithPrefix(
dsl.select(..., POSITION.ID.as("positions_id"), ...)
.from(HEAD)
.join(POSITION.as("positions")).onKey()
)) {
...
}
Another, more complex option would be to do this with a VisitListener.
Remember, irrespective of what your query looks like (because jOOQ's DSL mimicks SQL, syntactically), all your jOOQ queries are dynamic SQL queries, so you can relatively easily transform them to whatever you want, automatically.
Can jOOQ also help with the 128 byte/character name length limitation of databases?
I'll have the same reservations about this automatism as before. Where would you see jOOQ provide automatic help here? Given your naming scheme (table_column) would you like a fair distribution of 63 characters per object type? Or is the table less important than the column, and you'll truncate table names at 42 characters, leaving 85 characters for the column?
What if you wanted to fully qualify identifiers, including the schema name, as in schema_table_column? What if you project UDTs, meaning you'll get schema_table_column_attribute1_attribute2
I don't think any automation can be provided by jOOQ which would suit all needs. However, as shown above, it is very simple to implement this only once for your entire application, making sure the naming is always applied correctly. And if an SPI were available, you could implement your abbreviation logic there.
Related
I have generated a number of JOOQ classes from my database. I want to easily filter my tables by customer while maintaining the strong type of my tables.
This is what I want to be able to do:
// Generated class books
JBooks books = JBooks.BOOKS;
// get ownershipCheck (this could be more complicated, possibly joining multiple tables)
Condition ownershipCheck = books.customer().ID.eq(currentCustomer);
// desired output that I can do further operations on
JBooks filteredBooks = selectFrom(books).where(ownershipCheck).asTable();
// a bunch of random operations using the functionality from JBooks
db.select(filteredBooks.AUTHOR, filteredBooks.PUBLISH_DATE, ...etc)
Unfortunately, I can't do this. I get a Table<JBooksRecord> instead and I see no way to cast my new Table to JBooks
This is being worked on through:
#8012 "Override Table.where(Condition) methods in generated tables"
#1969 "Add support for views expressed in jOOQ"
In short, a table can accept a predicate and the result is a modified table of the same table type, exposing the same type safe column expressions. In generated SQL, this can either produce a derived table or be inlined into the calling SQL statement.
As of jOOQ 3.11, these features are not yet available.
I have a Java, GraphQL, Hibernate, PostgreSQL, QueryDSL application that queries a very large PostgreSQL table with over 275 columns.
I've created a GraphQL schema with the 25 most popular columns as query-able fields. I'd like to add a generic "field" input type that consists of a name (the db column name + "_" + operation (like gte, gt, contains, etc.) and a value (the value the user is searching for).
So when the user (in GraphiQL) enters something like (field:{name:"age_gt", value:"50"}) as a search input to the GraphQL query, I can come up with: "age > 50".
All that works fine, but when it's time to create the Predicate and add it to the whole query ( booleanBuilder.and(new Predicate) ), I cannot figure out how to create a Predicate that just contains a raw String of SQL ("age > 50").
I've created several Predicates the "right" way using my entity POJO tied to Hibernate and the jpa generated "Q" object. But I need the ability to add one or more Predicates that are just a String of SQL. I'm not even sure if the ability exists, the documentation for QueryDSL Predicates is non-existent.
I'm thinking PredicateOperation() might be the answer, but again, no documentation and I cannot find any examples online.
My apologies for not posting code, all my stuff is behind a firewall on a different network so there's no cut and paste to my internet machine.
In Hibernate its possible to inject arbitrary SQL using custom functions or the FUNCTION-function (introduced in JPA 2.1). In QueryDSL its possible to inject arbitrary JPQL/HQL through TemplateExpressions. Combined you get:
Expressions.numberTemplate("FUNCTION('SUM', {0}), x)
However, age > 50 as expression is probably valid JPQL as well, so one can just write:
Expressions.numberTemplate("SUM(age)")
Either way, its probably best to create a visitor that traverses the GraphQL query and creates the proper expression in QueryDSL, as TemplateExpressions are prone to SQL injection.
I am busy developing a SQL query builder util which will allow me to easily and quickly write SQL queries. I have the fundamentals sorted for the actual query builder but I am stuck when it comes to using some form on String literals/constants for the database entities (i.e. the tables and columns).
What I would like is to do something like this:
String sqlQuery = queryBuilder.select(Tables.Users.FirstName)
.where(Tables.Users.Age >= 10)
.build()
.toString();
I've read up quite a bit on enums and how to nest them but it seems like I will have to create an enum for every table (each containing specific columns). If possible I would like to have one class, which contains all the tables and for each table all the columns.
This should allow me to "build" a string literal by simply calling something like:
Tables.Address (returns the string value for 'address' table)
Tables.Address.Country (returns the string value for 'country' column)
This is what I've tried so far using enums but not quite what I wanted.
public enum Table {
Users("users"),
Addresses("addresses"),
private String tableName;
Table(String tableName) {
this.tableName = tableName;
}
public String getName() {
return tableName;
}
}
public enum Column {
ID(Table.Users, "id"),
NAME(Table.Users, "name");
private Table table;
private String columnName;
Column(Table table, String columnName) {
this.table = table;
this.columnName = columnName;
}
public String getColumnName() {
return columnName;
}
}
First, I do not really understand why are you trying to reinvent the wheel that has been already invented many many times.
So, if you are developing library in order to use it take a look on JOOQ. If however you really want to develop your own query builder you may probably find as useful my project BeanCrumbs.
Enums aren't the way to go; they aren't flexible enough. You need to access the metadata for each database schema you attach to.
I never see why solutions like yours are considered better than just writing SQL and executing it with JDBC. It's just as much work to use your builder as it is to write SQL.
FYI - You realize that you aren't the first or the best attempt to solve this problem. Spring JDBC template takes the boilerplate and work out of interacting with relational databases. iBatis has been around for a long time and is well proven. There are lots of ORM solutions, like Hibernate, but those are a bridge too far. I wouldn't recommend those. JPA is built into Java EE spec.
I would not want to work on a team that reinvented this wheel and forced me to prefer it over commodity, proven solutions.
I have been using ActiveJDBC quite successfully.
See first point in Design principles, this is as far as it goes in applying "convention over configuration" principle
I can't believe I'm asking this, but...
Is there any way, in Java, to execute a SQL statement (not JPQL) and map the results to a List of Plain Old Java Objects?
I want to be able to create small lightweight POJO objects and then have them populated by raw SQL queries. I'm expressly NOT looking to create complex objects: just primitives, with no relationships.
Everything seems to be centered around JPA/JPQL, but the problem with that is that I do not want to bind my objects to a specific table.
I feel like I'm either:
(a) on crazy pills, or
(b) missing something fundamental
A lightweight mapper is not available as part of the JDK itself. You could either roll-your-own simple mapper using Java's standard JDBC API (in fact JPA implementations build on top of that) or you could have a look at external libraries that provide simple SQL-to-Object mappers. I know MyBatis (formerly known as iBatis).
A) No, I think you're not on crazy pills and B) is it possible that you just missed JDBC?
Sormula may be able to do what you want. You would need to extend Table and override getTableName() and/or getQualifiedTableName() to supply the desired table name since sormula normally associates one POJO to one table. See example 2a and example 3a.
jOOQ has a couple of Record -> POJO mapping capabilities that will probably do the job for you (although jOOQ can do much more). Here's an example:
// A "mutable" POJO class
public class MyBook1 {
public int id;
public String title;
}
// The various "into()" methods allow for fetching records into your POJOs:
List<MyBook1> myBooks = create.select().from(BOOK).fetchInto(MyBook1.class);
Taken from the manual here:
http://www.jooq.org/doc/latest/manual/sql-execution/fetching/pojos/
The mapping algorithm is described in the Javadoc:
http://www.jooq.org/javadoc/latest/org/jooq/impl/DefaultRecordMapper.html
While the above example makes use of jOOQ's DSL API, you can do with plain SQL as well:
List<MyBook1> myBooks = create.resultQuery("SELECT * FROM BOOK")
.fetchInto(MyBook1.class);
You can even operate on a JDBC ResultSet, using jOOQ only for mapping:
ResultSet rs = stmt.executeQuery();
List<MyBook1> myBooks = create.fetch(rs).into(MyBook1.class);
I have a couple of sql views with composite primary keys that I want to query, and since Hibernate makes it a pain to work with composite keyes, I'm using createSQLQuery. The problem is that this method can only return a List, and I need to refer to the colums by their index.
Any chance I could do something like jdbc and refer to the columns by their sql name instead of their index?
Query query=session.createSQLQuery("your query");
query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> aliasToValueMapList=query.list();
As you can figure out from code, the list contains Map objects representing each row. Each Map object will have column name as key and value as value.
Note: This work for SQLQuery, if your using AliasToEntityMapResultTransformer on hql query without specifying aliases you will get index value as key.
If you are again transforming aliasToValueMapList to your POJO list, I advice you to create your own
ResultTransformer and return your custom object from 'transformTuple' method.
Your question is ambiguous - in the first paragraph you want to refer to columns by index and in the second, by sql name. Since by index is easy, I'll assume by name.
First of all, you can use the doWork method to access the underlying JDBC connection and handle it as you would with pure JDBC:
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
connection.prepareStatement(...
}
});
Or, you can use query.getReturnAliases which returns a String[] of the column names. For effciency, I'd probably build a Map of alias to index and then you can do something like result[map.get("column name")].
But really, Hibernate handles composite keys pretty easily when using xml mappings (haven't tried with annotations). It's a little more work up front and there are a few issues with complex relationships (mainly when foreign key names/spans don't match), but once you create the id class and map it, you can stick with HQL/Criteria and get all the benefits of lazy loading, simple joins, dirty checking, etc.
I got the same problem but it solved when i used this
Query query=session.createSQLQuery("your query");
query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
I get the result with header name but i got a new problem when i create a new column in sql query Select 'DCA5E3' as Shipmentcolor from Employee.class
But in this case i got SHIPMENTCOLOR: "D".
How to get whole value of SHIPMENTCOLOR.