Using cfc with threads in a coldfusion page - multithreading

I have a component "bulletin.cfc" which contains lots of functions.
My main page has two threads running on it using the cfthread tag.
Coming from .net I thought I would create two instrances of the component, and use one in each thread. This way they wouldnt mess with each other and I wouldnt need to worry about putting locks in the functions.
<cfset bullObj = new bulletin()>
<cfset bullObj2 = new bulletin()>
Is this correct?
EDIT:
Thanks for the answers so far, I still can't understand a problem that is happening with this though. I have the following code inside two seperate cfthread elements:
<cfset listCount = 1>
<cftry>
<cfquery name="ins" datasource="#datasourceVar#" >
INSERT INTO element_user_shown
(elementid, userid, date_shown)
(
<cfloop list="#elementIDList#" index="lcv">
SELECT #lcv#, #tmpuserid#, GETDATE()
<cfif listCount LT listlen(elementIDList)>
UNION ALL
</cfif>
<cfset listCount = listCount + 1>
</cfloop>
)
</cfquery>
This runs about 70,000 times a night but gets about 3-4 errors each time. Checking the sql that errors it looks like
INSERT INTO element_user_shown
(elementid, userid, date_shown)
(
SELECT 621, 267509, GETDATE()
UNION ALL
SELECT 586, 267509, GETDATE()
UNION ALL
SELECT 594, 267509, GETDATE()
UNION ALL
SELECT 613, 267509, GETDATE()
SELECT 622, 267509, GETDATE()
SELECT 599, 267509, GETDATE()
SELECT 602, 267509, GETDATE()
)

You are correct that your instances are passed by reference so the potential might be there to have a concurrency problem. But if your function arguments are properly paramed and you are not changing properties as a part of the instance (in other words this is an interface and not a bean) you can safely reuse the same instance. Each function call is it's own scope and variables within and returned are for the life of the function call.

Related

Writing a subquery to display records in a grid

I have two DAC's POReceipt, and and POReceiptLine. POReceiptLine containts a field called MfrPartNbr.
I want the user to be able to lookup all the POReceipts where the POReceiptLine.MfrPartNbr is equal to an entered value.
The SQL would be
SELECT *
FROM dbo.POReceipt
WHERE POReceipt.ReceiptNbr IN
(
SELECT ReceiptNbr
FROM dbo.POReceiptLine
WHERE MfrPartNbr = 'MY_ENTERED_PART_NBR'
)
Any idea how to write the BQL Statement for this?
As stated, an inner join won't work in this case because you will receive the same POReceipt multiple times (once for each POReceiptLine). The following BQL query shows how you can get the desired results using a sub query. If mfrPartNbr is an extension field, then replace POReceiptLine.mfrPartNbr with the correct extension name (e.g. POReceiptLineExtension.mfrPartNbr).
PXSelect<POReceipt, Where<Exists<
Select<POReceiptLine,
Where<POReceiptLine.receiptNbr, Equal<POReceipt.receiptNbr>,
And<POReceiptLine.mfrPartNbr, Equal<Required<POReceiptLine.mfrPartNbr>>>>>>>>.Select(this, "MY_ENTERED_PART_NBR");

Nested subquery in FOR ALL ENTRIES

Consultant sent me this code example, here is something he expects to get
SELECT m1~vbeln_im m1~vbelp_im m1~mblnr smbln
INTO CORRESPONDING FIELDS OF TABLE lt_mseg
FROM mseg AS m1
INNER JOIN mseg AS m2 ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
FOR ALL ENTRIES IN lt_vbfa
WHERE
AND m2~bwart = '102'
AND 0 = ( select SUM( ( CASE
when SHKZG = 'S' THEN 1
when SHKZG = 'H' THEN -1
else 0
END ) *MENGE ) MENGE
into lt_mseg-summ
from mseg
where
VBELN_IM = m1~vbeln_im
and VBELP_IM = m1~vbelp_im
).
The problem is I don't see how that should work in current syntax. I think about deriving internal select and using it as condition to main one, but is there a proper way to write this nested construction?
As i get it, if nested statement = 0, then main query executes. The problem here is the case inside nested statement. Is it even possible in ABAP? And in my opinion this check could be used outside from main SQL query.
Any suggestions are welcome.
the logic that you were given is part of Native/Open SQL and has some shortcomings that you need to be aware of.
the statement you are showing has to be placed between EXEC SQL and ENDEXEC.
the logic is platform dependent.
there is no syntax checking performed between the EXEC and ENDEXEC
the execution of this bypasses the database buffering process, so its slower
To me, I would investigate a better way to capture the data that performs better outside of open/native sql.
If you want to move forward with this type of logic, below are a couple of links which should be helpful. There is an example select using a nested select with a case statement.
Test Program
Example Logic
This is probably what you need, it works at least since ABAP 750.
SELECT vbeln UP TO 100 ROWS
FROM vbfa
INTO TABLE #DATA(lt_vbfa).
DATA(rt_vbeln) = VALUE range_vbeln_va_tab( FOR GROUPS val OF <line> IN lt_vbfa GROUP BY ( low = <line>-vbeln ) WITHOUT MEMBERS ( sign = 'I' option = 'EQ' low = val-low ) ).
SELECT m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
INTO TABLE #DATA(lt_mseg)
FROM mseg AS m1
JOIN mseg AS m2
ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
WHERE m2~bwart = '102'
AND m1~vbeln_im IN ( SELECT vbelv FROM vbfa WHERE vbelv IN #rt_vbeln )
GROUP BY m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
HAVING SUM( CASE m1~shkzg WHEN 'H' THEN 1 WHEN 'S' THEN -1 ELSE 0 END * m1~menge ) = 0.
Yes, aggregating and FOR ALL ENTRIES is impossible in one SELECT, but you can trick the system with range and subquery. Also you don't need three joins for summarizing reversed docs, your SUM subquery is redundant here.
If you need to select documents not only by delivery number but also by position this will be more complicated for sure.

Is cfstoredproc.statusCode returned?

When ReturnCode=true, it should populate cfstoredproc.statusCode with the status code returned by the stored procedure.
But I can only see cached and executionTime.
Here is an example that I tested and works. Perhaps the issue is with the way the stored procedure is written or with the JDBC Driver you are using to connect with the database.
Another option you have at hand is to not use the tag to call the storedprocedure. You can also call stored procedures using the cfquery tag. This often gives more predictable results.
Here is the code for the test procedure:
CREATE PROCEDURE dbo.PTest
#Somename nvarchar(50) = NULL, -- NULL default value
#SomeResponse nvarchar(50) = NULL OUTPUT
AS
IF #Somename IS NULL
BEGIN
RETURN(1)
END
ELSE
SET #SomeResponse = 'Hello World:' + #Somename;
BEGIN
RETURN(2)
END
Then on the coldfusion side using the cfstoredproc tag:
<cfstoredproc datasource="yourdatasource" procedure="dbo.PTest" returncode="yes">
<cfprocparam type="in" variable="Somename" cfsqltype="cf_sql_varchar" value="John Smith">
<cfprocparam type="out" variable="SomeResponse" cfsqltype="cf_sql_varchar">
</cfstoredproc>
<cfoutput>
#SomeResponse#
<cfdump var="#cfstoredproc#">
</cfoutput>
Using cfquery this would look like this:
<cfquery name="qFoo" datasource="yourdatasource">
SET NOCOUNT ON
DECLARE #SomeResponse varchar(50), #return_code int;
EXECUTE #return_code = dbo.PTest 'John Smith', #SomeResponse = #SomeResponse OUTPUT
SET NOCOUNT OFF
SELECT #return_code as returnCode,
#SomeResponse as someResponse
</cfquery>
<cfoutput>
#qFoo.returnCode# | #qFoo.someResponse#
<cfdump var="#qFoo#">
</cfoutput>

CFspreadsheet looping

I have a question regarding cfspreadsheet....So I'm using cfspreadshseet to create excel spreadsheets for reporting purposes. My page allows a user to select whatever columns from the database to include in the report. So here is an example:
The spreadsheet could look like this:
First Name---Last Name---Organization---Address---City---State---Zip---Concern
Joe Smith Sample 12 main denver co 80513 concerns go here
My question is this, if Joe has more than 1 concern I get multiple rows with joe's info...is there a way I can loop the concerns and only have 1 row for joe?
Thanks,
Steve
Using the "group" feature of cfoutput is perfectly fine for this task. Just to throw out another possibility, you could also generate the list within your database query.
For example, MySQL has the GROUP_CONCAT function. I do not know the structure of your tables, but say you have two tables User and UserConcern, you could use GROUP_CONCAT like so to concatenate the "Concern" values into a single string:
SQLFiddle
SELECT
u.UserID
, u.FirstName
, ... other columns
, GROUP_CONCAT( uc.Concern ) AS ConcernList
FROM UserTable u INNER JOIN UserConcern uc
ON uc.UserID = u.UserID
GROUP BY
u.UserID
, u.FirstName
, ... other columns
For SQL Server, a standard trick is to use XML Path:
SQLFiddle
SELECT
u.UserID
, u.FirstName
, ... other columns
, STUFF( ( SELECT ',' + uc.Concern
FROM UserConcern uc
WHERE uc.UserID = u.UserID
ORDER BY uc.Concern
FOR XML PATH('')
)
, 1, 1, ''
) AS ConcernList
FROM UserTable u
GROUP BY
u.UserID
, u.FirstName
, ... other columns
Then simply generate your spreadsheet as usual.
You need a unique row ID to do this safest, the outer group working with lastname, or something, could cause conflict. UserID is a placeholder variable. Make sure to replace it with an accurate ID name. Of course, a few of these variable names are just guessed.
<cfoutput query ="thequery" group="UserID">
<cfset cList="">
<cfoutput group="concern">
<cfset cList=ListAppend(cList,Concern)>
</cfoutput>
<cfset temp = spreadsheetAddRow(my_spreadsheet,"'#fn#','#ln#',...,'#cList#'">
</cfoutput>
Assuming you want separate lines for each comment, something like this would work:
<cfset current_id = "">
<cfloop query = "my_query">
<cfset next_id = user_id>
<!--- or whatever else forms the primary key --->
<cfif next_id neq current_id>
<cfset current_id = next_id>
<cfset SpreadsheetAddRow(my_spreadsheet, "#first_name#,#last_name#,etc, #comment#">
<cfelse>
<cfset SpreadsheetAddRow(my_spreadsheet, ",,#comment#">
</cfif>
</cfloop>
This is based on the information provided. If you have a unique ID the group attribute would work better.

The sum of a struct within an array

I am in the process of putting together a shopping cart, I am holding the cart data in an array, inside the array is a struct which holds the individual product information. I need get the sum of the totalprice column within the struct, please see my dump below, I have tried
<cfset carTotal = ArraySum(session.mycart[ "totalPrice" ])> but this creates an error
the value totalprice cannot be converted to a number
Is this because I am using arraysum in a struct?
Any help would be appreciated, thank you.
If mycart was a Query object, it'd be a simple ArraySum(mycart.totalPrice)
Since it's an array of struct, you've got to loop it yourself, which is still rather easy:
<cfset sum = 0>
<cfloop array="#session.mycart#" index="item">
<cfset sum += item.totalPrice>
</cfloop>
<cfdump var="#sum#">
Don't forget to use PrecisionEvaluate() when you need full precision.
As it's an array you'll have to loop yourself, keeping track of the sum.
<cfset cartTotal = 0 />
<cfloop array=#session.mycart# index="i">
<cfset cartTotal += i.totalPrice />
</cfloop>
<cfdump var="#cartTotal#" />
In Coldfusion 10 or Railo 4, you could use the reduce function of Underscore.cfc:
_ = new Underscore();
cartTotal = _.reduce(session.mycart, function(total, item){
return total + item.TotalPrice;
}, 0);
reduce is a common higher-order function that "reduces" a set of values to a single value. In this case, we're "reducing" the price of all items in a collection to a single sum.
Using a well-understood function likereduce, rather than a custom solution, yields more readable code.
Note: I wrote Underscore.cfc
Additional to the answer by Russ, in Coldfusion 11 or Lucee you can simply do arrayReduce() as well. No explicit need for underscore anymore.
cartTotal = arrayReduce(session.mycart, function(total, item){
return total + item.TotalPrice;
}, 0);

Resources