Room #Relation with two foreign keys - select

I have a three entities:
Lets say I wanna receive the Car with the Owner and Brand as with normal SELECT I only receive the ids of my foreign keys.
My approach was using #Relation:
public class CarWithPersonAndBrand {
#Embedded Person person;
#Relation(parentColumn = "id", entityColumn = "brandId", entity = Brand.class)
Car brand;
#Relation(parentColumn = "id", entityColumn = "personId", entity = Person.class)
Car car; //this would not make anysence???
}
However using the relation annotation, I receive the Object where the foreignkeys are annoted, however I want other way around. Rather than receiving two car objects, which dont make sence my suggest was receiving brand and person object using the #Relation anotation, would this be possible?

Consider that you want CarWithPersonAndBrand
So you want the Car with the respective(related) Person and with the respective Brand.
So you get the Car from the Car table and the related person and the related brand from the respective related tables.
So you could use:-
class CarWithPersonAndBrand {
#Embedded
Car car;
#Relation(entity = Person.class,parentColumn = "idPerson",entityColumn = "personId")
Person person;
#Relation(entity = Brand.class,parentColumn = "idBrand",entityColumn = "brandId")
Brand brand;
}
This was built using you diagram showing that the Car table has columns idPerson and idBrand and that your CarWithPersonAndBrand class indicates that the Person table has the column personId and that the Brand table has the column brandId (each uniquely identifying the respective row).
With the following Dao :-
#Transaction
#Query("SELECT * FROM car")
List<CarWithPersonAndBrand> getAllCarsWithPersonAndBrand();
And the following in an activity :-
public class MainActivity extends AppCompatActivity {
MotorDatabase db;
AllDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = MotorDatabase.getInstance(this);
dao = db.getAllDao();
Person p1 = new Person("Mary");
Person p2 = new Person("Fred");
Brand b1 = new Brand("Ford");
Brand b2 = new Brand("Chevrolet");
p1.personId = dao.insert(p1);
p2.personId = dao.insert(p2);
b1.brandId = dao.insert(b1);
b2.brandId = dao.insert(b2);
Car c1 = new Car("MyCar",p1.personId,b1.brandId);
Car c2 = new Car("TheirCar",p2.personId,b1.brandId);
dao.insert(c1);
dao.insert(c2);
List<CarWithPersonAndBrand> carsList = dao.getAllCarsWithPersonAndBrand();
for (CarWithPersonAndBrand c: carsList) {
Log.d("CARINFO","Car is " + c.car.carName + " owner is " + c.person.personName + " brand is " + c.brand.brandName);
}
}
}
Then the result output to the log of the above is :-
D/CARINFO: Car is MyCar owner is Mary brand is Ford
D/CARINFO: Car is TheirCar owner is Fred brand is Ford

Related

Using Expression to return selective columns on entities having multiple objects

I have an application where I have a model composed of several other objects. For instance:
class Customer
{
public int CustomerId{get;set;}
public int? AddressId {get;set;} // this is set as allow null in database
public string Name {get;set}
public virtual Addresss Address {get;set;}
}
class Address
{
public int AddressId {get;set}
public string A1 {get;set}
}
The idea is to use context.customers.include("Address"). However the model I am currently working on is much more complex than the above.
I have used https://stackoverflow.com/a/51772067 as a reference, but unfortunately this does not work for an id having no value (nullable, as the database allows nulls)
How can I modified the expression to behave as a true left join (includes an empty entity if the id is null).
Thanks in advance for your assistance
As per the official doc you could do left join as below
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
var query = from person in people
join pet in pets on person equals pet.Owner into gj
from subpet in gj.DefaultIfEmpty()
select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
When you use DefaultIfEmpty() then it becomes a left join.
Upvote if it works.

How to join tables using linq and entity framework

In the code given below i want to join all the three table i get the data by joining the table but while displaying the data only the data of CK_Model is displayed. Please help
public List<CK_Model> GetDetails()
{
try
{
using (var entities = new MobileStore2020Entities())
{
var details = from a in entities.CK_Model
join b in entities.CK_Brand
on a.BrandID equals b.BrandID
join c in entities.CK_Stock
on a.ModelID equals c.ModelID
select new
{
ModelID = a.ModelID,
ModelName = a.ModelName
};
return details.ToList();
Thank You.
If I understand correctly you want to return data from all 3 tables by accessing them through your context. If you want to do this you have change your method's return type. For example create a new class with all 3 item types
public class DetailsItemType
{
public CK_Model Model{ get; set; }
public CK_Brand Brand { get; set; }
public CK_Stock Stock { get; set; }
}
Then change your method's return type to DetailsItemType and you'll have something like the following
public List<DetailsItemType> GetDetails()
{
using (var entities = new MobileStore2020Entities())
{
var details = from a in entities.CK_Model
join b in entities.CK_Brand
on a.BrandID equals b.BrandID
join c in entities.CK_Stock
on a.ModelID equals c.ModelID
select new DetailsItemType
{
Model= a,
Brand = b,
Stock = c
};
return details.ToList();
}
}
Now every time you call GetDetails() you can access all 3 tables. For example
var details = GetDetails();
var model = details.Model;
var brand = details.Brand;
var stock = details.Brand;

How can I query specific columns from 2 tables inside my objects using JPA 2.0?

I am looking for a way to request specific columns and have the foreign object present in the root object using CriteriaBuilder. Here is the context:
I have EntityA
#Entity
#Table(name = "ENTITY_A")
public class EntityA {
int id;
int entityBKey;
EntityBObject entityBObject;
int AColumn1;
int AColumn2;
#Basic
public Long getEntityBKey() {
return entityBKey;
}
#ManyToOne
#JoinColumn(name = "ENTITY_B_FK")
public EntityBObject getProgramType() {
return entityBObject;
}
#Basic
#Column(name = "COLUMN_1")
public String getAColumn1() {
return AColumn1;
}
...
}
Then I have EntityB
public class EntityB {
int id;
int BColumn1;
int BColumn2;
...
}
Now, I want to request column AColumn1 from EntityA and column BColumn1 from EntityB, while having the object EntityB inside the EntityA. How can I achieve this ?
How can I modify the following to get a partial EntityA with an EntityB inside ?
public List<EntityA> findAll() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<EntityA> criteria = cb.createQuery(EntityA.class);
Root<EntityA> root = criteria.from(EntityA.class);
criteria.select(root);
return em.createQuery(criteria).getResultList();
}
Thanks !
Edit
#Tassos Bassoukos Yes, that's what I ended up doing, but it would get really messy when the request gets more complex. Ex.: Pull customers with their orders, with items for each orders. There would be so much java to achieve this, I though it could be automated so my object are automatically populated.
public List<EntityA> findAll() {
ArrayList<EntityA> result = new ArrayList<>();
Query q = em.createQuery("select eA, eB, from EntityA eA, EntityB eB where eA.key = eB.key");
#SuppressWarnings("unchecked")
List<Object[]> abc = q.getResultList();
for (Object[] array : abc) {
EntityA eA = (EntityA) array[0];
EntityB eB = (EntityB) array[1];
eA.setEntityB(eB);
result.add(pe);
}
return result;
}
First, why do you want a partial entity? That does not make sense from an OO perspective. Is there an actual, specific requirement for this?
Secondly, do you want entities or columns of entities? You can do both with CriteriaBuilder, but you need to be clear on a) what you want to achieve, b) why you want to achieve it.
Thirdly, there's JOIN FETCH.

How do I add a reference to another existing entity using code-first framework without first pulling the existing entity from the database

I'm trying to attach an existing product to an auction, but am unable to do so without first pulling the product from the database.
This code works, but how would I go about just providing the productid and then saving the auction
var product = new Product()
{
//Known existing product id
ProductId = 1
};
var auction = new Auction
{
BuyItNowPrice = 10.
Product = product,
...
...
};
using (var db = new DataContext())
{
var product = db.Products.Find(auction.Product.ProductId);
auction.Product = product;
db.Auctions.Add(auction);
db.SaveChanges();
}
Include the scalar property ProductId in the Auction class
public class Auction
{
public int Id {get;set;}
public int ProductId {get;set;}
public Product Product {get;set;}
//other proerties
}
Then
auction.ProductId = 1;
db.Auctions.Add(auction);
db.SaveChanges();

Defining multiple Address properties in Person per AddressType by a triple join table

I've here a database with a PERSON - ADDRESS - ADDRESS_TYPE relationship maintained by a triple join table PERSON_ADDRESS. The PERSON-ADDRESS relationship is effectively one-to-many.
PERSON
ID FIRSTNAME LASTNAME
-- --------- --------
1 John Doe
2 Jane Doe
ADDRESS
ID STREET CITY
-- -------------------- -------------
1 Home Street 1 Hometown
2 Office Street 1 Officetown
3 Main Street 1 Maintown
4 Business Building 1 Businesstown
ADDRESS_TYPE
ID NAME
-- ---------------
1 Home Address
2 Office Address
PERSON_ADDRESS
PERSON_ID ADDRESS_TYPE_ID ADDRESS_ID
--------- --------------- ----------
1 1 1
1 2 2
2 1 3
2 2 4
For practical reasons, I'd like to have my Person entity to end up like:
public class Person {
private Address homeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=1
private Address officeAddress; // Insertable/updateable by ADDRESS_TYPE_ID=2
}
Is this ever possible with JPA 2.0 annotations?
I've read the Map Key Columns chapter of the JPA wikibook and it seems that I have to use a #MapKeyJoinColumn, but it's not entirely clear to me how to successfully use it in this situation. I expected to see a #JoinColumn example along it, but it is absent in the code snippets in the wikibook.
If that's not possible with #MapKeyJoinColumn, then an alternative approach with help of maybe #MapKeyClass on a Map<AddressType, Address> is also welcome, as long as I can end up with a getHomeAddress() and getOfficeAddress() in the Person entity.
Assuming that you have two predefined AddressTypes when other can be added, the following approach with #MapKeyJoinColumn works:
public class AddressType {
public static final AddressType HOME = new AddressType(1L, "Home");
public static final AddressType OFFICE = new AddressType(2L, "Office");
...
... hashCode, equals based on id ...
}
public class Person {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "PERSON_ADDRESS",
joinColumns = #JoinColumn(name = "PERSON_ID"),
inverseJoinColumns = #JoinColumn(name = "ADDRESS_ID"))
#MapKeyJoinColumn(name = "ADDRESS_TYPE_ID")
private Map<AddressType, Address> addresses = new HashMap<AddressType, Address>();
...
public Address getHomeAddress() {
return getAddress(AddressType.HOME);
}
public void setHomeAddress(Address a) {
setAddress(AddressType.HOME, a);
}
...
public void setAddress(AddressType type, Address a) {
if (a == null) {
addresses.remove(type);
} else {
addresses.put(type, a);
}
}
public Address getAddress(AddressType type) {
return addresses.get(type);
}
}
So, you have predefined methods for predefined address types, when other types can be used via direct access to the map. orphanRemoval is used to implement setHomeAddress(null) behaviour. #ElementCollection wouldn't work here because it doesn't support join table.