Eclipselink Inheritance policy causing malformed query - eclipselink

There are three entities namely Employee, Person and Address. There is a parent-child relationship between Person and Employee (Employee IS-A Person).
There is a 1:1 relationship from Person to Address. (A Person is assumed to have one permanent address).
The key properties of Employee class are: 1. employeeId(pk) 2. personId(fk)
The key properties of Person class are: 1. pId(pk) 2. pCode
The key properties of Address class are: 1. addressId(pk) 2. employeeId(fk)
The following are the descriptor code snippets for Person, Employee and Address classes:
public RelationalDescriptor buildPersonDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(Person.class);
descriptor.addTableName("PERSON");
descriptor.addPrimaryKeyFieldName("PERSON.PID");
// Inheritance properties.
descriptor.getInheritancePolicy().setClassIndicatorFieldName("PERSON.PCODE");
descriptor.getInheritancePolicy().addClassIndicator(Employee.class, "EMP");
// RelationalDescriptor properties.
descriptor.useSoftCacheWeakIdentityMap();
descriptor.setIdentityMapSize(100);
descriptor.useRemoteSoftCacheWeakIdentityMap();
descriptor.setRemoteIdentityMapSize(100);
descriptor.setSequenceNumberFieldName("PERSON.PID");
descriptor.setSequenceNumberName("PERSON_SEQ");
descriptor.setAlias("Person");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Named Queries
DirectToFieldMapping productIDMapping = new DirectToFieldMapping();
productIDMapping.setAttributeName("pId");
productIDMapping.setFieldName("PERSON.PID");
descriptor.addMapping(productIDMapping);
DirectToFieldMapping productIDMapping = new DirectToFieldMapping();
productIDMapping.setAttributeName("pCode");
productIDMapping.setFieldName("PERSON.PCODE");
descriptor.addMapping(productIDMapping);
return descriptor;
}
public RelationalDescriptor buildEmployeeDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(Employee.class);
descriptor.addTableName("EMPLOYEE");
// Inheritance properties.
descriptor.getInheritancePolicy().setParentClass(Person.class);
// RelationalDescriptor properties.
descriptor.setAlias("Employee");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Named Queries
// Event manager.
// Mappings.
DirectToFieldMapping employeeIdMapping = new DirectToFieldMapping();
employeeIdMapping.setAttributeName("employeeId");
employeeIdMapping.setFieldName("EMPLOYEE.EID");
descriptor.addMapping(employeeIdMapping);
DirectToFieldMapping personIdMapping = new DirectToFieldMapping();
personIdMapping.setAttributeName("personId");
personIdMapping.setFieldName("EMPLOYEE.PID");
descriptor.addMapping(personIdMapping);
OneToOneMapping addressMapping = new OneToOneMapping();
addressMapping.setAttributeName("address");
addressMapping.setReferenceClass(Address.class);
addressMapping.dontUseIndirection();
addressMapping.addTargetForeignKeyFieldName("ADDRESS.EID", "EMPLOYEE.EID");
descriptor.addMapping(addressMapping);
return descriptor;
}
public RelationalDescriptor buildAddressDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(com.tropics.application.products.domain.costingandpricing.SellingPriceAddOn.class);
descriptor.addTableName("ADDRESS");
descriptor.addPrimaryKeyFieldName("ADDRESS.AID");
// Descriptor properties.
descriptor.useSoftCacheWeakIdentityMap();
descriptor.setIdentityMapSize(100);
descriptor.useRemoteSoftCacheWeakIdentityMap();
descriptor.setRemoteIdentityMapSize(100);
descriptor.setSequenceNumberFieldName("ADDRESS.AID");
descriptor.setSequenceNumberName("ADDRESS_SEQ");
descriptor.setAlias("address");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Mappings
DirectToFieldMapping personIDMapping = new DirectToFieldMapping();
personIDMapping.setAttributeName("employeeId");
personIDMapping.setFieldName("ADDRESS.EID");
descriptor.addMapping(personIDMapping);
DirectToFieldMapping addressIDMapping = new DirectToFieldMapping();
addressIDMapping.setAttributeName("addressId");
addressIDMapping.setFieldName("ADDRESS.AID");
descriptor.addMapping(addressIDMapping);
}
Following is the code snippet for generating the dynamic query:
ExpressionBuilder employee = new ExpressionBuilder();
ReportQuery query = new ReportQuery(Employee.class,employee);
Expression address = employee.getAllowingNull("address");
query.addAttribute("pId");
query.addAttribute("pCode");
query.addAttribute("employeeId");
query.addAttribute("addressId",address.get("addressId"));
query.addNonFetchJoin(employee.leftJoin(address,
address.get("employeeId")));
resultCollection = (Vector) clientSessionHolder.eclipselinkClientSession().executeQuery(query);
On running this program, the query which is generated as per logs:
SELECT t0.PID, t0.PCODE, t1.EID, t2.AID FROM PERSON t0 LEFT OUTER JOIN ADDRESS t2 ON ((t2.EID = t1.EID),EMPLOYEE t1 WHERE ((t1.PID = t0.PID) AND (t0.PCODE = 'EMP'));
The expected query is:
SELECT t0.PID, t0.PCODE, t1.EID, t2.AID FROM PERSON t0,EMPLOYEE t1 LEFT OUTER JOIN ADDRESS t2 ON ((t2.EID = t1.EID) WHERE ((t1.PID = t0.PID) AND (t0.PCODE = 'EMP')));
The table t1 is not correctly applied in the join clause.
Could anyone help me with what's wrong with the expression?
Waiting for a positive reply.

I ran into the same problem. Left join between two not linked entities (person left join address in your case) through another entities (employee).
I used JPQL. These entities could be linked directly by IDs, but eclipselink (2.6.2) added implicit join after the left join. And referred (in left join) to ID from implicit join. It broke the query. I got the exception "ORA-00904: "T3"."ID": invalid identifier".
I solved my problem by removing left join and adding exists and subquery. I did not need to receive entity from left join.
You can try add explicit join on employee before the left join on address. It did not help me. My eclipselink changing join on implicit join. But maybe you are lucky.
Or you can add left join on employee before the left join on address. It helps. But maybe this is not what you need.

Related

jpql left join condition

I don´t get that simple query working and i don´t now why. i have three entities (just semantics code):
Profile {
#OneToMany
Set<ProfileClubMapping> clubs
String username
...
}
ProfileClubMapping {
#ManyToOne
Profile profile
#ManyToOne
Club club
...
}
Club {
#OneToMany
Set<ProfileClubMapping> profiles
...
}
now i want to select every profile which contains a string in the username and the ProfileClubMapping with a certain Club id. If no ProfileClubMapping exists just return null for the ProfileClubMapping.
I tried something like that:
SELECT p,pcm FROM Profile p LEFT JOIN p.clubs pcm ON pcm.club.id = :clubId WHERE UPPER(p.username) = UPPER(:str)
but this query only returns me the profiles which have an ProfileClubMapping.
Anyone an idea?
EDIT: maybe the sql query helps:
SELECT p.USERNAME, pcm.CLUB_ID FROM PROFILE AS p LEFT JOIN PROFILECLUBMAPPING AS pcm ON pcm.PROFILE_ID = p.ID AND pcm.CLUB_ID = :clubId WHERE p.USERNAME LIKE '%:username%';
Us LEFT OUTER JOIN instead. The behaviour of join is the same as with normal SQL. See for example -> http://www.w3schools.com/sql/sql_join_left.asp

Entity Framework left outer join not working

I have an Entity Framework model with the following classes (I have simplified the classes for simpler viewing):
PuzzleItem
-PuzzleId (int, primary key)
-Title
PuzzleProgress
-ProgressId (int, primary key)
-PuzzleId (FK)
-UserId (FK)
In PuzzleItem I have a number of levels. The PuzzleProgress keeps track of which level the user is at by inserting a record of that level when the user completes a previous level. For a start a new user will have one entry in PuzzleProgress with PuzzleId = 1.
In my code, I am using the following statement to perform a left outer join, so that I will get a list of all puzzles, and indicating to me which puzzle has not been solved. I made reference to this post from StackOverflow.
Here is my code:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on pzs equals prg.PuzzleItem
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
where pzs_prg.UserId == userId
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};
After running the code, only the first level of this new user is returned (while the PuzzleItem table has 3 records).
I've tried playing around with the code but the one I pasted above is the nearest I can get to, can anybody point me in the correct direction? Thanks!
It's a bit hard to say exactly without seeing more of the code, but where pzs_prg.UserId == userId is probably negating the left outer join.
What I mean is, if you're intending PuzzleItems LEFT JOIN PuzzleProgress then you want all PuzzleItems even when there is no PuzzleProgress. But the where pzs_prg.UserId == userId means PuzzleProgress cannot be null, because it must have a UserId (of value userId). So, you effectively have an inner join.
Personally, I don't like the "correct" way of doing joins (left or inner) in linq, so this is how I would correct the linq statement:
var result = from pz in db.PuzzleItems
from pg in db.PuzzleProgresses
.Where(pg => pg.PuzzleId == pz.PuzzleId)
.Where(pg => pg.UserId == userId)
.DefaultIfEmpty()
select new
{
PuzzleId = pz.PuzzleId,
PuzzleName = pz.Title,
IsUnlocked = (pg != null)
};
This reads a lot more like SQL joins, which I learned a long time ago so it fits my thinking.
If you want to re-factor the join type syntax, look at this 'LINQ Joining in c# with multiple conditions'
I think the where clause is filtering out the records. You need to include the where clause in your left join. Like so:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on new { pzs.PuzzleId, UserId = userId } equals new { prg.PuzzleId, prg.UserId }
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};

How to join dbo.LocalizedLabelView to get form labels in Dynamics CRM?

In Dynamics CRM, I often get requirements from business users to create reports. Business users know and speak about entity display names and attribute labels. To write a query, I need to map those to entity names and attribute names. I would like to use a query to look this up.
To what do I join the dbo.LocalizedLabelView view to get the AttributeLabel column in the following query? I can't figure out what ObjectId is supposed to reference. (And if you can tell me how you figured out the answer I'd be especially appreciative!)
select
[EntityName] = entityNames.Name,
[EntityDisplayName] = entityDisplayNames.Label,
[AttributeName] = attributeNames.PhysicalName,
[AttributeDisplayName] = attributeDisplayNames.Label
--[AttributeLabel] = attributeLabels.Label
from
dbo.EntityView entityNames
inner join dbo.LocalizedLabelView entityDisplayNames
on entityDisplayNames.ObjectId = entityNames.EntityId
and entityDisplayNames.ObjectColumnName = 'LocalizedName'
left outer join dbo.AttributeView attributeNames
on attributeNames.EntityID = entityNames.EntityID
inner join dbo.LocalizedLabelView attributeDisplayNames
on attributeDisplayNames.ObjectId = attributeNames.AttributeID
and attributeDisplayNames.ObjectColumnName = 'DisplayName'
and attributeDisplayNames.LanguageID = entityDisplayNames.LanguageID
--inner join dbo.LocalizedLabelView attributeLabels
-- on attributeLabels.ObjectId = ?????
-- and attributeLabels.LanguageID = entityDisplayNames.LanguageID
where
entityDisplayNames.LanguageID = 1033
order by
entityDisplayNames.Label,
attributeDisplayNames.Label
ObjectId is a reference to the internal ID of a thing in the CRM database. This thing can be an attribute, entity, label, or whatever.
Since you want the label for your attribute, use that attribute's id as the ObjectId here. I think that you want your join condition to look like this:
inner join dbo.LocalizedLabelView attributeLabels
on attributeLabels.ObjectId = attributeNames.AttributeID
and attributeLabels.LanguageID = entityDisplayNames.LanguageID
and attributeLabels.ObjectColumnName = 'DisplayName'
If you want the description for the attribute, you can change the ObjectColumnName to 'Description'.

AX2009 EP Custom lookup with joined tables

I'm trying to make custom lookup (in .Net) using 2 joined tables:
AxLookup nameLookup = e.LookupControl;
Proxy.QueryBuildDataSource emplTable = null;
Proxy.QueryBuildDataSource dirPartyTable = null;
using (Proxy.SysDataSetBuilder sysDataSetBuilder = proxy.SysDataSetBuilder.constructLookupDataSet(this.AxSession.AxaptaAdapter, TableMetadata.TableNum(this.AxSession, "EmplTable")))
{
nameLookup.LookupDataSet = new DataSet(this.AxSession, sysDataSetBuilder.toDataSet());
}
using (Proxy.Query query = nameLookup.LookupDataSet.DataSetViews[0].MasterDataSource.query())
{
emplTable = query.dataSourceNo(1);
dirPartyTable = emplTable.addDataSource(TableMetadata.TableNum(this.AxSession, "DirPartyTable"));
dirPartyTable.clearLinks();
dirPartyTable.addLink(TableDataFieldMetadata.FieldNum(this.AxSession, "EmplTable", "PartyId"), TableDataFieldMetadata.FieldNum(this.AxSession, "DirPartyTable", "PartyId"));
dirPartyTable.joinMode = 6; //should be an exists join
}
nameLookup.LookupDataSet.Init();
nameLookup.Fields.Add(AxBoundFieldFactory.Create(this.AxSession, nameLookup.LookupDataSetViewMetadata.ViewFields["EmplId"]));
nameLookup.Fields.Add(AxBoundFieldFactory.Create(this.AxSession, nameLookup.LookupDataSetViewMetadata.ViewFields["DirPartyTable!Name"])); //not working!!
nameLookup.SelectField = "EmplId";
Now when I use the lookup it gives me an errror about the Name field in DirPartyTable (key not found) Does someone know how to add a lookupfield in a joined table?
I'm sure the join works ok, made some other lookups with ranges on the joined table (and ofcourse no lookupfields on the second table) and that works ok.
Any help is appreciated!
This has been discussed in the following blog post:
http://palleagermark.blogspot.com/2009/12/data-set-lookups-on-enterprise-portal.html
The answer is that you cannot use a datasource with a join with the AxLookup control as it was built to support only one DataSetView and will throw this exception if you have more than one.

NHibernate Left Outer Join Unrelated Entities

I have 2 entities Role & Translation.
Role -> Role_ID, Code
Translation -> Code, Language, Name
The idea is to say for a certain role, that it has English name, French name and so on.
For example:
A Role(1, 'Rol_001') can have the relations: Translation('Rol_001', 'English', '') & Translation('Rol_001', 'French', '').
I would like to express the following SQL query in HQL:
select r.Role_ID, t.Name
from Role r left outer join Translation t
on r.Code = t.Code and t.Language = #lang;
In my mapping files I don't have any relation between the 2 entities but the following HQL query works as if it is inner join
IQuery query = session.CreateQuery("select new Lookup(r.Role, t.Name) from Role r, Translation t where r.Code = r.Code and t.Language = :language");
If I change the HQL to left outer join, I get the Path expected for join exception.
Can you help me with the following:
1- Do I need to change my mapping files?
2- If I can keep the mapping files as is, how write such a query in HQL?
3- How does HQL really works? Why such a simple outer join query is not working? I must be missing something here!
Edit:
Now I am using the following code based on the suggetion to use CreateSQL:
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.SetString("language", language);
IList rawLookup = query.List();
IList medicineTypesLookup = new List(rawLookup.Count);
foreach (object[] lookup in rawLookup)
{
medicineTypesLookup.Add(new Lookup((int)lookup[0], (string)lookup[1], (bool)lookup[2]));
}
return medicineTypesLookup;
This is working however I want to use query.List() to get the result directly instead of converting it myself.
I tried to use query.AddEntity(typeof(Lookup)); but I get the exception NHibernate.MappingException: No persister for: DAL.Domain.Lookup.
The Lookup is just a POCO and doesn't map to any database table. Its mapping file is simply <import class="Lookup" />
Finally I found the answer:ISession session = NHibernateHelper.Session;
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID as ID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.setString("language", language);
IList lookup = query.SetResultTransformer(Transformers.AliasToBean()).List();
return lookup;
And the lookup is a POCO class with a parameterless constructor and 3 properties ID, Name and IsDeleted.
I would like to thank Kelly and Diego Mijelshon for their hints. Although they don't provide the full answer the but using Session.CreateSqlQuery() was a very useful hint.
So the complete solution is Session.CreateSQLQuery and query.SetResultTransformer
Note: Transformers.AliasToBean() is so java.
Edit: http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/impl/SQLQueryImpl.html for correct method of setString()
You must to define the relationship in the mappings or do a subquery