Laravel relationship always returning empty array - sql-server

I have two models in a Laravel application Ft and Fi.
Ft contains the following code:
protected $table = 'ft';
protected $connection = 'XXX';
protected $keyType = 'string';
public function lines()
{
return $this->hasMany('App\Fi', 'ftstamp', 'ftstamp');
}
Fi contains the following code:
protected $table = 'fi';
In my controller I have the following code
Ft::select($fields)->with(['lines'])
All results have "lines": []
Relationship exists in database.
Database is SQL Server
ft.ftstamp and fi.ftstamp field is type char.
Driver used for connection is sqlsrv.

I'm sure this is a duplicate, but I can't find it. If you only select certain fields from the table, ensure that the primary key is one of them. The eager load is done as a separate query, not a join, so the key values have to be available.
For example, this code:
User::select(["id", "name"])->with("posts")->get();
Runs two database queries:
SELECT id, name FROM users;
SELECT * FROM posts WHERE user_id IN (?, ?, ?, ?...);
The second query is populated with the id values from the first. Without those values, the second query can't be run.
In a normal database, this would mean making sure that the array $fields contains "id" as an element, and I'd provide a code sample. Your database looks frightening however, so I won't try.

Related

Mage::getModel but from another database don't work

I've managed to create a module that can connect to a second database next that one from the shop. It started an setup script to create the new table and I inserted one test data.
So far so good.
Now I want to get that data inside an observer.
I can get the model and the collection. Bbut when I count that collection it returns null. When I try getSelectSql(true) on that model that query looks like:
SELECT `main_table`.* FROM `custom_table` AS `main_table`
Running that query in the database returns my added test data.
app/etc/local.xml
<config>
<global>
<resources>
<external_db>
<connection>
<host><![CDATA[127.0.0.1]]></host>
<username><![CDATA[db_user]]></username>
<password><![CDATA[db_pass]]></password>
<dbname><![CDATA[external_db]]></dbname>
<active>1</active>
</connection>
Observer
public function validateRequest(Varien_Event_Observer $observer) {
try {
$collection = Mage::getModel('custom_module/custom_table')->getCollection();
$items = $collection->getItems();
That returns me the following exception:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'shop.custom_table' doesn't exist, query was: SELECT main_table.* FROM custom_table AS main_table
It looks like magento isn't able to choose the correct connection.
But I can't find the way to change that in a magento way.
The only way it works is:
$resource = Mage::getSingleton('core/resource');
$conn = $resource->getConnection('externaldb_read');
$results = $conn->query('SELECT * FROM custom_table');
I would like to do it without creating raw queries.
use setConnection() method before you load your collection. The Argument should be a valid core/resource object with your database selected.

Inserting/Updating Rows to DB table where Rows result from VO [Backed by EO] based on Union Query

Jdev Version : 11.1.1.7
I have created a Department VO based Department EO with the following query :
SELECT DeptEO.DEPARTMENT_ID,
DeptEO.DEPARTMENT_NAME,
DeptEO.MANAGER_ID,
DeptEO.LOCATION_ID,
DeptEO.ACTIVE
FROM DEPARTMENTS DeptEO where DeptEO.DEPARTMENT_ID > 250
UNION
SELECT 280 , 'Advertising',200,1700,'Y' from Dual
For the simplicity , I have used a sample statement from dual table , in real scenario , the query after UNION clause will populate from a table.
After running the query ,I get the result that is desired on the UI .
Now my requirement is to insert this newly created row with DEPARTMENT_ID as 280 , into DB table DEPARTMENTS.
While committing , ADF throws error as " oracle.jbo.RowAlreadyDeletedException: JBO-29114 " which is correct as the this row is missing from DB table , so when it goes for taking a lock on the row for update , it doesn't find anything .
Is there any way that i can instruct ADF to consider this row for Insert rather than update .
We also tried to populate the data of this row into a new row instance created from RowSetIterator , and afterwards remove the culprit row by calling removeFromCollection() and then inserting the duplicated row , but still no luck .
Other approaches that we are thinking of are :
1- Create another VO/EO and insert values in table through them .
2- Create a DB View for this query and trigger on this view , so when ever an update operation comes , we do our logic in trigger i.e. decide whether to update or insert the data.
Can you please guide what should be done in such scenario .
Regards,
Siddharth
Edit : Code for Inserting Row (What I was trying but it's not working)
RowSetIterator rsi=iterator.getRowSetIterator();
Row editableRow= rsi.createRow();
while(rsi.hasNext()){
Row r =rsi.next();
if((""+r.getAttribute("DepartmentId")).toString().equals("280") ){
System.err.println("? Equality row found!!!");
editableRow.setAttribute("DepartmentId", r.getAttribute("DepartmentId"));
editableRow.setAttribute("DepartmentName", r.getAttribute("DepartmentName"));
editableRow.setAttribute("ManagerId", r.getAttribute("ManagerId"));
editableRow.setAttribute("LocationId", r.getAttribute("LocationId"));
editableRow.setAttribute("Active", r.getAttribute("Active"));
rsi.removeCurrentRowFromCollection();
}
}
if(editableRow !=null){
System.err.println("? Row value after removal : "+editableRow.getAttribute("DepartmentName"));
rsi.insertRow(editableRow);
operBindingCommit.execute();
}
Your use case can be implemented in a couple of ways. First way is to iterate over row set in managed bean and check if department with id 280 exists, if yes then update the row otherwise invoke Create with parameters for department VO. The second way, and would say the better way, is to create a method for update/insert at business component level, either in ViewObjectImpl or in ApplicationModuleImpl and then invoke it from managed bean.
Here is the sample code for insert/update method written in VOImpl
public void updateInsertJobs(String jobId, String jobTitle,
String minSalary, String maxSalary)
{
RowSetIterator rSet = this.createRowSetIterator(null);
JobsViewRowImpl row = new JobsViewRowImpl();
Boolean jobExist = false;
if (null != jobId)
{
try
{
while (rSet.hasNext())
{
row = (JobsViewRowImpl) rSet.next();
if (row.getJobId().equals(jobId))
{
row.setJobTitle(jobTitle);
row.setMinSalary(new Number(minSalary));
row.setMaxSalary(new Number(maxSalary));
jobExist = true;
}
}
if (!jobExist)
{
JobsViewRowImpl r = (JobsViewRowImpl) this.createRow();
r.setJobId(jobId);
r.setJobTitle(jobTitle);
r.setMinSalary(new Number(minSalary));
r.setMaxSalary(new Number(maxSalary));
this.insertRow(r);
}
this.getDBTransaction().commit();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Make sure to expose the method in Client Interface in order to be able to access it from data control.
Here is how to invoke the method from managed bean:
public void insertUpdateData(ActionEvent actionEvent)
{
BindingContainer bc =
BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding oB = bc.getOperationBinding("updateInsertJobs");
oB.getParamsMap().put("jobId", "TI_STF");
oB.getParamsMap().put("jobTitle", "Technical Staff");
oB.getParamsMap().put("minSalary", "5000");
oB.getParamsMap().put("maxSalary", "18000");
oB.execute();
}
Some references which could be helpful:
http://mahmoudoracle.blogspot.com/2012/07/adf-call-method-from-pagedefinition.html#.VMLYaf54q-0
http://adftidbits.blogspot.com//2014/11/update-vo-data-programatically-adf.html
http://www.awasthiashish.com/2012/12/insert-new-row-in-adf-viewobject.html
Your view object become readonly due to custom sql query.
However you still can create row in dept table using entity.
Create java implemetation including accessors for DeptEO.
Create custom method in view object and create new entity or update existing using entity definition there. To find that required row exist, you can check that entity with this key is already exists. Something like this (assuming deptId is your primary key):
public void createOrUpdateDept(BigInteger deptId){
DeptEOImpl dept;
EntityDefImpl deptDef = DeptEOImpl.getDefinitionObject();
Key key = new Key(new Object[]{deptId});
dept = deptDef.findByPrimaryKey(getDBTransaction(), key);
if (dept == null){
// Creating new entity if it doesn't exist
dept = deptDef.createInstance2(getDBTransaction(), null);
dept.setDepartmentId(deptId);
}
// Changing other attributes
dept.setDepartmentName("New name");
// Commiting changes and refreshing ViewObject if required
getDBTransaction().commit();
executeQuery();
}
This code is just a sample, use it as reference/idea, don't blindly copy/paste.

h2: "data conversion error" on array returned from stored procedure

This is a followup post to this post. I am writing an accounting system backed by an h2 database. The tree of accounts is stored in the ACCOUNTS table, with the PARENT_ID column storing the links in the tree.
To get the path to a given node in the tree, I have the following stored procedure:
public static Long[] getAncestorPKs(Long id)
whose job is to produce an array of integers, being the PARENT_ID values between the given node and the root of the tree. Let's imagine it is defined like this (because I have tried this and I get the same error):
public static Long[] getAncestorPKs(Long id)
{
return new Long[]{new Long(1), new Long(2), new Long(3)};
}
It is properly registered in the database and I can call it from within a SQL query. My problem is that h2 seems to be unable to deal with the return value: if I use it like this:
SELECT ID FROM ACCOUNTS WHERE ID IN (ANCESTOR_PKS(5))
then I get the following error:
Data conversion error converting "(1, 2, 3)"; SQL statement:
SELECT ID FROM ACCOUNTS WHERE ID IN (ANCESTOR_PKS(5)) [22018-167]
If, instead, I send the following to the database:
SELECT ID FROM ACCOUNTS WHERE ID IN (1, 2, 3)
I get back a result set with 3 rows, containing the three integers (exactly what I expect).
I really can't see what is the problem here! I am returning an array of Longs, which are to be used in comparing against a column which contains BIGINTS. Why is h2 refusing to convert this array? I have tried making the return value be Object[], because the h2 documentation is not entirely clear whether this is required on the return side as well as on the call side, but that makes no difference at all. I'm just banging my head against a brick wall here. This ain't rocket science! Surely someone has written similar code before?
Many thanks in advance, before I go mad!
If the method returns an array of objects, then for the database this is one value of data type ARRAY. And not a table with 3 rows. But of course you don't use the data type ARRAY in your table, you use INT or BIGINT. So your query is incorrect.
Either the method needs to return a ResultSet, or you need to convert the array value to a table. To do that, you could use the function TABLE(..) as follows:
select x from table(x bigint = getAncestorPKs(1));
So what you could do is:
drop table accounts;
create table accounts(id int);
insert into accounts values(1), (2), (10), (20);
drop alias getAncestorPKs;
create alias getAncestorPKs as 'Long[] getAncestorPKs(Long id) {
return new Long[]{new Long(1), new Long(2), new Long(3)};
}';
select * from accounts where id in
(select x from table(x bigint = getAncestorPKs(1)));

Correct method of deleting over 2100 rows (by ID) with Dapper

I am trying to use Dapper support my data access for my server app.
My server app has another application that drops records into my database at a rate of 400 per minute.
My app pulls them out in batches, processes them, and then deletes them from the database.
Since data continues to flow into the database while I am processing, I don't have a good way to say delete from myTable where allProcessed = true.
However, I do know the PK value of the rows to delete. So I want to do a delete from myTable where Id in #listToDelete
Problem is that if my server goes down for even 6 mintues, then I have over 2100 rows to delete.
Since Dapper takes my #listToDelete and turns each one into a parameter, my call to delete fails. (Causing my data purging to get even further behind.)
What is the best way to deal with this in Dapper?
NOTES:
I have looked at Tabled Valued Parameters but from what I can see, they are not very performant. This piece of my architecture is the bottle neck of my system and I need to be very very fast.
One option is to create a temp table on the server and then use the bulk load facility to upload all the IDs into that table at once. Then use a join, EXISTS or IN clause to delete only the records that you uploaded into your temp table.
Bulk loads are a well-optimized path in SQL Server and it should be very fast.
For example:
Execute the statement CREATE TABLE #RowsToDelete(ID INT PRIMARY KEY)
Use a bulk load to insert keys into #RowsToDelete
Execute DELETE FROM myTable where Id IN (SELECT ID FROM #RowsToDelete)
Execute DROP TABLE #RowsToDelte (the table will also be automatically dropped if you close the session)
(Assuming Dapper) code example:
conn.Open();
var columnName = "ID";
conn.Execute(string.Format("CREATE TABLE #{0}s({0} INT PRIMARY KEY)", columnName));
using (var bulkCopy = new SqlBulkCopy(conn))
{
bulkCopy.BatchSize = ids.Count;
bulkCopy.DestinationTableName = string.Format("#{0}s", columnName);
var table = new DataTable();
table.Columns.Add(columnName, typeof (int));
bulkCopy.ColumnMappings.Add(columnName, columnName);
foreach (var id in ids)
{
table.Rows.Add(id);
}
bulkCopy.WriteToServer(table);
}
//or do other things with your table instead of deleting here
conn.Execute(string.Format(#"DELETE FROM myTable where Id IN
(SELECT {0} FROM #{0}s", columnName));
conn.Execute(string.Format("DROP TABLE #{0}s", columnName));
To get this code working, I went dark side.
Since Dapper makes my list into parameters. And SQL Server can't handle a lot of parameters. (I have never needed even double digit parameters before). I had to go with Dynamic SQL.
So here was my solution:
string listOfIdsJoined = "("+String.Join(",", listOfIds.ToArray())+")";
connection.Execute("delete from myTable where Id in " + listOfIdsJoined);
Before everyone grabs the their torches and pitchforks, let me explain.
This code runs on a server whose only input is a data feed from a Mainframe system.
The list I am dynamically creating is a list of longs/bigints.
The longs/bigints are from an Identity column.
I know constructing dynamic SQL is bad juju, but in this case, I just can't see how it leads to a security risk.
Dapper request the List of object having parameter as a property so in above case a list of object having Id as property will work.
connection.Execute("delete from myTable where Id in (#Id)", listOfIds.AsEnumerable().Select(i=> new { Id = i }).ToList());
This will work.

Obtain 'Identity' setting for a column in VistaDB

I am reading the database schema for VistaDB 4.0 database using the standard ADO.NET 'DbConnection.GetSchema' API. I haven't found a way to obtain the 'Identity' setting for a column? The 'Columns' schema collection doesn't seem to have a column for this and I am not aware of any other collection that I should look into.
If it is not possible by querying any of the available collections, do I have to query some system table or view?
Any help would be appreciated.
There are no "sys" tables in VistaDB. There is a [database schema] table that contains most of what you need though.
[database schema]
You can get the identity columns for a database using the database schema table like this:
select * from [database schema] where typeid = 6
Look in the help file for the typeid list and what they mean.
Then once you have the list, you can match it up to the typeid for tables to see what table the identity column came from.
The only catch with the database schema table is that you cannot self reference or join it to itself (design limitation). So if you need to pull and reference from itself you have to do it in two commands, or through a temp table. The help file has an example of how to do this as well.
Alternate Way
You can also find all the identity columns using a VistaDB stored proc:
select * from VistaDBColumnSchema() where is_identity = true
DDA
If you need to find the next value, seed, etc you can also get those through DDA (Direct Data Access) methods.
The Identities property on an IVistaDBTableSchema object is a collection of the identities for that table. That collection can then be walked to pull the individual values.
The identity information included is the Seed, Step, Tablename, and Columnname.
ADO.NET GetSchemaTable Way
And yes, there is still another way. You can call GetSchemaTable on a reader to get some more information about the underlying structure.
using (VistaDBConnection cn = new VistaDBConnection("Data Source=" + dbName))
{
cn.Open();
using (VistaDBCommand cmd = new VistaDBCommand("Select * from simpletable", cn))
{
using (VistaDBDataReader myReader = cmd.ExecuteReader(CommandBehavior.KeyInfo))
{
//Retrieve column schema into a DataTable.
DataTable schemaTable = myReader.GetSchemaTable();
foreach (DataRow myField in schemaTable.Rows)
{
foreach (DataColumn myProperty in schemaTable.Columns)
{
System.Diagnostics.Debug.WriteLine(myProperty.ColumnName + " = " + myField[myProperty].ToString());
}
}
}
}
}

Resources