I have two tables:
Limit_utilisation_history with fields:
bigint limit_utilisation_history_id (PK)
numeric(20,2) utilisation_amount
bigint limit_id (FK to limit_utilisation table)
Limit_utilisation with fields:
bigint limit_id (PK)
varchar customer_id
So both tables are related.
I need to expose the result of the following query via rest call:
select limit_utilisation_history_id, utilisation_amount, limit_id, customer_id
where customer_id in (some list of values)
I have done it in the following way:
#Entity
#Data
public class LimitUtilisation{
#Id
private Long limitIdl
private String customerId;
}
#Entity
#Data
#NamedQuery{
name = "LimitUtilisationHistory.getByCustomer",
query = "FROM LimitUtilisationHistory luh FETCH ALL PROPERTIES " +
"INNER JOIN luh.limitId al " +
"WHERE al.customerId in :values"
}
public class LimitUtilisationHistory{
#Id
private Long limitUtilisationHistoryId;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "limit_id", referencedColumnName = "limitId")
private LimitUtilisation limitId;
}
public interface LimitUtilisationHistoryRepository extends PagingAndSortingRepository<LimitUtilisationHistory, Long> {
#RestResource(path = "byCustomerId")
#Query
List<LimitUtilisationHistory> getByCustomer (#Param("values") List<String> customer);
}
It works fine, however when I call my rest endpoint I have only utilisation_amount value, others (mainly PK, FK, customer id are missing).
Does anyone have an idea how to do it correctly?
select limit_utilisation_history_id, utilisation_amount, limit_id, customer_id
where customer_id in (some list of values)
Remark: I cannot update DB structure. My intention is to only read from existing DB structure
As you you using FetchType.EAGER I don't see any reason of using FETCH ALL in the query, Could you please try with the below query :
select luh.limit_utilisation_history_id, luh.utilisation_amount, luh.limit_id, lu.customer_id
from LimitUtilisationHistory luh , LimitUtilisation lu
where luh.limit_id = lu.limit_id
and lu.customerId in :values
Related
I am new to spring, sql world and would love if someone can help me here.
I have three tables which are
Table1
-id, appName, version, education ,region
Table2
-id, appName, status, subjects, result
Table3
-id, appName, type, state, sports
Note:
id: primary key
appName: unique key
I want to create a controller where request body is "region", "status" and I want to return json of appName,status,version,type,version,region.
How can I achieve the same in springboot.
I was thinking to extend crudRepository but not able figure out the query to use and implementation part.
PS:Thank you in advance.
You should have one entity which is going to contains Table1, Table2, table3 data.
Use hibernate to join the table with all tables.
Example.
#Entity
Class Table1 {
#Id
Long id;
String appName;
...
}
#Entity
Class Table2 {
#Id
Long id;
String appName;
...
}
#Entity
Class Table3 {
#Id
Long id;
String appName;
...
}
#Entity
Class MainObject {
#Id
Long id;
#OneToOne
Table1 table1;
#OneToOne
Table2 table2;
...
}
Use main object to use curdRepository
Note: use mapping based on your requirements. https://www.tutorialspoint.com/hibernate/hibernate_or_mappings.htm
Suggestion: you can add app name to main object as it is repetitive in all tables
I got following tables. Lets ignore the fact that the relation is done wrong here. I cannot change that.
Each company can have multiple employes and each employe belongs to only one company.
Table: Company
ID
EMPLOYE_ID
10
100
Table: Employe
ID
NAME
100 (Same as EMPLOYE_ID)
John
Now i want to create a relation #OneToMany between Company -> Employe . My entities look as follow
class Company {
#Id
#Column(name = "id", unique = true, nullable = false)
private String id;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "EMPLOYE_ID", referencedColumnName = "ID")
private Set<Employe> employees;
}
No matter if i try to create a uniderectional, or biderection relationship by adding also #ManyToOne on my Employe class, when using Criteria api to select all Company entities and their Employes i always end up with a wrong generated SQL query at the point where it joines the tables. The above relation for example creates following:
FROM company company0
INNER JOIN employe employe0 ON company0.id = employe0.employe_id
I tried several approaches, but i end up almost with the same error. It tries either to access a column which does not exist on the table, or joins wrong columns (e.g. id = id). Or by the following exception
Caused by: org.hibernate.MappingException: Repeated column in mapping
for entity: com.Employe column: id (should be mapped with
insert="false" update="false")"}}
What is a simple approach to create a bidrectional relation with the above table structure?
Note: I finally ended up changing the DB schema. Still, it would be interesting if someone could provide an answer for such a case, even if it is based on a not well formed
The central problem is that the described table structures do not allow a 1:n relationship from Company to Employee. According to the table design (especially the design of PKs) above, a company can only have one employee.
However, if the DB design cannot be changed, the following approach using the JoinColumnOrFormula annotation may lead to partial success.
The #JoinColumnOrFormula annotation is used to customize the join between a child Foreign Key and a parent row Primary Key when we need to take into consideration a column value as well as a #JoinFormula.
See https://docs.jboss.org/hibernate/stable/orm/userguide/html_single/Hibernate_User_Guide.html#associations-JoinColumnOrFormula for details.
More concretely with these Entities
#Entity
#Table(name="t_company")
public class Company {
#Id
#Column(name="id")
private Integer id;
#Column(name="employee_id")
private Integer employeeId;
#OneToMany(mappedBy = "company")
private List<Employee> employees;
// ..
}
#Entity
#Table(name = "t_employee")
public class Employee {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumnOrFormula( column =
#JoinColumn(
name = "id",
referencedColumnName = "employee_id",
insertable = false,
updatable = false
)
)
private Company company;
// ..
}
and this custom repository
#Repository
public class EmployeeRepository {
#Autowired
EntityManager entityManager;
List<Employee> findAll() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
Join<Employee, Company> joinCompany = root.join("company");
TypedQuery<Employee> query = entityManager.createQuery(cq);
return query.getResultList();
}
}
you get the following query:
select
employee0_.id as id1_1_,
employee0_.name as name2_1_
from t_employee employee0_
inner join t_company company1_ on employee0_.id=company1_.employee
It`s possible to create one map with hibernate #ManyToOne just like this:
public class IndicadorAtos {
#JsonIgnore
#Id
#Column(name="cod_ato_praticado")
private Integer codAtoPraticado;
#Column(name="descricao_ato")
private String ato;
#JoinColumn(name = "cod_ato", referencedColumnName = "cod_ato")
#ManyToOne
#Fetch(FetchMode.SUBSELECT)
private Atos atos;
}
But in some cases I dont have association or in my table IndicadorAtos have one code, that don`t existis in table Atos
this is my tables:
create table IndicadorAtos (
codAtoPraticado integer primary key,
ato varchar(250),
cod_ato integer
);
create table Atos(
cod_ato integer primary key.
name varchar(250)
)
I try to create this join:
Select t FROM IndicadorAtos t , Atos a where t.cod_ato = a.cod_ato, but I need to return all records from my IndicadorAtos, and with this select he only return all itens that have one item in Atos.
tks
It`s possible to create one map with hibernate #ManyToOne
Yes; it is called unidirectional relationship.
If I understood your question properly, you want to select all entries from IndicadorAtos with possibly associated entries from Atos. You can achieve this by using left join as follows:
SELECT t FROM IndicadorAtos t LEFT JOIN t.atos at
provided that you have an entity Atos defined like:
#Entity
public class Atos {
#Id #GeneratedValue
private int cod_ato;
private String name;
// getters and setters
}
I have a problem very similar to this: How do I join tables on non-primary key columns in secondary tables?
But I'm not sure if I can apply the same solution.
I have two tables like these:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID INTEGER NOT NULL,
DETAIL_ID INTEGER NOT NULL,
PRIMARY KEY( CUSTOMER_ID ),
CONSTRAINT cust_fk FOREIGN KEY( DETAIL_ID ) REFERENCES DETAILS( DETAIL_ID )
)
CREATE TABLE DETAILS
(
DETAIL_ID INTEGER NOT NULL,
OTHER INTEGER NOT NULL,
PRIMARY KEY( DETAIL_ID )
)
I'd like to map these tables to a single class called Customer, so I have:
#Entity
#Table(name = "CUSTOMERS")
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID"))
public class Customer {
#Id
#GeneratedValue
#Column(name = "CUSTOMER_ID")
private Integer id;
#Column(table = "DETAILS", name = "OTHER")
private Integer notes;
// ...
}
but this works only if DETAIL_ID matches CUSTOMER_ID in the primary table.
So my question is: how can i use a foreign-key field in my primary table to join on the primary-key of the secondary table?
UPDATE
I tried to set:
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID", referencedColumnName="DETAIL_ID"))
but when I run the application I get this exception:
org.hibernate.MappingException: Unable to find column with logical name: DETAIL_ID in org.hibernate.mapping.Table(CUSTOMERS) and its related supertables and secondary tables
For anyone looking for an answer to this, using #SecondaryTable is not the way to join two tables with non-primary key columns, because Hibernate will try to assosiate the two tables by their primary keys by default; you have to use #OneToMany review http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/ for a solution, here's a code snippet in case that url stops working:
Customer Class:
#Entity
#Table(name="CUSTOMERS")
public class Customer {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Integer id;
#ManyToOne
#JoinColumn(name="DETAIL_ID")
private Details details;
// Getter and Setter methods...
}
Details Class:
#Entity
#Table(name="DETAILS")
public class Details {
#Id
#GeneratedValue
#Column(name="DETAIL_ID")
private int detailId;
#Column(name="OTHER")
private String other;
#OneToMany(mappedBy="details")
private Set<Customer> customers;
// Getter and Setter methods...
}
This is easily accessible through hibernate with the following code:
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("select id, details.other from Customer");
I hope this helps anyone out there spending hours searching for a way to achieve this like I did.
You can use the referenceColumnName attribute of the #PrimaryKeyJoinColumn annotation to define the join column to the referenced table. In fact, by combining use of name/referencedColumnName you can join on arbitrary on both sides, with the constraint that if duplicates are found your ORM provider will throw an exception.
I thought I know how to use JOIN in JPQL but apparently not. Can anyone help me?
select b.fname, b.lname from Users b JOIN Groups c where c.groupName = :groupName
This give me Exception
org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing the query
Internal Exception: org.eclipse.persistence.internal.libraries.antlr.runtime.EarlyExitException
Users have a OneToMany relationship with Groups.
Users.java
#Entity
public class Users implements Serializable{
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
List<Groups> groups = null;
}
Groups.java
#Entity
public class Groups implements Serializable {
#ManyToOne
#JoinColumn(name="USERID")
private Users user;
}
My second question is let say this query return a unique result, then if I do
String temp = (String) em.createNamedQuery("***")
.setParameter("groupName", groupName)
.getSingleResult();
*** represent the query name above. So does fname and lname concatenated together inside temp or I get a List<String> back?
Join on one-to-many relation in JPQL looks as follows:
select b.fname, b.lname from Users b JOIN b.groups c where c.groupName = :groupName
When several properties are specified in select clause, result is returned as Object[]:
Object[] temp = (Object[]) em.createNamedQuery("...")
.setParameter("groupName", groupName)
.getSingleResult();
String fname = (String) temp[0];
String lname = (String) temp[1];
By the way, why your entities are named in plural form, it's confusing. If you want to have table names in plural, you may use #Table to specify the table name for the entity explicitly, so it doesn't interfere with reserved words:
#Entity #Table(name = "Users")
public class User implements Serializable { ... }