When you use cqlsh with Cassandra you can make a describe query to get the information of a table for example:
DESCRIBE TABLE emp;
And it will give you something like:
CREATE TABLE emp (
empid int PRIMARY KEY,
deptid int,
description text
) ...
....
So how can I query this using Astyanax support for CQL. I was able to query simple SELECT statements with this:
OperationResult<CqlResult<String, String>> result
= keyspace.prepareQuery(empColumnFamily)
.withCql("Select * from emp;")
.execute();
But this isn't working for DESCRIBE statements.
PD: I am really doing this to get the DATA TYPES of the table, parsing it later and obtaining for example int, int, text, so please if you have a different approach to get those, it could be awesome.
This query select column, validator from system.schema_columns; doesn't work because it doesn't return the composite keys.
DESCRIBE is not part of the CQL spec (neither CQL2 nor CQL3). If you'd like to completely reconstruct the DESCRIBE you could take a look at cqlsh implementation (look for print_recreate_columnfamily).
You could also get some more meta info from system.schema_columnfamilies:
select keyspace_name, columnfamily_name, key_validator from schema_columnfamilies;
Related
I have three tables in my database, SUBSCRIPTION, USER_ID, and an association table called SUBSCRIPTION_USER_ID.
My strategy is to use JOOQ batch with three queries, the first one to insert on row into SUBSCRIPTION, the second query to insert multiple rows into USER_ID, and finally, I need to insert the association IDs into SUBSCRIPTION_USER_ID, so I did the following:
InsertValuesStep2 insertUserIds = insertInto(
USER_ID, USER_ID.USER_ID_TYPE, USER_ID.USER_ID_VALUE);
for (String userId : subscriptionDTO.getUserId())
insertUserIds = insertUserIds.values(getValue(0, userId), getValue(1, userId));
InsertReturningStep insertReturningUserIds = insertUserIds.onConflictDoNothing();
InsertResultStep insertReturningSubscription = insertInto(SUBSCRIPTION)
.set(SUBSCRIPTION.CHANNEL_ID, subscriptionDTO.getChannel())
.set(SUBSCRIPTION.SENDER_ID, subscriptionDTO.getSenderId())
.set(SUBSCRIPTION.CATEGORY_ID, subscriptionDTO.getCategory())
.set(SUBSCRIPTION.TOKEN, subscriptionDTO.getToken())
.onConflictDoNothing()
.returningResult(SUBSCRIPTION.ID);
Unfortunately, to insert values into the association table, I tried many ways but nothing works for me, finally, I tried to insert values in SUBSCRIPTION_USER_IDusing with select but It doesn't work:
InsertValuesStep insertValuesSubscriptionUserIds = insertInto(
SUBSCRIPTION_USER_ID,
SUBSCRIPTION_USER_ID.SUBSCRIPTION_ID,
SUBSCRIPTION_USER_ID.USER_ID_ID)
.select(select(SUBSCRIPTION.ID, USER_ID.ID)
.from(SUBSCRIPTION)
.innerJoin(USER_ID)
.on(concat(USER_ID.USER_ID_TYPE,
val(CATEGORY_USER_ID_DELIMITER),
USER_ID.USER_ID_VALUE).in(subscriptionDTO.getUserId())
.and(SUBSCRIPTION.SENDER_ID.equal(subscriptionDTO.getSenderId()))
.and(SUBSCRIPTION.CHANNEL_ID.equal(subscriptionDTO.getChannel()))
.and(SUBSCRIPTION.CATEGORY.equal(subscriptionDTO.getCategory()))
.and(SUBSCRIPTION.TOKEN.equal(subscriptionDTO.getToken()))));
Am I missing something above? Is there a better way using JOOQ to insert many-to-many relationship values or to use queries results as parameters for other queries?
I'm assuming you posted your entire code. In case of which:
You don't call execute on your USER_ID insertion
Simply add
insertUserIds.onConflictDoNothing().execute();
Or alternatively, fetch the generated IDs using a call to returning().fetch()
Inner join
This might just be a stylistic question, but what you seem to be doing is a cross join. Your INNER JOIN filters aren't really join predicates. I'd put them in the WHERE clause. Clarity may help avoid further problems in such a query.
Specifically, that first "join predicate" is very confusing, containing a CONCAT call, which isn't something one would see in an INNER JOIN every day, and only touches one table, not both:
.on(concat(USER_ID.USER_ID_TYPE,
val(CATEGORY_USER_ID_DELIMITER),
USER_ID.USER_ID_VALUE).in(subscriptionDTO.getUserId())
Wrong predicate
That last predicate seems wrong. You're inserting:
.set(SUBSCRIPTION.TOKEN, subscriptionDTO.getToken())
But you're querying
.and(SUBSCRIPTION.TOKEN.equal(subscriptionDTO.getContactId()))));
That should probably be subscriptionDTO.getToken() again
As mentioned above, I have inserted values for SUBSCRIPTION and USER_ID tables. And get for the association table I need to get the IDs of the already inserted values from the above two tables, so to solve the issue I've used this query to insert in SUBSCRIPTION_USER_ID:
InsertReturningStep insertReturningSubscriptionUserId = insertInto(
SUBSCRIPTION_USER_ID,
SUBSCRIPTION_USER_ID.SUBSCRIPTION_ID,
SUBSCRIPTION_USER_ID.USER_ID_ID)
.select(select(SUBSCRIPTION.ID, USER_ID.ID).from(SUBSCRIPTION
.where(concat(USER_ID.USER_ID_TYPE, val(CATEGORY_USER_ID_DELIMITER), USER_ID.USER_ID_VALUE).in(subscriptionDTO.getUserId()))
.and(SUBSCRIPTION.SENDER_ID.equal(subscriptionDTO.getSenderId()))
.and(SUBSCRIPTION.CHANNEL_ID.equal(subscriptionDTO.getChannel()))
.and(SUBSCRIPTION.CATEGORY.equal(subscriptionDTO.getCategory()))
.and(SUBSCRIPTION.TOKEN.equal(subscriptionDTO.getToken()))).onConflictDoNothing();
Finally, I have executed all the queries using batch:
using(configuration).batch(insertReturningSubscription,
insertReturningUserIds,
insertReturningSubscriptionUserId).execute()
For example, I have a database type alias defined as follows:
create type aml_acct from varchar(50) not null
Then in the SQL for creating a table, I would have a column definition like this:
create table ACCOUNTS (
.
acct aml_acct,
.
)
In 3.7.3 the Jooq generated code was this:
public final TableField<AmlAccountsRecord, String> ACCT =
createField("acct", org.jooq.impl.SQLDataType.VARCHAR.length(50).nullable(false), this, "");
In 3.12.3 the Jooq generated code is this:
/**
* #deprecated Unknown data type. Please define an explicit {#link org.jooq.Binding} to specify how this type should be handled. Deprecation can be turned off using {#literal <deprecationOnUnknownTypes/>} in your code generator configuration.
*/
#java.lang.Deprecated
public final TableField<AmlAccountsRecord, Object> ACCT = createField(DSL.name("acct"), org.jooq.impl.SQLDataType.OTHER.nullable(false), this, "");
But I can't figure out how to make a Binding class to make this properly handle the aml_acct database type and generate the code as before. Or is there a way to handle this with a ForcedType?
Any ideas or help would be appreciated...
A bug in jOOQ
This is a bug in the jOOQ 3.12 code generator (probably also present in previous releases). Recent releases of the jOOQ code generator have added support for table valued functions and table valued parameters in SQL Server. For that, new SYS and INFORMATION_SCHEMA queries have been written to fetch meta data for SQL Server's code generation. In this case, SYS.ALL_COLUMNS is joined to SYS.TYPES on the USER_TYPE_ID column rather than the SYSTEM_TYPE_ID column.
This will be fixed in 3.13.0 and 3.12.4: https://github.com/jOOQ/jOOQ/issues/9551.
Workaround
The workaround is to use a <forcedType> configuration to force the type of your columns to the wanted data type:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-database/codegen-database-forced-types/
For example:
<forcedType>
<name>VARCHAR</name>
<includeTypes>aml_acct</includeTypes>
</forcedType>
If you have many such types, you can also use <sql> in the above configuration to match all the columns that should have this forced type applied. This could look as follows:
<forcedType>
<name>VARCHAR</name>
<sql>
select string_agg(o.name + '\.' + c.name, '|')
from sys.all_objects o
join sys.all_columns c on o.object_id = c.object_id
join sys.types u on c.user_type_id = u.user_type_id
join sys.types t on u.system_type_id = t.system_type_id
where u.is_user_defined = 1
and t.name = 'varchar'
</sql>
</forcedType>
See the above documentation link for details.
EDITTED
Curious... I have a custom Spring JPA query which I'm not sure how to write.
I'm extending PagingAndSortingRepository
The #Query: select * from Table1 tb1 JOIN Table2 tb2 on tb1.id = tb2.tb1_id
where tb2.personId = :personId and tb1.mainId=:mainId and tb2.status in (:statusList)
I'm not sure how to create the method name for this as it keeps giving me an error saying it can't find status in the Table1.
I figured something like:
public Page findByMainIdAndStatusInAndPersonId(#Param("mainId") Integer mainId, ..........); would work but it's telling me it can't find status. Which is understandable since status is in the Table2 object which I'm trying to join on.
**Table1**
id
column1
column2
mainId
List<Table2> table2List
**Table2**
id
table1_id
status
person_id
table 1 and 2 are linked via table2's table_id column. however in the Table1 JPA repository, I need to fetch all of Table1 based on criteria in Table2.
I checked "property expressions" but I'm not catching how to write the jpa method name
Thanks guys :)
After a while of looking around and trying different things... ANSWER:
When you want to query on Table2, you need to add it to the method as:
findBymainIdAndTable2List_StatusInAndTable2List_personId
So essentially add the list name followed by underscore and the column name in that table. If anyone wants to add more, def feel free :D This is how I got it to work
I have an oracle database, and am trying to delete a record based on the client number, with the query returning the rowid of the deleted record. When executing the query, I am getting the following exception: java.lang.IllegalArgumentException: Field (rowid) is not contained in Row. If I attempt returning a different field (such as the client_number field itself) instead of rowid, the query works perfectly.
The query I am trying to execute looks like this:
ClientDetails clt = CLIENT_DETAILS.as("clt");
ClientDetailsRecord result = context.deleteFrom(clt)
.where(clt.CLIENT_NUMBER.equal(clientNumber))
.returning(rowid())
.fetchOne();
Is this a limitation of Jooq, or am I doing this the wrong way?
This is a known (and unfortunate) limitation of jOOQ 3.x, which can only return declared columns of your table CLIENT_DETAILS, no "system" columns like ROWID or any expressions for that matter. The relevant feature request to rectify this is: https://github.com/jOOQ/jOOQ/issues/5622
You could work around this limitation by creating your own CLIENT_DETAILS table which includes a "synthetic" ROWID column, e.g. by:
Extending the CustomTable type
Extending the code generator by adding the ROWID column to your CLIENT_DETAILS table (beware that this can have undesired side-effects, e.g. when calling UpdatableRecord.store())
In my Java Web application I use Postgresql and some data tables are filled automatically in server. In the database I have a STATUS table like below:
I want to select the data related to a vehicle between selected dates and where the vehicle stayed connected. Simply I want to select the data which are green in the above table which means I exactly want the data when firstly io1=true and the data when io1=false after the last io1=true. I have postgresql query statement which exactly gives me the desired data; however, I have to convert it to HQL because of my application logic.
working postgresql query:
WITH cte AS
( SELECT iostatusid, mtstrackid, io1,io2,io3, gpsdate,
(io1 <> LAG(io1) OVER (PARTITION BY mtstrackid
ORDER BY gpsdate)
) AS status_changed
FROM iostatus
WHERE mtstrackid = 'redcar' AND gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
)
SELECT iostatusId, mtstrackid, io1, io2, io3,gpsdate
FROM cte
WHERE status_changed
OR io1 AND status_changed IS NULL
ORDER BY gpsdate ;
How should I convert the above query to HQL or how could I retrieve the desired data with HQL?
The goal of hibernate is mapping database entities to java objects. This kind of complex queries are not entities themselves. This is against the spirit of hibernate.
If this query generates an entity in your application logic, I recommend putting the results into a table and applying Hibernate queries to that table.
If this query generates some kind of aggregation or summary, there are two possible ways:
One way is you compute this aggregation/summary in your application after retrieving entities from iostatus table with hibernate.
If this query has nothing to do with your application logic then you can use Native SQL interface of Hibernate and execute the query directly. (You can even use JPA if you are willing to manipulate two database connections.)
If you absolutely need to convert it to HQL, you need to eliminate the partition function. If the order of iostatusId is identical to the order of gpsdate, you can do it similar to
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.iostatusId = i2.iostatusId - 1
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59'
If gpsdate is no way related to iostatusId then you need something like
SELECT i2.*
FROM iostatus i1
INNER JOIN iostatus i2 ON i1.gpsdate < i2.gpsdate
AND i1.io1 <> i2.io1
AND i1.mstrackid = i2.mstrackid
WHERE i2.mtstrackid = 'redcar' AND
i2.gpsdate between '2014-02-28 00:00:00' and '2014-02-28 23:59:59' AND
NOT EXISTS (SELECT * FROM iostatus i3
WHERE i3.gpsdate > i1.gpsdate AND
i2.gpsdate > i3.gpsdate AND
i3.io1 = i1.io1 AND
i1.mstrackid = i3.mstrackid)
I guess both of the queries can be converted to HQL, but I'm not positively sure.
By the way I must warn you that, these methods might not perform better then finding the changes in your application, because they involve joining the table onto itself, which is an expensive operation; and the second query involves a nested query after the join, which is also quite expensive.