Join non-unique column in spring jpa - java

I was hopping to find an answer to my probleme on this here forum. My problem is as follows, I have two classes :
#Entity
#Table(name = "a")
public class A implements Serializable{
#Id
private String id = UUID.randomUUID().toString();
#Column(name = "REFERENCE_ID")
private String referenceId;
#Column(name = "VERSION")
private String version;
}
And
#Entity
#Table(name = "b")
public class B{
#Id
private String id = UUID.randomUUID().toString();
#Column(name = "REFERENCE")
private String reference;
#ManyToMany(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value =
"(select r from A r where r.reference_id = reference_id order by r.version desc limit 1)",
referencedColumnName = "reference_id")),
#JoinColumnOrFormula(column = #JoinColumn(name = "reference_id",
referencedColumnName = "reference_id", insertable = false))
})
private A referenceId;
}
The thing is reference_id is not a unique key in the b table and was just an indicative value in table A so in order to fetch the entire correspondent row I had to do some filtering with the formula in my join annotation.
When I try to fetch my data I get the following error
[Request processing failed; nested exception is
org.springframework.dao.InvalidDataAccessResourceUsageException:
could not extract ResultSet; SQL [n/a]; nested exception is
org.hibernate.exception.SQLGrammarException: could not extract
ResultSet] with root cause org.postgresql.util.PSQLException:
ERROR: relation "a" does not exist Position : 309
EDIT
ACtually t works as intended when changing my join formula to
#JoinFormula(value =
"(select r from schema_A r where r.reference_id = reference_id order by r.version desc limit 1)",
referencedColumnName = "reference_id"))
the problem now is that the code is intended to work on multipple envirnments
as for my application.yml it looks a bit like this;
jpa:
database: POSTGRESQL
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
default_schema: schema
jdbc:
lob:
non_contextual_creation: true
time_zone: UTC
Thanks for your responses :)

I agree with Simon's comment. For Postgres (and relational databases in general), the word "table" and "relation" are the same and where the term "relational" comes from. So, when it says "Can't find relation B" it literally means "Can't find a table called B".
You should check your connection settings for the schema to see if those tables have/haven't been defined. If it's not obvious, maybe add/edit the question accordingly with your connection settings & appropriate debugging showing you DO see the relations (tables) there.

Related

Hibernate and Criteria Api generates wrong Join condition

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

Fetch join causes N+1 queries or throws org.hibernate.QueryException

I am trying to fetch with one query list of objects and its associations, unfortuantely, either I cause N+1 requests to database, or get hit with exception "org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list".
Please let me walk you through my case.
Below is my Data Model:
#Table(name = "first_table")
public class FirstObject {
#Id
#Column(nullable = false, name = "first_id")
private Long id;
#Column(nullable = false, name = "first_param")
private String param1;
#ManyToOne
#JoinColumn(nullable = false, name = "second_id")
private SecondObject second;
...other columns...
}
#Table(name = "second_table")
public class SecondObject {
#Id
#Column(nullable = false, name = "second_id")
private Long id;
#Column(nullable = false, name = "second_param")
private Long param2;
#ManyToOne
#JoinColumn(nullable = false, name = "third_id")
private ThirdObject third;
...other columns...
}
#Table(name = "third_table")
public class ThirdObject {
#Id
#Column(nullable = false, name = "third_id")
private Long id;
...other columns...
}
It is true to database relations, also exactly how I want it on FE.
All I am trying to achieve is to fetch all the associations with one query, giving 2 conditions:
ConditionBuilder condition = new ConditionBuilder()
.and(FirstObject.second.param2.eq(some_number))
.and(FirstObject.param1.eq(some_string));
return from(FirstObject)
.join(FirstObject.second).fetchJoin()
.join(FirstObject.second.third).fetchJoin()
.where(condition.generate())
.fetch();
Unfortunately this code throws exception:
org.hibernate.QueryException: query specified join fetching, but the
owner of the fetched association was not present in the select list
I can make it work, but with N+1 queries, but it is acceptable only for development phase, as will cause performance issue.
...
.join(FirstObject.second).fetchJoin()
.join(FirstObject.second.third)
...
same here:
...
.join(FirstObject.second)
.join(FirstObject.second.third)
...
What I am trying to figure out is how to make hibernate to create one simple query like that:
select
*
from
first_table table1
inner join
second_table table2
on table1.second_id=table2.second_id
inner join
third_table table3
on table2.third_id=table3.third_id
where
table1.first_param="some_string"
table2.second_param=some_number
All the help is very much appreciated, I've been fighting this for some time now, and really counting on community. Thank you very much.
You should be mapping both sides of the entity relationship:
for instance, in FirstObject you have this:
#ManyToOne
#JoinColumn(nullable = false, name = "second_id")
private SecondObject second;
So in SecondObject you should have this:
#OneToMany(mappedBy = "second") // this is the name of the field in the class that defines the join relationship
Collection<FirstObject> firstObjects;
In ThirdObject you should have this:
#OneToMany(mappedBy = "third") // this is the name of the field in the class that defines the join relationship
Collection<SecondObject> secondObjects;

JPA insertable and updatable with View and SecondaryTable

Thera are oracle view vendor_view and table vendors (vendors table contains only PK with name id for simplicity)
create view vendor_view as
select id as vid, 'YES' as active
from vendors;
Coresponding entity
#Entity
#Table(name = "vendors")
#SecondaryTable(name = "vendor_view", pkJoinColumns = {#PrimaryKeyJoinColumn(name = "vid", referencedColumnName = "id")})
public class Vendor {
#Id
private Long id;
#Column(table = "vendor_view", name = "vid", insertable = false, updatable = false)
private Long vid;
#Column(table = "vendor_view", name = "active", insertable = false, updatable = false)
private String active;
getter and setter....
}
When i try to persist new Vendor entity then face with issue:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [insert into vendor_view (vid) values (?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:238)
.....
Caused by: org.hsqldb.HsqlException: INSERT, UPDATE, DELETE or TRUNCATE not permitted for table or view
at org.hsqldb.error.Error.error(Unknown Source)
JPA Implementation is Hibirnate.
Question is why Hibirnate generate insert query for field that mark as insertable = false, updatable = false ?
As hibernate doesn't know whether the table you are trying to insert is a table or a view until and unless it interacts with the database. So it is runtime exception which can be checked only when Java program interacts with Database.
It is similar to even if table doesn't exist it will make query but on runtime it will throw exception.
You would do that when the responsibility of creating/udpating the related entity in question isn't in the current entity. E.g. you have a Person and an Address. You'd like to add insertable=false, updatable=false to the #OneToMany relationship with the Person entity in the Address entity, simply because it's not the responsibility of the Address entity to create or update a Person. It's the other way round. This is not really a technical, but more a semantic/natural decision.

jpa eclipselink Exception [EclipseLink-4002]

Here is an exception I'm getting:
[EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd):
org.eclipse.persistence.exceptions.DatabaseException Internal Exception:
java.sql.SQLSyntaxErrorException: Vergleiche zwischen 'BIGINT' und 'VARCHAR (UCS_BASIC)'
werden nicht unterstützt.
Error Code: 30000
Call: SELECT t1.ID, t1.TEXTINFO FROM COORDINATESLOCATION_INFORMATION t0, TEXTINFORMATION t1
WHERE ((t0.CoordinatesLocation_ID = ?) AND (t1.ID = t0.informationList_ID))
bind => [1 parameter bound]
When I run my application first (without an empty database) everything works. I easily can manage data in all CRUD functions. Later (after an unspecific time or several requests), the exception appears.
This is a tourist information application. There are authors that create tours. One tour contains many locations. One location contains many information. The associations are realised as compositions.
As I changed every association to eager fetch, the exception appears earlier in the workflow.
Here are the code snippets:
#Entity
public class CoordinatesLocation implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String description;
#OneToMany(fetch = FetchType.EAGER)
private List<Information> informationList = new ArrayList<>();
private double lat;
private double lng;
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Information implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
#Entity
public class TextInformation extends Information {
private String textInfo;
}
Plus getter & setter!
Thanks for helping!
With the code you have provided, I can recreate the issue in EclipseLink . The generated script is;
CREATE TABLE COORDINATESLOCATION_INFORMATION (CoordinatesLocation_ID BIGINT
NOT NULL, informationList_ID VARCHAR(255) NOT NULL, PRIMARY KEY
(CoordinatesLocation_ID, informationList_ID))
I have tried explicitly defining the columns of the join table;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable (name = "COORDINATESLOCATION_INFORMATION",
joinColumns = #JoinColumn(name = "CoordinatesLocation_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "Information_ID",referencedColumnName = "ID" ))
private List<Information> informationList;
….but still get VARCHAR(255).
I have removed the
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
From the Information class (so defaulting to SINGLE_TABLE strategy) and this leads to the desired script:
CREATE TABLE COORDINATESLOCATION_INFORMATION (CoordinatesLocation_ID BIGINT NOT
NULL, informationList_ID BIGINT NOT NULL, PRIMARY KEY (CoordinatesLocation_ID,
informationList_ID)).
So, following that somewhat empirical analysis (I’m not sure why ID was mapped to type VARCHAR(255)) you have a couple of choices.
Modify you CREATE TABLE COORDINATESLOCATION_INFORMATION script to include informationList_ID BIGINT.
Change to #Inheritance(strategy = InheritanceType. SINGLE_TABLE). This requires the addition of a discriminator column and may not be in line with your approach.
Note also that with #OneToMany you do not need a join table if you add a Foreign Key in your Information table and define #ManyToOne on the Information side and #OneToMany on the CoordinatesLocation side with the mappedBy Attribute.
This link will give some idea of that. https://en.wikibooks.org/wiki/Java_Persistence
I can't read the localized message in your exception, however I'll guess that it says you can't compare bigint and varchar. I'll further guess that the error is in this part of the SQL: (t1.ID = t0.informationList_ID). Is one of those columns a bigint and the other a varchar?
basically here t0.CoordinatesLocation_ID = ? at sometime your parameter is of type BIGINT and the reqested type is VARCHAR, you should look at this

Select DISTINCT values form #ElementCollection List<String> in #NamedQuery

For a project I'm trying to lookup all distinct categories within a List #ElementCollection field. Each foo instance has one or more String categories assigned. The code below does not work as JBOSS/Hibernate throws an exception when deploying the ear to the server:
Error in named query: Foo.listUniqueCategories: org.hibernate.QueryException: not an entity [SELECT DISTINCT f.categories FROM com.Foo f]
I have the class:
#Entity(name = "Foo")
#NamedQuery(name = "Foo.listUniqueCategories", query = "SELECT DISTINCT f.categories FROM Foo f")
public class FooEntity
{
#Id()
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
protected Long id;
#ElementCollection
#CollectionTable(name = "categories", joinColumns = #JoinColumn(name = "foo_id"))
private List<String> categories;
...
}
Is there anything wrong with the select distinct? Is it even supported to perform a 'SELECT DISTINCT' on an #EllementCollection?
Any help is appreciated!
Richard
You are confused between HQL and SQL .. replace your named query with the following. Named Queries are always HQL.
SELECT distinct f.categories FROM FooEntity f
But, I'm not sure if this will work. If you need to find out the distinct categories, why query on FooEntity? Why not create an entity for Categories and run a query like below. Also, you are calling join column on a list which is wrong as well ,it should join on entity types like below
#ElementCollection
#CollectionTable(name = "categories", joinColumns = #JoinColumn(name = "foo_id"))
private List<**Category**> categories; // replace String with Category
--
select distinct category.name from Category c
Where Category is a new entity that you have to create.

Categories