jOOQ - Fluent trigger builder API? - java

Is there any fluent "create trigger" builder? It's weird but I can't find any trigger builder example (also searched in the jOOQ manual but without success).
I would like to transform hard-coded statement:
"CREATE TRIGGER " + TRIGGER_DELETE_TAB + " " +
"BEFORE DELETE ON " + TABLE_TABS + " " +
"BEGIN " +
"DELETE FROM " + TABLE_CHORDNAMES + " " +
"WHERE " + CHORDNAME_TAB + " = " + "OLD."+TAB_ID +"; " +
"END;"
in something like that:
SQLiteDSL.createTrigger(TRIGGER_DELETE_TAB)
.beforeDeleteOn(TABLE_TABS)
.begin()
.deleteFrom(TABLE_CHORDNAMES)
.where(column(CHORDNAME_TAB).eq("OLD."+TAB_ID))
.end()
.getSQL();

jOOQ could contain an API to implement a really trivial trigger like yours seems to be, and chances are, it will in some future version (#6956).
But in order to fully support triggers, jOOQ needs a runtime model abstracting over all sorts of procedural languages first, before going into the details of vendor specific trigger features. There's a feature request "Add procedural language abstraction API", in fact: #6475
This is being worked on for the upcoming version jOOQ 3.11, which will definitely support BEGIN .. END style blocks: #6474.
For jOOQ 3.10 and less, you will need to build your own jOOQ extension API based on the plain SQL templating mechanism documented here:
https://www.jooq.org/doc/latest/manual/sql-building/plain-sql-templating
You don't need too much plumbing to get that functionality working...

I don't think you can do that, but you can use instead ExecuteListeners, which can be considered as triggers and you can do something similar that what you build with your SQL.
You can check out their documentation regarding ExecuteListeners, they also provide a few example, like query statistics listener, logging listener and so forth.
They recommend extending DefaultExecuteListener and start from their with Javadoc, they have quite a variety of methods that you can override. I am pretty sure you will find what you need.

Related

IntelliJ IDEA not recognizing SQL dialect

We use IntelliJ IDEA actively and we have our wrappers for work with DB (PostgreSQL). The thing is that when we use placeholders, SQL is stopped being highlighted.
"select * from " + schema + ".users where id = " + id + ";";
This code is not recognised as SQL, so highlighting doesn't work.
I agree with Jesper, you should try using preparedStatements, not only would your Statements get more secure, also the problem which you are currently having should be solved by it.

"show version" in Cassandra and thrift protocol

I would like to make sure that I can connect to the db from Java service from time to time (like ping). This answer suggests to use show version.
My Java service is using the Cassandra thrift client (which is deprecated) and I couldn't find an example how to execute such a query.
I have a client of type org.apache.cassandra.thrift.Cassandra.Client.
Those are the methods I found that are similar:
System.out.println("describe_schema_versions: " + nativeClient.describe_schema_versions());
System.out.println("describe_version: " + nativeClient.describe_version());
System.out.println("describe_snitch: " + nativeClient.describe_snitch());
System.out.println("describe_cluster_name: " + nativeClient.describe_cluster_name());
System.out.println("describe_keyspaces: " + nativeClient.describe_keyspaces());
System.out.println("describe_partitioner: " + nativeClient.describe_partitioner());
System.out.println("describe_token_map: " + nativeClient.describe_token_map());
How can I execute query to get Cassandra server version like this?
cqlsh> show version;
[cqlsh 3.1.8 | Cassandra 1.2.19 | CQL spec 3.0.5 | Thrift protocol 19.36.2]
Or is there an alternative query preferred as "ping"?
if you just want make a health check, i think run a simple query is a good choice.
SELECT now() FROM system.local
here is the doc about how thrift client run query.
Cassandra.Client (apache-cassandra API) - Javadoc Extreme
Worth noting, but some folks have had a hard time with system.now() due to the integer format with Thrift. This is largely due to the fact that system.local is a cql3 table, and not a Thrift column family.
To that end, this returns a string, which may be easier to deal with:
aploetz#cqlsh> SELECT release_version FROM system.local;
release_version
-----------------
3.11.6
(1 rows)
Or via the Cassandra CLI:
[default#system] get local['local']['release_version']
You can also pull thrift_version, if you'd rather do that (which is also a string).

Spring Repositories and DATA_FORMAT with nativeQuery=false

I am trying do something super simple but with Spring Repositories somethings is a bit hard. Basically I wanted to group by with DATE_FORMAT, example:
#Query("SELECT " +
" new users.bridge.models.dto.PerformanceDTO(sum(t.gl), sum(t.gl)) " +
"FROM " +
" Transaction t " +
"GROUP BY DATA_FORMATE(t.createdDate,'%Y-%m-%d')")
But it throws a syntax error. Is there a way to do that with spring repositories? I don't want to use nativeQuery=true flag, otherwise I can not use this syntax
new gara.users.bridge.models.dto.PerformanceDTO(sum(t.gl), sum(t.gl))
UPDATE:
The erros are:
all the java stack is quite big but:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: asc near line 1, column 180 [SELECT new gara.users.bridge.models.dto.PerformanceDTO(sum(t.gl), sum(t.gl),t.createdDate) FROM gara.model.db.Transaction t GROUP BY DATE_FORMAT(t.createdDate,'%Y-%m-%d') asc]
If you specify nativeQuery=false (the default) you need to use valid JPQL.
JPQL doesn't know the function DATE_FORMAT but you can use the generic FUNCTION function which allows you to call arbitrary SQL functions.
So a group by clause like this should work: GROUP BY FUNCTION('DATA_FORMAT', t.createdDate,'%Y-%m-%d')
Just be aware that such queries aren't portable between databases.
but with Spring Repositories somethings is a bit hard.
You can always fall back on custom method implementations which shouldn't be much harder than implementing your repository yourself in the first place.

Test if a good query is being issued

It is quite usual to find myself writing unit tests against database calls and I always hit the same issue: How to validate if a good query is being sent to the database?
Example, I have this class that will send a final update to the database in the following form:
update credential set password_hash = ?, password_crypt = ?, password_plain = ? where id = ?
(this is a password migration tool, please dont mind the security issues with the password_plain field)
Writing the test class for this class, I have mocked the database access class (in this case I am using Spring JDBCTemplate) and captured the issued sql. After I have the sql, I do the following checks:
String space = "\\s+";
String optSpace = "\\s*";
String something = ".+";
String optSomething = ".*";
sql = sql.toLowerCase();
assertTrue(sql.matches(optSpace + "update" + space + "credential" + space + "set" + space + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_hash" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_crypt" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_plain" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "where" + space + optSomething + "id" + optSpace + "=" + optSpace + "\\?" + optSomething));
With those checks I am indeed validating if the issued SQL contains the most important parts of the update like:
correct table is being updated
all the 3 fields are being updated to values passed as parameters
the id is being used in the where statement, with its value as a parameter
I could simply validate if the issued query is exactly the expected query above, but that would make the test too restrict for future changes and would force a failure if any part of the query was changed, even if the update stays correct. As I think that tests are written to be used mostly in the future (when you are changing software and need more reassurance for that) and not in the present, this option would make the test kinda useless.
Well, finally, I declare my question: Which better options do we have for validating the issued SQL?
I see a lot of projects that create small embedded databases with a small amount of data for testing classes that deal with the database, but I wanted to write a more pure unit test alternative (if I can call that)
I don't think there's a good alternative to testing against a real database (even if it's embedded etc). At the moment you're testing that your SQL is syntactically valid, but will it actually work. e.g. do you know if it would violate constraints etc...
Mocking etc is all well and good but at some stage you have to test against the database. I would ensure that where possible you don't test against the database, and then bite the bullet and construct tests around a small database (with suitable rollback/rebuilds etc) to actually confirm correct db functionality.
I'd implore you to reconsider the benefits of these tests because
it is verifying implementation not behavior
when you change the SQL query in the future (or someone just adds a innocuous space by mistake), you'd have a failing test even if you have preserved behavior.
For the final DataAccessLayer, I'd recommend writing an integration test. One that runs against a real but minimal DB. Sure these tests would be slow but the confidence that they offer is worth it.
So write tests against GetCustomers() and verify the returned DTO contains the right data vs verifying that the SQL query you issued is X.
don't assert your sql. it's pointless. you will end up comparing passed sql string to another string (also created by you so there is no validation) or you'll have to implement your own database. instead just use existing one. check is query returned correct data or correctly changed data in database. use dbunit or sth similar.

Issue with a JPA query

I am trying to execute the following JPA query:
public static final String UPDATE_INVENTORY_CUSTOMER_FOR_AMS_MAPPING = "UPDATE Inventory inventory SET"
+ " inventory.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID
+ " ,inventory.lastUpdateUserId = :" + DataAccessConstants.PARAM_USER_ID
+ " where inventory.amsConsignorName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME
+ " and inventory.amsConsignorOrgCd = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD
+ " and inventory.amsConsignorTypeName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE
+ " and inventory.status.code in (:" + DataAccessConstants.PARAM_STATUS + ")";
but it is seeing the following:
update ATL_INVENTORY, set CONSIGNOR_ID=?, LAST_UPDATE_USER_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (CODE in (? , ? , ? , ?))
Any ideal as to why there is a comma after the table name?
Solution
I had to change the original query to the following:
update Inventory inv set "
+ "inv.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID + " "
+ "where inv.amsConsignorName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME + " "
+ "and inv.amsConsignorOrgCd =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD + " "
+ "and inv.amsConsignorTypeName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE + " "
+ "and exists(select 1 from Code code where inv.status = code and code.code in (:" + DataAccessConstants.PARAM_STATUS + "))
Which then produced this:
update ATL_INVENTORY set CONSIGNOR_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (exists (select 1 from ATL_CODE code1_ where ATL_INVENTORY.STATUS=CODE_ID and (code1_.CODE in (? , ? , ? , ?))))
Based on a clarification located here: Incorrect SQL generated for JPA QL Update statement involving multiple entities
Your query is code as UPDATE Inventory inventory SET, but the generated SQL says update ATL_INVENTORY, set. Why is the literal SQL string not what you coded? When I encounter mysteries like this, they're usually caused by assuming that one thing is being done when in fact another is in play.
This suggests that the SQL you coded isn't being used to generate that SQL the way you're assuming. See where else this query might be coming from. I'd bet that the real source has a misplaced comma in it.
Which JPA implementation are you using? If I'm incorrect about a bad assumption, it says that there's a bug in the implementation. Have you used it before? Have you had success with UPDATE? If yes, it's definitely buried somewhere in your code base.
You have an interface with a bunch of constants in it. Personally, I don't care for a design like that. It's an anti-pattern with a name.
A bug in the JPA provider is very unlikely in my opinion so, as #duffymo said, are you sure you're using the right constant, that the code or maybe dependencies are up-to-date? I'd dig in that direction.
That being said, I really wonder why you're not using named queries (that are most of time pre-compiled by the persistence implementation at deployment time), either in the Java code or in meta-data mapping files (the fun part is that people didn't find having EJB-QL queries externalized in XML very manageable in EJB 2.x, hence the #NamedQuery annotation of JPA).

Categories