embedded ids: primary key and foreign key - java

Given entity with embedded id and embedded foreign key:
#Entity
public class One {
#EmbeddedId
private ModelId id;
#Embedded
#AttributeOverride(name = "id", column = #Column(name = "two_id"))
private ModelId twoId;
}
#Embeddable
public class ModelId {
private Integer id;
}
when in JPQL:
SELECT o.id FROM One o WHERE o.twoId IN :twoId
then JPA generates SQL:
SELECT id FROM one WHERE id IN (?)
instead of:
SELECT id FROM one WHERE two_id IN (?)
How to make it work?

put the keys inside ModelID entity and use a reference of it as PK inside entity One:
#Entity
public class One {
#EmbeddedId
protected ModelId modelPK;
//getter and setter
}
public class ModelId {
#column(name="id")//give the real column name
private int id;
#column(name="towID")//give the real column name
private int twoId;
//getters and setters
}
and then use below JPQL query:
SELECT o.modelPK.id FROM One o WHERE o.modelPK.twoId=:twoId

Related

How to get additional column from different table with Spring Data?

So lets imagine following situation. I have an entity such as this:
#Entity
public class Price {
#Id
private int id;
#Column
private int amount;
private String currency;
}
And I have two tables:
CREATE TABLE currency (
id integer not null primary key,
name varchar
);
CREATE TABLE price (
id integer not null primary key,
amount integer,
currency_id integer references currency(id)
);
I want to tell Spring that when I access Price.getCurrency() I want to have whatever is stored in column "name" of the "currency" table. In other words, I want to connect two tables in one entity.
I can make currency a separate class, annotate the property with #OneTo... and get it like price.getCurrency().getName(). But I don't want a separate class, I just need this specific column.
I tried adding it via #SecondaryTable annotation like this:
#SecondaryTable(name = "currency",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "id", referencedColumnName = "currency_id"))
But in this case Spring connect two tables by it's ids like this:
SELECT * FROM price LEFT JOIN price ON price.id = currency.id
And of course it is not working. So how do I do this? Is #SecondaryTable a correct way and if so how do I connect it through non-primary key column?
Yes, you can use #SecondaryTable:
#Entity
#Table(name = "price")
#SecondaryTable(
name = "currency",
pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "currency_id")
})
public class Price {
#Id
private int id;
#Column
private int amount;
#Column(table = "currency", name = "name")
private String currency;
}

org.springframework.orm.jpa.JpaSystemException: ERROR: missing FROM-clause entry for table "attributeid"

Query to fetch data:
JPA not able to read attributeId table.
-- select query to fetch data
select r,a from data r ,
Attributes a
where a.attributeId.type != 'test'
and r.typeid = a.attributeId.typeid
and r.deviceid=:deviceid order by r.typeid;
-- table1
#Entity
#Table(name = "data")
public class data {
#Id
#Column(name = "typeid")
private Integer typeid;
--- table 2
#Entity
#Table(name = "attributes")
public class Attributes implements Serializable {
#EmbeddedId
private Attributeid attributeId;
#Column
private String value;
-- Class with composite keys
#Embeddable
public class Attributeid implements Serializable {
#Column
private Integer typeid;
#Column
private String type;
#Column
private String attributename;
Your query is wrong. It's JPQL not SQL so you have to join with on clause.
It should be
select r, a from data r join Attributes a on r.typeid = a.attributeId.typeid
where a.attributeId.type != 'test'
and r.deviceid=:deviceid order by r.typeid;
Is the relationship to Attributes oneToOne or onToMany?
The question is why you don't map the relationship but the only the attributes?

JPA OneToMany-Mapping without mapping class for Many-side

In a Spring Boot application using JPA 2.1 and Hibernate, there are two (PostgreSQL) tables of interest:
entity external_id
-- --
id serial id serial
... entity_id int
... external_id int
The relation between entity and external_ids is obviously OneToMany, which I want to use in the JPA mapping as well. A simple way to do this is to create #Entity-mappings for each table and use a #OneToMany-relation:
#Entity
public class Entity {
#Id
private Integer id;
#OneToMany(mappedBy= "entityId")
private Set<ExternalId> externalIds;
}
#Entity
public class ExternalId {
#Id
private Integer id;
#ManyToOne
private Integer entityId;
private Integer externalId;
}
But since the table external_ids just holds a list of numbers for each member of entity, I would like to go without an explicit mapping of the table external_id and immediately map the values of external_id.external_id:
#Entity
public class Entity {
#Id
private Integer id;
#OneToMany(???)
private Set<Integer> externalIds;
}
Is this possible with JPA 2.1 and if so how?
You can utilize #ElementCollection for this purpose:
#ElementCollection
#CollectionTable(name = "TableName", joinColumns=#JoinColumn(name = "JoinColumnName"))
private Set<Integer> externalIds;

Hibernate criteria on embedded id member member value

I would like to find an entity using a critera with restriction on the value of an attribute of a second entity wich is a member of the embedded id of my first entity.
First entity :
#Entity
public class Car {
#EmbeddedId
private Id id = new Id();
private String color;
#Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = -8141132005371636607L;
#ManyToOne
private Owner owner;
private String model;
// getters and setters...
// equals and hashcode methods
}
// getters and setters...
}
Second entity :
#Entity
public class Owner {
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
#OneToMany (mappedBy = "id.owner")
private List<Car> cars;
// getters and setters...
}
In this example, I would like to obtain the car with the color 'black', model 'batmobile' and the owner's firstname 'Bruce' (oops... spoiler ;) )
I tried to do something like that but it won't work :
List<Car> cars = session.createCriteria(Car.class)
.add(Restrictions.eq("color", "black"))
.add(Restrictions.eq("id.model", "batmobile"))
.createAlias("id.owner", "o")
.add(Restrictions.eq("o.firstname", "Bruce"))
.list();
Result :
Hibernate: select this_.model as model1_0_0_, this_.owner_id as owner_id3_0_0_, this_.color as color2_0_0_ from Car this_ where this_.color=? and this_.model=? and o1_.firstname=?
ERROR: Unknown column 'o1_.firstname' in 'where clause'
What is the right way to obtain what I want ?
update
I tried in hql :
String hql = "FROM Car as car where car.color = :color and car.id.model = :model and car.id.owner.firstname = :firstname";
Query query = em.createQuery(hql);
query.setParameter("color", "black");
query.setParameter("model", "batmobile");
query.setParameter("firstname", "Bruce");
List<Car> cars = query.getResultList();
It works but is there a way to do this with criteria ?
You forgot to add the #Column annotation on top of the firstname and lastname fields (and the color field in Car). In hibernate if a field is not annotated, it doesn't recognize it as a database field. This page should give you a good idea about how to set up your model objects.
NOTE: You can have the column annotation over the getters and be fine, but you didn't show the getters. Either place is fine.
Look at what HQL is spitting back out, specifically the statement (formated for easier reading):
select
this_.model as model1_0_0_,
this_.owner_id as owner_id3_0_0_,
this_.color as color2_0_0_
from Car this_
where
this_.color=?
and this_.model=?
and o1_.firstname=?
It looks like hibernate is translating the field "id.owner" to "o" as your alias told it to to, but for some reason it's not writing down that "id.owner=o" as intended. You may want to do some research into why it may be doing that.
As per https://hibernate.atlassian.net/browse/HHH-4591 there is a workaround.
You have to copy the needed relation-property of the #EmbeddedId (owner in this case) to the main entity (Car in this case) with insertable = false, updatable = false as follows
#Entity
public class Car {
#EmbeddedId
private Id id = new Id();
private String color;
#ManyToOne
#JoinColumn(name = "column_name", insertable = false, updatable = false)
private Owner owner;
#Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = -8141132005371636607L;
#ManyToOne
private Owner owner;
private String model;
// getters and setters...
// equals and hashcode methods
}
// getters and setters...
}
Then just create directly the alias instead of using the composite id property
List<Car> cars = session.createCriteria(Car.class)
.add(Restrictions.eq("color", "black"))
.add(Restrictions.eq("id.model", "batmobile"))
.createAlias("owner", "o")
.add(Restrictions.eq("o.firstname", "Bruce"))
.list();

JPA Join using arbitrary field (not primary key)

I've got two entities that I want to join together using a field they have in common, called shared_id. The field is not the primary key of either entity. The shared_id is unique - each Hipster will have a unique shared_id.
The tables look like:
Hipster Fixie
========= ========
id id
shared_id shared_id
There is a OneToMany relationship between Hipsters and their Fixies. I've tried something like this:
#Entity
public class Hipster {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "shared_id")
private Integer sharedId;
#OneToMany(mappedBy = "hipster")
private List<Fixie> fixies;
}
#Entity
public class Fixie {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name = "shared_id", referencedColumnName = "shared_id")
private Hipster hipster;
}
#Repository
public class HipsterDAO {
#PersistenceContext
private EntityManager entityManager;
public Hipster getHipsterBySharedId(Integer sharedId) {
String queryString = "SELECT h FROM Hipster h WHERE h.sharedId = :sharedId";
TypedQuery<Hipster> query = entityManager.createQuery(queryString, Hipster.class);
query.setParameter("sharedId", sharedId);
try {
return query.getSingleResult();
} catch (PersistenceException e) {
return null;
}
}
}
Now, my DAO gives me this error:
java.lang.IllegalArgumentException: Can not set java.lang.Integer field Hipster.sharedId to java.lang.Integer
I think it's upset because the sharedId field is used in a relation, rather than just being a basic field. I haven't included the sharedId field in the Fixie entity, but I get the same result if I do. How do I persuade it to run this query for me? Do I need to change the query or the entities?

Categories

Resources