How do I run transactions in CakePHP3 for multiple model operation? - cakephp

I want to use transactions on multiple insert/update query for multiple model/table.
In cakephp3 documentation have an example of transaction using connection manager execute() method but I have to use transaction with save() method of cakephp3. I tried it using save() method but it didn't work. There was a SQL error, it execute all query and saving data except the query where the sql error have, rollback didn't work!
my code was like-
$connection = ConnectionManager::get('default');
$connection->transactional(function ($connection) use($inputs, $status) {
$serviceTbl = $this->Service;
$service = $serviceTbl->newEntity();
$service->name = $inputs['name'];
$service = $serviceTbl->save($service);
if($service)
{
$this->loadModel('SrvApiClientInfo');
$SrvApiClientInfo = $this->SrvApiClientInfo;
$SrvApiClient = $SrvApiClientInfo->newEntity();
$SrvApiClient->client_name = $inputs['basic_info']['client_name'];
$SrvApiClient->organization_name = $inputs['basic_info']['vendor_name'];
$SrvApiClient->address = $inputs['basic_info']['address'];
$SrvApiClient->email = $inputs['basic_info']['email'];
$SrvApiClient->mobile = $inputs['basic_info']['mobile'];
$SrvApiClient->phone = $inputs['basic_info']['phone'];
$SrvApiClient->fax = $inputs['basic_info']['fax'];
$SrvApiClient->emergency_contact_name = $inputs['basic_info']['emergency_contact_name'];
$SrvApiClient->emergency_contact_mobile = $inputs['basic_info']['emergency_contact_mobile'];
$SrvApiClient->emergency_contact_designation = $inputs['basic_info']['emergency_contact_designation'];
$SrvApiClient = $SrvApiClientInfo->save($SrvApiClient);
if($SrvApiClient){
$this->loadModel('ServiceApiRegistration');
$tblServiceApiRegistration = $this->ServiceApiRegistration;
foreach($inputs['method'] as $method)
{
$ServiceApiRegistration = $tblServiceApiRegistration->newEntity();
$ServiceApiRegistration->service_id = $service['service_id'];
$ServiceApiRegistration->service_api_id = $method;
$ServiceApiRegistration->client_id = $SrvApiClient['client_id'];
$ServiceApiRegistration->status_id = $status['Inactive'];
$tblServiceApiRegistration->save($ServiceApiRegistration);
}
}
}
});

Check the docs that you've linked a little more closely, especially point 3 and 4 of the "The transactional method will do the following" list
The transactional method will do the following:
[...]
If the closure raises an exception, a rollback will be issued. The original exception will be re-thrown.
If the closure returns false, a rollback will be issued.
Your code neither returns false, nor does it throw an exception when something goes wrong, hence no rollback will be issued.
If there really was an SQL "error", ie an exception triggered in the core, then it should have been catched by the transaction wrapper, and a rollback should have been issued. So I'd suspect that either, there was no exception, or that rollbacks aren't supported or the driver thinks they aren't.

Related

name 'EmptyArrowIterator" is not defined error when calling stored procedure with Snowflake's Python Connector

The problem: When I call a stored procedure in Snowflake using the Python connector an exception is raised that reads:"name 'EmptyArrowIterator" is not defined".
My goal: Use Snowflake's Python connector to call a stored procedure and save the return value to a variable.
What I tried: I followed the instructions in this Stack Exchange post but was not successful in resolving the error.
My code:
import pandas as pd
#my code is long...a dataframe called credentials is defined elsewhere in my code and is not shown below
def logging_sproc(credentials):
try:
#import snowflake module and binding method
import snowflake.connector
#necesary for passing python variables to statements; note this method is server side
#so there is no concern about SQL injection attacks
snowflake.connector.paramstyle='qmark'
#etl logging variables
etl_name = credentials.iloc[0]['etl_name']
etl_guid = credentials.iloc[0]['etl_guid']
etl_taskname = credentials.iloc[0]['etl_task_name']
etl_record_count = credentials.iloc[0]['record_count']
#snowflake variables
snowflake_warehouse = credentials.iloc[0]['snowflake_warehouse']
snowflake_account = credentials.iloc[0]['snowflake_account']
snowflake_role = credentials.iloc[0]['snowflake_role']
snowflake_username = credentials.iloc[0]['Username']
snowflake_password = credentials.iloc[0]['Password']
snowflake_connection = ''
cs = ''#snowflake connection cursor
call_logging_sproc = ''
#call the logging stored procedure
snowflake_connection = snowflake.connector.connect(
user = snowflake_username,
password = snowflake_password,
account = snowflake_account,
warehouse = snowflake_warehouse,
role = snowflake_role)
cs = snowflake_connection.cursor()
call_logging_sproc = cs.execute("CALL EDW_DEV.LOGGING.LOGGING_SP(?,?,?,?)",(etl_name,etl_guid,etl_taskname,etl_record_count))
except Exception as error:
return error
finally:
snowflake_connection.close()
return call_logging_sproc
logging_sp = logging_sproc(credentials)
Thank you for your help!
I added call_logging_sproc.fetchmany(1) and the error went away.

JDBC SQL Server raised error handling in multi statement stored procedures

I have a multi-statement stored procedure that first performs a select and then raises an error if certain conditions are met.
The raise error in the stored procedure doesn't cause a JDBC SQLException like I expect however.
If I remove the SELECT, then it works fine. The same type of behavior occurs with the print statement.
I have multiple other ways to handle this, but for future reference I was wondering if there was a way to check if raised errors do exist.
The way the SQL server protocol works, you first need to process the result set produced by the select, and then move to the next result to get the exception.
To process all results (result sets, update counts and exceptions), you need do something like:
CallableStatement csmt = ...;
boolean isResultSet = cstmt.execute();
do {
if (isResultSet) {
// process result set
try (ResultSet rs = csmst.getResultSet()) {
while(rs.next()) {
// ...
}
}
} else {
int updateCount = rs.getUpdateCount();
if (updateCount == -1) {
// -1 when isResultSet == false means: No more results
break;
} else {
// Do something with update count
}
}
isResultSet = cstmt.getMoreResults();
} while(true);
When the execution of the stored procedure reaches the exception, this will also report the exception to your java application (iirc from getMoreResults()).

How to handle/catch error from database trigger, in YII2

this is my trigger
CREATE OR REPLACE TRIGGER trg_cek_pengurus
BEFORE INSERT ON tbl_pengurus
FOR EACH ROW
DECLARE
v_cek NUMBER(2);
BEGIN
IF :NEW.jabatan = 1 OR :NEW.jabatan = 2 THEN
select count(id)
into v_cek
from tbl_pengurus
where idkoperasi = :NEW.idkoperasi and jabatan = :NEW.jabatan;
IF v_cek > 0 THEN
RAISE_APPLICATION_ERROR (-20000, 'kepengurusan sudah ada');
END IF;
END IF;
END;
/
nah.., if the trigger have a return value is good,:v but it doesn't :v
so I set RAISE_APPLICATION_ERROR
And this is my controller
public function actionTambahPengurus()
{
$model = new Pengurus();
if ($model->load(Yii::$app->request->post())){
$model->IDKOPERASI = Yii::$app->user->identity->ID;
if($model->save())
return $this->redirect('kepengurusan');
}
return $this->render('tambah-pengurus',[
'model' => $model,
]);
}
And then I get error
SQLSTATE[HY000]: General error: 20000 OCIStmtExecute: ORA-20000: kepengurusan sudah ada
ORA-06512: at "DB_KOPERASI.TRG_CEK_PENGURUS", line 13
ORA-04088: error during execution of trigger 'DB_KOPERASI.TRG_CEK_PENGURUS'
(ext\pdo_oci\oci_statement.c:148)
The SQL being executed was: INSERT INTO "TBL_PENGURUS" ("NIK", "NAMA", "JABATAN", "EMAIL", "TGL_LAHIR", "ALAMAT", "TELEPON", "IDKOPERASI") VALUES ('2110131041', 'Rahmat Heru Kurniawan', 1, 'rahmatheruka2#gmail.com2', '3 July, 2015', 'sidoarjo', '0987654321', 8) RETURNING "ID" INTO :qp8
this is good, because this tell me that trigger is working
but of course this is bad for my website.
So i want to handle this, I've tries various ways from google, but nothing works.
Please.. is there anyone who can help me?
As far as I remember, any database related error will throw yii\db\Exception. You can use standard try / catch block to handle that:
try {
...
} catch (\yii\db\Exception $e) {
...
}

Does Entity Framework change the default return value of a SQL Stored Procedure from 0 to -1?

Trying to troubleshoot a return value problem and it appears as though EF4 doesn't handle the return value from a stored procedure as expected.
When I run just the stored Procedure from SQL server management studio the return value is zero. this is expected. However, when I run the code from my service the same stored procedure call returns -1 instead.
using (var ctx = new LAMSEntities())
{
ObjectParameter tempComputerId = new ObjectParameter("computerId", Guid.Empty);
ObjectParameter tempCustomerId = new ObjectParameter("customerId", string.Empty);
result = ctx.RegisterCustomerComputer_i(regCode, machineHashId, tempComputerId, tempCustomerId);
if (result != 0) //0 = success in SQL Stored Procedures
///TODO: may want to add logging at this point to determine what the cause of failure was.
return null;
CustomerRegistration cr = new CustomerRegistration();
cr.ComputerHash = machineHashId;
cr.ComputerId = Guid.Parse(tempComputerId.Value.ToString());
cr.CustomerId = tempCustomerId.Value.ToString();
cr.RegistrationCode = regCode;
cr.RegistrationDate = DateTime.Now;
return cr;
}
this is the code from my service call. The RegisterCustomerComputer_i is the stored procedure. Any suggestions or insights are appreciated.
TRY
ctx.RegisterCustomerComputer_i(regCode, machineHashId, tempComputerId, tempCustomerId).First();

Multiple result sets and return value handling while calling sql server stored procedure from Groovy

I have MS SQL Server stored procedure (SQL Server 2012) which returns:
return value describing procedure execution result in general (successfull or not) with return #RetCode statement (RetCode is int type)
one result set (several records with 5 fields each)
another result set (several records with 3 fields each)
I calling this procedure from my Groovy (and Java) code using Java's CallableStatement object and I cannot find right way to handle all three outputs.
My last attempt is
CallableStatement proc = connection.prepareCall("{ ? = call Procedure_Name($param_1, $param_2)}")
proc.registerOutParameter(1, Types.INTEGER)
boolean result = proc.execute()
int returnValue = proc.getInt(1)
println(returnValue)
while(result){
ResultSet rs = proc.getResultSet()
println("rs")
result = proc.getMoreResults()
}
And now I get exception:
Output parameters have not yet been processed. Call getMoreResults()
I tried several approaches for some hours but didn't find correct one. Some others produced another exceptions.
Could you please help me with the issue?
Thanks In Advance!
Update (for Tim):
I see rc while I launched code:
Connection connection = dbGetter().connection
CallableStatement proc = connection.prepareCall("{ ? = call Procedure_Name($param_1, $param_2)}")
boolean result = proc.execute()
while(result){
ResultSet rs = proc.getResultSet()
println(rs)
result = proc.getMoreResults()
}
I see rc as object: net.sourceforge.jtds.jdbc.JtdsResultSet#1937bc8

Resources