SQL Component internally resets CamelSqlUpdateCount to zero - apache-camel

Oddly enough, the SQL component reset the CamelSqlUpdateCount header value to zero when there is a 'SET FOREIGN_KEY_CHECKS' before a 'REPLACE SELECT'.
I ran in debug mode with a watcher on the header map. Yes the value is correct at first but it is set to zero in the process.
This situation does not occur with the JDBC component, only with the SQL component. Unfortunately, the JDBC component cannot be used in a "from".
working.sql
REPLACE INTO TABLE_B
SELECT * FROM TABLE_A;
SET FOREIGN_KEY_CHECKS = 1;
non_working.sql
SET FOREIGN_KEY_CHECKS = 0;
REPLACE INTO TABLE_B
SELECT * FROM TABLE_A;
SET FOREIGN_KEY_CHECKS = 1;
Working route
.enrich("sql:working.sql") // CamelSqlUpdateCount header is set e.g. to 500
.process(MyClass::operation) // CamelSqlUpdateCount is still 500
Non-working route
.enrich("sql:non_working.sql") // CamelSqlUpdateCount header is set e.g. to 500
.process(MyClass::operation) // CamelSqlUpdateCount has been lost, it is zero
Camel version 3.12.0

Related

Database not update when using apache camel sql component

I'm trying to use apache camel (sql component) to update the DB. Problem is that the DB just doesn't get updated. The sql:update is working fine when i hard code the query, but when i try to use ${body[0][id]} it doesn't update required field. Any feedback on what might be going wrong?
from("direct:updateSql")
.to("sql:select * from mytable limit 1")
.log("update mytable set status = '100' where id = '${body[0][id]}'")
.to("sql:update mytable set status = '100' where id = '${body[0][id]}'")
.end();
Note that status and id are integer fields but if i remove the ' from the .to() then i throws some errors.
According to the docs you must use :# as the placeholder syntax
http://camel.apache.org/sql-component
So try with
.to("sql:update mytable set status = 100 where id = :#${body[0][id]}")
Below code worked too.
from("direct:updateSql")
.to("sql:select * from mytable limit 1")
.setHeader("id", simple("${body[0][id]}"))
.to("sql:update mytable set status_id = 100 where id = :#id")
.end();

Multiple file upload blueimp “sequentialUploads = true” not working

I have to store multiple files in a Database. In order to do that I am using jQuery Multiple File Uplaod control written by blueimp. For the server language I use asp.net. It uses a handler – ashx which accepts every particular file and stores it in the DB.
In my DB I have a stored procedure which returns the document id of the currently uploaded file and then stores it into the DB.
This is a code fragment from it which shows that getting this id is a 3 step procedure:
SELECT TOP 1 #docIdOut = tblFile.docId --
,#creator=creator,#created=created FROM [tblFile] WHERE
friendlyname LIKE #friendlyname AND #categoryId = categoryId AND
#objectId = objectId AND #objectTypeId = objectTypeId ORDER BY
tblFile.version DESC
IF #docIdOut = 0 BEGIN --get the next docId SELECT TOP 1
#docIdOut = docId + 1 FROM [tblFile] ORDER BY docId DESC END
IF #docIdOut = 0 BEGIN --set to 1 SET #docIdOut = 1 END
If more than one calls to that stored procedure are executed then will be a problem due to inconsistency of the data, but If I add a transaction then the upload for some files will be cancelled.
https://dl.dropboxusercontent.com/u/13878369/2013-05-20_1731.png
Is there a way to call the stored procedure again with the same parameters when the execution is blocked by transaction?
Another option is to use the blueimp plugin synchronously with the option “sequentialUploads = true”
This option works for all browsers except firefox, and I read that it doesn’t work for Safari as well.
Any suggestions would be helpful. I tried and enabling the option for selecting only one file at a time, which is not the best, but will stop the DB problems (strored procedure) and will save the files correctly.
Thanks,
Best Regards,
Lyuubomir
set singleFileUploads: true, sequentialUploads: false in the main.js.
singleFileUploads: true means each file of a selection is uploaded using an individual request for XHR type uploads. Then you can get information of an individual file and call the store procedure with the information you have just got.

VFP ComboBox Content from SQL Server 2005 Express table

I'm still newbie in VFP, I'm looking for advices and suggestions on how to pull SQL Server table fields into VFP combobox(or other objects as well, if its better), just like auto-complete but from sql server database.
I have about 2 columns and 1000 rows inside the table, the combobox should only show the second columns field for user to choose but use the chosen first column field to be recorded to another table. I hope you get the idea.
Thanks in advance for your feedback.
I would handle it a little differently since you want BOTH columns.. one shown, and one for the actual data. Comboboxes can be directly bound to a table or cursor (cursor is nothing more than a temp table that automatically gets erased when closed).
In the INIT() of your combobox, I would run your query to your SQL database, but just grab those two columns...
* -- Try to connect and get a connection handle.
lnHandle = Sqlstringconnect( YourConnectionString )
if lnHandle < 1
*/ Return, get out, unable to get handle to SQL server
messagebox( "Unable to connect to SQL" )
return
end
lcSQLCmd = "Select ShowThisColumn, ButUseThisColumn from YourDatabase.dbo.YourTable"
lnSQLResult = SQLExec( lnHandle, lcSQLCmd, "C_ChoicesFromSQL" )
sqldisconnect( lnHandle )
if lnSQLResult < 1
messagebox( "Unable to retrieve data" )
return
endif
*/ Ok, we have the data, now the binding. VFP Can set the row source directly
*/ to the ALIAS ("C_ChoicesFromSQL") from the SQL query directly, no need to scan
*/ and add items. Just tell it both columns.
This.ColumnCount = 2 && You will bind BOTH columns to the combobox
This.BoundColumn = 2 && You want the data from the SECOND column bound for storage
This.BoundTo = .T. && Yes, bind to whatever the Control Source of the combobox
*/ Here's the trick. Set the column widths = whatever you have visible on the form
*/ for the first column, and the second column has a visible width of 0
This.ColumnWidths = alltrim( str( This.Width )) + ",0"
*/ Now, the row source...
This.RowSource = "C_ChoicesFromSQL.ShowThisColumn, ButUseThisColumn"
This.RowSourceType = 2 && bound to alias
*/ Fixed dropdown LIST, dont allow others to type in their own values
*/ but only choose from the data available in your source.
This.Style = 2
Well, here's how I'd do it programmatically assuming a combobox called 'cboSQL' on the form, and this code in the form Init() method (this only does one column in the combo but check the VFP help under AddItem() ):
Local lnHandle, lnResult As Integer
Local lcConnstring, lcCommand As String
Local llReturn As Boolean
* -- Do the default behaviour.
llReturn = DoDefault()
If llReturn
* -- Try to connect to a local SQL Express 2008 instance.
lcServername = ".\SQLEXPRESS"
lcDbname = "umbraco"
lcConnstring = [server=]+ lcServername+ [;driver={SQL Server};database=]+ lcDbname
lcConnstring = lcConnstring + [;DSN='';Trusted_Connection=Yes]
* -- Try to connect and get a connection handle.
lnHandle = Sqlstringconnect(lcConnstring)
* -- Got a connection ?
If lnHandle > 0
* -- Run a query, put the results in a VFP cursor called 'results'.
lcCommand = "select top 1000 logComment from [umbraco].[dbo].[umbracoLog]"
lnResult = SQLExec(lnHandle, lcCommand, "results")
If lnResult = -1
Messagebox("Error running SELECT.", 48, "Error!")
Else
* -- Got some rows, populate combobox.
Select results
Scan
* -- Transform() just stringifies the column.
Thisform.cboSql.AddItem(Transform(results.logComment))
Endscan
Endif
* -- Tidy up.
Use In Select("results")
SQLDisconnect(lnHandle)
Else
Messagebox("Couldn't connect to server.", 48, "Error!")
llReturn =.F.
Endif
Endif
Return llReturn

FoxPro form not updating tables

I've created a very simple form with two textboxes and an 'update locations' button, the inputted values of which should be updating to two linked tables in a database. I'm eventually trying to put this in a standalone application but for now I just want the form itself to work. I'm very new to FoxPro so I don't know if it's possible for just a form to update my tables, or if there's some other issue at work.
Here's the code for my 'update locations' button (OldLoc is the first textbox and NewLoc the second):
SET DATABASE TO LOCATIONS
CLOSE ALL DATABASES
OPEN DATABASE locations\locations EXCLUSIVE
IF this.parent.OldLoc.Value == "" then
MESSAGEBOX('Please fill in the old location.', 48, 'Missing Location Information')
this.parent.OldLoc.SetFocus
ELSE
INDEX ON table1.loc TAG loc
SET ORDER TO loc
SEEK this.parent.OldLoc.Value IN table1
IF FOUND()
IF this.parent.NewLoc.Value == "" then
MESSAGEBOX('Please fill in the new location.', 48,
'Missing Location Information') this.parent.NewLoc.SetFocus
UPDATE table1 SET loc = this.parent.NewLoc.Value ;
WHERE loc = this.parent.OldLoc.value
UPDATE table2 SET loc = this.parent.NewLoc.Value ;
WHERE loc = this.parent.OldLoc.value
ENDIF
ENDIF
ENDIF
Any input you have is appreciated!
you are doing redundant work in your click event... Closing the database, then re-opening, then opening exclusive. The tables you are trying to update, should already have an index on the key columns you ever plan to join or search based on. Once that is done, you don't need to explicitly create the index tag over and over... Once an index "TAG" is created, you don't need to do again..
So, that being said...
Open the form, and open the "INIT" event. In there, that is where you can explicitly open the tables for use while the form is in use...
IF NOT DBUSED( "Locations" )
*/ You only need exclusive if you will be modifying the database.
*/ The indexes should already exist before the form ever starts
Open Database Locations\Locations SHARED
ENDIF
Now, the "CLICK" event of your update button... Pre-validate that there are values, don't worry about trying to seek or update unless BOTH are filled in...
IF ALLTRIM( This.Parent.OldLoc.Value ) == "";
OR ALLTRIM( This.Parent.NewLoc.Value ) == ""
*/ Simple one message about BOTH being required
messagebox( "Please fill in both Old and New Locations.", 48, ;
"Missing Location Information" )
ENDIF
*/ Then, update... if no entries match, no records will be updated.
*/ VFP sometimes chokes with multiple "." values such as form values
*/ as SQL commands are expecting "alias.field" format...
lcOldValue = This.Parent.OldLoc.Value
lcNewValue = This.Parent.NewLoc.Value
Update Locations!Table1;
set Loc = lcNewLoc;
where Loc = lcOldLoc
*/ Were there any updates?
llAnyUpdates = _Tally > 0
*/ Now, the OTHER table too...
Update Locations!Table1;
set Loc = lcNewLoc;
where Loc = lcOldLoc
*/ Were there any updates on THIS cycle...
llAnyUpdates = llAnyUpdates OR _Tally > 0
if llAnyUpdates
messagebox( "Locations have been updated.", 0, "Update complete" )
else
messagebox( "No such location found to be updated.", 0, "No Records Updated" )
endif
You might also want to take a look at the data environment of the form.
You can select which tables the form is to open when the form loads, this saves explicitly opening them either in the forms load event or in the forms init event
My humble offering for the Click Event of the 'Update Locations' button is :-
LOCAL lcOldLoc,lcNewLoc
WITH THISFORM
lcOldLoc=.oldloc.VALUE
lcNewLoc=newloc.VALUE
DO CASE
CASE EMPTY(lcOldLoc)
MESSAGEBOX("PLEASE ENTER OLD LOCATION",16,"ERROR")
CASE EMPTY(lcNewLoc)
MESSAGEBOX("NEW LOCATION CAN'T BE EMPTY",16,"ERROR")
OTHERWISE
UPDATE table1 SET loc=lcNewLoc WHERE loc=lcOldLoc
DO CASE
CASE _TALLY=0
MESSAGEBOX("OLD LOCATION NOT FOUND",16,"ERROR")
OTHERWISE
UPDATE tabel2 SET loc=lcNewLoc WHERE loc=lcOldLoc
ENDCASE
ENDCASE
ENDWITH
This should be the only code required. Clearly there are many improvements that can be made to the above , but in essence that will do the job.
If you would like to see the above form built from scratch, pop an email to support#foxsoft.co.uk and I will give you a live demonstration.

SDAC -RecordCount and FetchAll

I am using SDAC components to query a SQL Server 2008 database. It has a recordcountproperty as all datasets do and it also has the FetchAll property (which I think it is called packedrecords on clientdatasets). Said that, I got a few questions:
1 - If I set FetchAll = True the recordcount property returns ok. But in this case, when I have a large database and my query returns a lot of lines, sometimes the memory grows a lot (because it is fetching all data to get the recordcount of course).
2 - If I set FetchAll = False, the recordcount returns -1 and the memory does not grow. But I really need the recordcount. And I also wanna create a generic function for this, so I dont have to change all my existent queries.
What can I do to have the recordcount working and the memory usage of the application low in this case?
Please, do not post that I dont need recordcount (or that I should use EOF and BOF) because I really do and this is not the question.
I thought about using a query to determine the recordcount, but it has some problems since my query is going to be executed twice (1 for recordcount, 1 for data)
EDIT
#Johan pointed out a good solution, and it seems to work. Can anybody confirm this? I am using 1 TMSCconnection for every TMSQuery (because i am using threads), so I dont think this will be a problem, will it?
MSQuery1.FetchAll := False;
MSQuery1.FetchRows := 10;
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
ShowMessage(IntToStr(MSQuery1.RecordCount)); //returns 10
MSQuery1.Close;
MSQuery2.SQL.Text := 'SELECT ##rowcount AS num_of_rows';
MSQuery2.Open;
ShowMessage(MSQuery2.FieldByName('num_of_rows').AsString); //returns 289
EDIT 2*
MSQuery1 must be closed, or MSQuery2 will not return the num_of_rows. Why is that?
MSQuery1.FetchAll := False;
MSQuery1.FetchRows := 10;
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
ShowMessage(IntToStr(MSQuery1.RecordCount)); //returns 10
//MSQuery1.Close; <<commented
MSQuery2.SQL.Text := 'SELECT ##rowcount AS num_of_rows';
MSQuery2.Open;
ShowMessage(MSQuery2.FieldByName('num_of_rows').AsString); //returns 0
Run your query as normal, than close the query
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
MSQuery1.Close;
You need the close otherwise SQL-server has not closed the cursor yet, and will not register the query as 'completed'.
and run the following query right afterwards:
SELECT ##rowcount AS num_of_rows
This will select the total number of rows your last select read.
It will also select the number of rows your update/delete/insert statement affected.
See: http://technet.microsoft.com/en-us/library/ms187316.aspx
Note that this variable is per connection, so queries in other connections do not affect you.
I use ODAC and I believe SDAC inherits from the same base classes and works the same way as ODAC. In ODAC, there is an option called QueryRecCount under Options in your query component. Look for TCustomDADataSet.Options.QueryRecCount in your help file.
Setting QueryRecCount = True and FetchAll = False will reduce your memory usage and give you the record count. But SDAC will run a second query in the background to get the record count so it does add a little bit of extra time to your query.
Take a look at the Devart forum entry at http://www.devart.com/forums/viewtopic.php?t=8143.

Resources