I want to append a new column in the fashion of
public CachedRowSet addColumn(cachedRowSet Original,List<item> column, String columnName);
or
public CachedRowSet addColumn(cachedRowSet Original,int column,String columnName);
with the column value repeated if it is a primitive.
What is the best way to do this?
Hmm.. hard to answer without knowing the context. Who is providing that CachedRowSet? They might or might not offer a way to generate a new instance. Are you using CachedRowSetImpl from the RI?
The RowSet is not really intended for that. Can you add it to the generating SQL? SELECT a,b,'additional' from .... Or you can use your CachedRowSet and generate JoinedRowSet with a FULL_JOIN with a single field result set.
You can't do that in SQL, let alone CachedRowSet, without executing DDL, and CachedRowSet doesn't support that. The part about a repeating value is an elementary violation of 3NF. You probably don't want to do any of this.
Related
I'm extending on my last question I asked about jOOQ. In the Hibernate models the #Filter annotation gets used, and I want to apply this same 'default filter' to the jOOQ queries. As I'm passing a jOOQ query to the nativeQuery(org.jooq.Query query, Class<E> type) I was wondering if it's possible to extract the table (TableImpl<?,?>) used from the FROM clause in the jOOQ query (org.jooq.Query).
This is what I've tried:
private static <E> SelectConditionStep<Record> applyDefaultFilters(Class<E> type, SelectConditionStep<Record> query)
{
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(
query
.getQuery()
.asTable()
.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(query.getQuery().asTable().field("deleted", Boolean.class).eq(false));
}
}
return query;
}
The result is this SQL, which is not what I want. I want it to filter the corresponding table.
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `alias_100341773`.`organization_id` = ?
and `alias_17045196`.`deleted` = ?
)
I want this
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `EventGroup`.`organization_id` = ?
and `EventGroup`.`deleted` = ?
)
Is this possible at all? And if not, what possible other routes are there? (aside from the obvious passing the table to the function)
Using jOOQ 3.16 query object model API
jOOQ 3.16 introduced a new, experimental (as of 3.16) query object model API, which can be traversed.
On any Select, just call Select.$from() to access an unmodifiable view of the contained table list.
An alternative, dynamic SQL approach for the ad-hoc case
Every time you're trying to mutate an existing query, ask yourself, is there a more elegant way to do this using a more functional, immutable approach do dynamic SQL? Rather than appending your additional predicates to the query, why not produce predicates from a function?
private static Condition defaultFilters(Class<?> type, Table<?> table) {
Condition result = noCondition();
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
result = result.and(table.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type))
result = result.and(not(table.field("deleted", Boolean.class)))
}
return result;
}
And now, when you construct your query, you can add the filters:
ctx.select(T.A, T.B)
.from(T)
.where(T.X.eq(1))
.and(defaultFilters(myType, T))
.fetch();
A generic way to transform your SQL
If you really want to mutate your query (e.g. in a utility for all queries), then a transformation approach might be better suited. There are different ways to approach this.
Using views
Some RDBMS can access session variables in views. In Oracle, you'd be setting some SYS_CONTEXT variable to your organization_id inside of a view, and then query only the (possibly updatable) views instead of the tables directly. MySQL unfortunately can't do the equivalent thing, see Is there any equivalent to ORACLE SYS_CONTEXT('USERENV', 'OS_USER') in MYSQL?
I've described this approach here in this blog post. The advantage of this approach is that you will never forget to set the predicate (you can validate your view source code with CI/CD tests), and if you ever forget to set the session context variable, the view will just not return any data, so it's quite a secure approach.
Together with the WITH CHECK OPTION clause, you can even prevent insertions into wrong organization_id, which improves security.
Using a VisitListener in jOOQ
This is the most powerful approach to do this in jOOQ, and exactly what you want, but also quite a tricky one to get right for all edge cases. See this post about implementing row level security in jOOQ. Starting from jOOQ 3.16, there will be better ways to transform your SQL via https://github.com/jOOQ/jOOQ/issues/12425.
Note, it won't work for plain SQL templates that do not use any jOOQ query parts, nor for JDBC based queries or other queries that you may have in your system, so be careful with this approach as you might leak data from other organisations.
Of course, you could implement this step also on the JDBC layer, using jOOQ's ParsingConnection or ParsingDataSource, that way you can intercept also third party SQL and append your predicates.
This can work for all DML statements, including UPDATE, DELETE. It's a bit harder for INSERT, as you'd have to transform INSERT .. VALUES into INSERT .. SELECT, or throw an exception if someone wants to insert into the wrong organization_id.
Using a ExecuteListener in jOOQ
A bit more hackish than the above VisitListener approach, but generally easier to get right, just regex-replace the WHERE clause of all your statements by WHERE organization_id = ... AND in an ExecuteListener.
To play it safe, you could reject all queries without a WHERE clause, or do some additional trickery to add the WHERE clause at the right place in case there isn't already one.
Using jOOQ's equivalent of Hibernate's #Filter
jOOQ's equivalent of Hibernate's #Filter is the Table.where(Condition) clause. It's not an exact equivalent, you'd have to prevent direct access to T in your code base and make sure users access T only via a method that replaces T by T.where(defaultFilters(myType, T)) instead.
This approach currently loses type safety of the T table, see: https://github.com/jOOQ/jOOQ/issues/8012
I have generated jooq records via the code generator. In my record I have a timestamp field which I want to set as the DB current dat time.
The below works:
MyRecord myrecord = new MyRecord();
myrecord.setCreateTime(Timestamp.from(Instant.now()));
But I want to use the DB time , so something like this:(obviously there is a compile error in the below syntax)
myrecord.set(MY_RECORD.CREATE_TIME, DSL.currentTimestamp());
What is the best way to achieve this?
Using jOOQ's INSERT statement directly, rather than UpdatableRecord
jOOQ's UpdatableRecord API doesn't allow for using SQL expressions as values, only actual values can be passed to the Record.set(Field<T>, T) method, by design. If you wish to introduce SQL expressions, you should revert to using classic SQL, e.g. an INSERT:
DSL.using(configuration)
.insertInto(MY_TABLE)
.set(...)
.set(MY_TABLE.CREATE_TIME, DSL.currentTimestamp())
.execute();
Preferred approach: Use SQL
Another option would be to resort to using triggers or SQL DEFAULT expressions that you can put on the table definition directly. That way, the current timestamp will be generated regardless if you insert the record through jOOQ or through some other means.
"Another option would be to resort to using triggers or SQL DEFAULT expressions that you can put on the table definition directly"
Agreed, but there should not be a need to resort to triggers depending on your application and/or database platform. Columns can be set as DEFAULT, eg
MY_COLUMN DATE DEFAULT SYSDATE
which still allows someone to override the value with a NULL, or you can do
MY_COLUMN DATE DEFAULT ON NULL SYSDATE
to put the default in place even if a null is explicitly requested.
We are examining 2 different methods to do our entities updates:
"Standard" updates - Fetch from DB, set Fields, persist.
We have a Query for each entity that looks like this:
String sqlQuery = "update Entity set entity.field1 = entity.field1, entity.field2 = entity.field2, entity.field3 = entity.field3, .... entity.fieldn = entity.fieldn"
We receive the fields that changed (and their new values) and we replace the string fields (only those required) with the new values. i.e. something like :
for each field : fields {
sqlQuery.replace(field.fieldName, getNewValue(field));
}
executeQuery(sqlQuery);
Any ideas if these options differ in performance to a large extent? Is the 2nd option even viable? what are the pros / cons?
Please ignore the SQL Injection vulnerability, the example above is only an example, we do use PreparedStatements for the query building.
And build a mix solution?
First, create a hasmap .
Second, create a new query for a PreparedStament using before hasmap (for avoid SQL injection)
Third, set all parameters.
The O(x)=n, where n is the number of parameters.
The first solution is much more flexible You can rely on Hibernate dirty checking mechanism for generating the right updates. That's one good reason why an ORM tool is great when writing data.
The second approach is no way way better because it might generate different update plans, hence you can't reuse the PreparedStatement statement cache across various column combinations. Instead of using string based templates (vulnerable to SQL injections) you could use JOOQ instead. JOOQ allows you to reference your table columns in Java, so you can build the UPDATE query in a type-safe fashion.
I have a very common encapsulation problem. I am querying a table through jdbc and needs to hold the records in memory for sometime for processing.I dont have any hibernate POJO for the same table to create any objects and save.I am talking about a load of say 200 million in a single query.
The common approach is to create an object array and do casting when I need to use them. (Assume, I can get the table details like column name and data type which will be saved in some reference tables..) But this approach will be very expensive (Time) I guess when the load is taken into consideration..
Any good approach will be appreciated...
Sounds like a CachedRowSet would do the trick here. That's pretty much exactly what you want. It will take a ResultSet and suck the entire thing down, then you can work on it at your leisure.
Addenda:
I am really looking for a robust record holder with easy access on the members
But that's pretty much exactly what a CachedRowSet is.
It manages a collection of records with named (and numbered) columns, and provides typed access to those columns.
CachedRowSet crs = getACachedRowSet();
crs.absolute(5) // go to 5th row, shows you have random access to the contents.
String name = crs.getString("Name");
int age = crs.getInt("Age");
date dob = crs.getDate("DateOfBirth");
While I'm sure you can make up something on your own, a CachedRowSet gives you everything you've asked for. If you don't want to actually load the data in to RAM, you could just use a ResultSet.
Only down side is that it's not thread safe, so you'll need to synchronize around it. But that's life. How exactly does a CachedRowSet not meet your needs?
Well, if you need 200m objects in memory then you can initialize each while iterating through the ResultSet - you don't need to save the metadata
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String col1= rs.getString("col1");
Integer col2= rs.getInt("col2");
MyClass o = new MyClass(col1,col2);
add(o);
}
rs.close();
To make it more clear, the table involved is completely configurable.
That is why I cant create POJO classes prior to this task.
In that case, I'd have thought that the only real way of doing this is to turn each row into a string with delimiters (CSV, XML or something) and then create an array of strings.
You can get a list of column names returned by a JDBC query as in this answer:
Retrieve column names from java.sql.ResultSet
We are looking to to conditionally add where clauses to a SQL where class
For example we have a DAO that has a method with say 10 params.
For each of those params we check if it is null, if not we add an AND to the where clause.
The "base" query is a hard coded string and we concat it with the ANDS.
I'm looking for ideas for a more elegent way of doing this.
We are using hibernate elsewhere in the app
You can use the Hibernate criteria API to dynamically build queries.
For simplicity you can use variable argument method and start a loop for array and check for not null and concat it. otherwise you can use the Hibernate criteria API.
The Hibernate Criteria might be what you want.
http://www.mkyong.com/hibernate/hibernate-criteria-examples/
http://www.dil.univ-mrs.fr/~massat/docs/hibernate-3.1/api/org/hibernate/Criteria.html