I want to implement the lightweight transaction for update statement with SpringBoot Cassandra.
Ex:
UPDATE cyclist_id SET id = 15a116fc-b833-4da6-ab9a-4a3775750239 where lastname = 'WELTEN' and firstname = 'Bram' IF age = 18;
Is there any API available with CassandraTemplate to achieve the above?
I tried using
UpdateOptions updateOptions = UpdateOptions.builder().consistencyLevel(ConsistencyLevel.QUORUM).build(); But not sure how to set the If condition with value (like age = 18)
I figured out the solution to implement lightweight transaction through SpringBoot Caasandra.
Create CqlSession using com.datastax.oss.driver.api.core.CqlSession builder.
Use PreparedStatement - for query. In query use IF condition. and set the parameter to the query using BoundStatement.
Related
If is use this code in a CQL shell , I get all the names of table(s) in that keyspace.
DESCRIBE TABLES;
I want to retrieve the same data using ResulSet . Below is my code in Java.
String query = "DESCRIBE TABLES;";
ResultSet rs = session.execute(query);
for(Row row : rs) {
System.out.println(row);
}
While session and cluster has been initialized earlier as:
Cluster cluster = Cluster.builder().addContactPoint("127.0.0.1").build();
Session session = cluster.connect("keyspace_name");
Or I like to know Java code to retrieve table names in a keyspace.
The schema for the system tables change between versions quite a bit. It is best to rely on drivers Metadata that will have version specific parsing built in. From the Java Driver use
Cluster cluster = Cluster.builder().addContactPoint("127.0.0.1").build();
Collection<TableMetadata> tables = cluster.getMetadata()
.getKeyspace("keyspace_name")
.getTables(); // TableMetadata has name in getName(), along with lots of other info
// to convert to list of the names
List<String> tableNames = tables.stream()
.map(tm -> tm.getName())
.collect(Collectors.toList());
Cluster cluster = Cluster.builder().addContactPoint("127.0.0.1").build();
Metadata metadata = cluster.getMetadata();
Iterator<TableMetadata> tm = metadata.getKeyspace("Your Keyspace").getTables().iterator();
while(tm.hasNext()){
TableMetadata t = tm.next();
System.out.println(t.getName());
}
The above code will give you table names in the passed keyspace irrespective of the cassandra version used.
Looking for a way to do a batch update using slick. Is there an equivalent updateAll to insertALL? Goole research has failed me thus far.
I have a list of case classes that have varying status. Each one having a different numeric value so I cannot run the typical update query. At the same time, I want to save the multiple update requests as there could be thousands of records I want to update at the same time.
Sorry to answer my own question, but what i ended up doing is just dropping down to JDBC and doing batchUpdate.
private def batchUpdateQuery = "update table set value = ? where id = ?"
/**
* Dropping to jdbc b/c slick doesnt support this batched update
*/
def batchUpate(batch:List[MyCaseClass])(implicit subject:Subject, session:Session) = {
val pstmt = session.conn.prepareStatement(batchUpdateQuery)
batch map { myCaseClass =>
pstmt.setString(1, myCaseClass.value)
pstmt.setString(2, myCaseClass.id)
pstmt.addBatch()
}
session.withTransaction {
pstmt.executeBatch()
}
}
It's not clear to me what you are trying to achieve, insert and update are two different operation, for insert makes sense to have a bulk function, for update it doesn't in my opinion, in fact in SQL you can just write something like this
UPDATE
SomeTable
SET SomeColumn = SomeValue
WHERE AnotherColumn = AnotherValue
Which translates to update SomeColumn with the value SomeValue for all the rows which have AnotherColumn equal to AnotherValue.
In Slick this is a simple filter combined with map and update
table
.filter(_.someCulomn === someValue)
.map(_.FieldToUpdate)
.update(NewValue)
If instead you want to update the whole row just drop the map and pass a Row object to the update function.
Edit:
If you want to update different case classes I'm lead to think that these case classes are rows defined in your schema and if that's the case you can pass them directly to the update function since it's so defined:
def update(value: T)(implicit session: Backend#Session): Int
For the second problem I can't suggest you a solution, looking at the JdbcInvokerComponent trait it looks like the update function invokes the execute method immediately
def update(value: T)(implicit session: Backend#Session): Int = session.withPreparedStatement(updateStatement) { st =>
st.clearParameters
val pp = new PositionedParameters(st)
converter.set(value, pp, true)
sres.setter(pp, param)
st.executeUpdate
}
Probably because you can actually run one update query at the time per table and not multiple update on multiple tables as stated also on this SO question, but you could of course update multiple rows on the same table.
I'm having difficulty combining BatchStatement and Lightweight Transactions using the Datastax java driver.
Consider the following:
String batch =
"BEGIN BATCH "
+ "Update mykeyspace.mytable set record_version = 102 where id = '" + id + "' if record_version = 101;
" <additional batched statements>
+ "APPLY BATCH";
Row row = session.execute(batch).one();
if (! row.getBool("[applied]")) {
throw new RuntimeException("Optimistic Lock Failure!");
}
This functions as expected and indicates whether my lightweight transaction succeeded and my batch was applied. All is good. However if I try the same thing using a BatchStatement, I run into a couple of problems:
-- My lightweight transaction "if" clause is ignored and the update is always applied
-- The "Row" result is null making it impossible to execute the final row.getBool("[applied]") check.
String update = "Update mykeyspace.mytable set record_version = ? where id = ? if record_version = ?";
PreparedStatement pStmt = getSession().prepare(update);
BatchStatement batch = new BatchStatement();
batch.add(new BoundStatement(pStmt).bind(newVersion, id, oldVersion));
Row row = session.execute(batch).one(); <------ Row is null!
if (! row.getBool("[applied]")) {
throw new RuntimeException("Optimistic Lock Failure!");
}
Am I doing this wrong? Or is this a limitation with the datastax BatchStatement?
I am encountering this same issue. I opened a ticket with DataStax support yesterday and received the following answer:
Currently Lightweight Transactions as PreparedStatements within a BATCH are not supported. This is why you are encountering this issue.
There is nothing on the immediate roadmap to include this feature in Cassandra.
That suggests eliminating the PreparedStatement will workaround the issue. I'm going to try that myself, but haven't yet.
[Update]
I've been trying to work around this issue. Based on the earlier feedback, I assumed the restriction was on using a PreparedStatement for the conditional update.
I tried changing my code to not use a PreparedStatement, but that still didn't work when using a BatchStatement that contained a RegularStatement instead of a PreparedStatement.
BatchStatement batchStatement = new BatchStatement();
batchStatement.add(conditionalRegularStatement);
session.executeQuery(batchStatement);
They only thing that seems to work is to do an executeQuery with a raw query string that includes the batch.
session.executeQuery("BEGIN BATCH " + conditionalRegularStatement.getQueryString() + " APPLY BATCH");
Update:
This issue is resolved in CASSANDRA-7337 which was fixed in C* 2.0.9.
The latest DataStax Enterprise is now on 2.0.11. If you are seeing this issue ==> Upgrade!
The 2nd code snippet doesn't look right to me (there's no constructor taking a string or you've missed the prepared statement).
Can you try instead the following:
String update = "Update mykeyspace.mytable set record_version = ? where id = ? if record_version = ?";
PreparedStatement pStmt = session.prepare(update);
BatchStatement batch = new BatchStatement();
batch.add(pStmt.bind(newVersion, id, recordVersion));
Row row = session.execute(batch).one();
if (! row.getBool("[applied]")) {
throw new RuntimeException("Optimistic Lock Failure!");
}
In update query, how to pass the value which i want to update in my table using the jsf and jpa
i create the namedquery in my entity class
#NamedQuery(name = "Showdetail.updateByShowDetailId", query = "update Showdetail s set s.avail = :avil where s.showdetailId =:showdetailID")
now i use it in my bean
em.createNamedQuery("Showdetail.updateByShowDetailId").setParameter("showdetailID",13).getResultList();
so i don't know how to pass the new value which i want to update in my table.
Did you try setting avil the same way as setting showdetailID?
em.createNamedQuery("Showdetail.updateByShowDetailId")
.setParameter("avil",100)
.setParameter("showdetailID",13)
.getResultList();
I am using version 2.1 of SubSonic. I am trying to build to build a relatively simple query where I get a list of Roles for a User using UserId as a parameter. (UserId is a string...)
SubSonic.SqlQuery sel = new SubSonic.Select().From(Data.Tables.Role).InnerJoin(Data.Tables.UserRole, Data.UserRole.Columns.RoleId, Data.Tables.Role, Data.Role.Columns.Id).InnerJoin(Data.Tables.User, Data.User.Columns.Id, Data.Tables.UserRole, Data.UserRole.Columns.UserId).Where("[dbo].[User].[Id]").IsEqualTo(userId);
this generates the query
SELECT [dbo].[Role].[Id], [dbo].[Role].[PID], [dbo].[Role].[Name]
FROM [dbo].[Role]
INNER JOIN [dbo].[UserRole] ON [dbo].[Role].[Id] = [dbo].[UserRole].[RoleId]
INNER JOIN [dbo].[User] ON [dbo].[UserRole].[UserId] = [dbo].[User].[Id]
WHERE [dbo].[User].[Id] = #[dbo].[User].[Id]0
which fails. If I replace the Where with .Where(Data.User.Columns.Id) this generates the query
SELECT [dbo].[Role].[Id], [dbo].[Role].[PID], [dbo].[Role].[Name]
FROM [dbo].[Role]
INNER JOIN [dbo].[UserRole] ON [dbo].[Role].[Id] = [dbo].[UserRole].[RoleId]
INNER JOIN [dbo].[User] ON [dbo].[UserRole].[UserId] = [dbo].[User].[Id]
WHERE [dbo].[Role].[Id] = #Id0
which uses the Role table in the Where clause instead of the User table.
Is this a bug, or am I doing something incorrectly? What would be the correct way to do this? Thanks.
This is fixed in version 2.2 - I would suggest upgrading.