Recently i had to use an outer join between two JPA entities that doesnt have object relational mapping. Going by the spec and the forum posts, outer joins are supported only if the entities are mapped at JPA level.
Example code below. Requirement is to find customers without any orders.
#Entity
class Customer {
private int id;
}
#Entity
class Order {
private int customerId;
public int getCustomerId() { return customerId; }
public void setCustomerId(int customerId) { this.customerId = customerId ; }
}
In my case, i had to opt for the native queries to get the job done.
Any thoughts on if future JPA specs going to support outer joins without relational mapping ?
Thanks
Rakesh
You can use a theta-style join to emulate an INNER JOIN:
select c, o
from Customer c, Order o
where c.id= o.customerId
Most modern database engine query optimizers will turn it into an INNER JOIN equivalent anyway.
Suppose you have customerId field (Integer) in the Order entity. In order to find customers without any orders, you can avoid outer join and native query by using subquery:
select c from Customer c where id not in (select customerId from Order)
This way you achieve the same goal in JPQL (HQL).
Related
I have a simple Restaurant Entity that has a List of Votes ( some fields and methods are omitted for brevity ).
public class Restaurant extends AbstractNamedEntity {
#OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
#Fetch(FetchMode.SUBSELECT)
private List<Vote> votes;
}
Here is another side of the relationship
public class Vote extends AbstractBaseEntity {
#ManyToOne
#JsonBackReference
private Restaurant restaurant;
}
When I try to fetch data using Spring data JPA findAll() method and then convert it to DTOs
public static RestaurantResponseDTO toRestaurantDto(Restaurant restaurant) {
return new RestaurantResponseDTO(restaurant.getId(), restaurant.getName(),
restaurant.getAddress(), getRestaurantVoteCount(restaurant));
}
public static long getRestaurantVoteCount(Restaurant restaurant) {
var votes = restaurant.getVotes();
if (votes == null) return 0;
return votes.stream().filter(vote -> vote.getVoteDate().equals(LocalDate.now())).count();
}
these are the SQLs I have:
Hibernate:
select
restaurant0_.id as id1_1_,
restaurant0_.name as name2_1_,
restaurant0_.address as address3_1_
from
restaurant restaurant0_
Hibernate:
select
votes0_.restaurant_id as restaura3_4_1_,
votes0_.id as id1_4_1_,
votes0_.id as id1_4_0_,
votes0_.restaurant_id as restaura3_4_0_,
votes0_.user_id as user_id4_4_0_,
votes0_.vote_date as vote_dat2_4_0_
from
vote votes0_
where
votes0_.restaurant_id in (
select
restaurant0_.id
from
restaurant restaurant0_
)
I thought that Subselect needs only 1, why do I have 2?
Actually, what you see is expected hibernate behavior. See this section of the hibernate documentation.
Hibernate is going to avoid the N+1 query issue by generating a single SQL statement to initialize all votes collections for all Restaurant entities that were previously fetched. Instead of using passing all entity identifiers, Hibernate simply reruns the previous query that fetched the Restaurant entities.
P.S. As for the FetchMode.JOIN option, please note that as it's mentioned here:
The reason why we are not using a JPQL query to fetch multiple Department entities is because the FetchMode.JOIN strategy would be overridden by the query fetching directive.
To fetch multiple relationships with a JPQL query, the JOIN FETCH directive must be used instead.
So, I guess the most flexible way for your case would be to write own JPQL query and use JOIN FETCH directive where it's needed.
I'm trying to learn how to create an object oriented model database using a tool called: Eyedb (on linux). I've made some classes and records generators for this database but I don't know how to code queries for it using Java.
The structure of the database looks like this:
class Lecturer {
attribute string<32> name;
attribute strin<32> surname;
relationship set <Lecture*> lectures inverse Lecture::Lecturer;
};
class Item{
attribute string<32> name;
attribute string<512> description;
};
class Topic extends Item {
relationship set<Lecture*> tlecture inverse Lecture::Topic;
};
class Lecture {
relationship Lecturer *llecturer inverse Lecturer::lectures;
relationship Topic *ltopic inverse Topic::tlecture;
relationship set <Content*> lcontent inverse Content::Lecture;
relationship set <Equipment*> lequipment inverse Content::Lecture;
relationship Room_timeslot* lroom_timeslot inverse Room_timeslot::rlecture;
};
class Content extends Item {
attribute int level;
relationship set <Content*> subcontent inverse Content::supcontent;
relationship set <Content*> supcontent inverse Content::subcontent;
relationship set <Teachingmaterial*> cteachingmaterial inverse Teachingmaterial::Content;
};
class Teachingmaterial extends Item {
attribute string link;
relationship set <Content*> tcontent inverse Content::Teachingmaterial;
};
class Equipment extends Item {
attribute int quantity;
attribute string symbol;
relationship set<Lecture*> electure inverse Lecture::Equipment;
relationship Room *eroom inverse Room::requipment;
};
class Room {
relationship set <Equipment*> requipment inverse Equipment::Room
attribute string name;
attribute int number;
attribute string symbol;
attribute string building;
attribute int floor;
attribute string wing;
relationship set <Room_timeslot*> rroom_timeslot inverse Room_timeslot::Room;
};
class Room_timeslot {
relationship Room *room inverse room::rroom_timeslot;
relationship Lecture* rlecture inverse Lecture::Room_timeslot;
relationship set <Timeslot*> rtimeslot inverse Timeslot::lroom_timeslot;
};
class Timeslot {
attribute string name;
attribute int number;
attribute time timemargin_start;
attribute time timemargin_end;
relationship set <Room_timeslot*> troom_timeslot inverse Room_timeslot::rtimeslot;
};
I want to make queries that look like this:
SELECT lr.name as lecturer, t.name as topic FROM topic t
JOIN lecture l ON t.id_topic = l.id_topic
JOIN lecturer lr ON lr.id_lecturer = l.id_lecturer
ORDER BY lr.name
select t.name as topic from topic t
join lecture l on t.id_topic = l.id_topic
join lecturer lr on lr.id_lecturer = l.id_lecturer
where lr.name = "name1"
SELECT tm.name as teaching_material, e.name as equipment, lr.name as lecturer FROM teachingmaterial tm
JOIN teaching_content tc ON tc.id_teachingmaterial = tm.id_teachingmaterial
JOIN content c ON c.id_content = tc.id_content
JOIN content_lecture cl ON cl.id_content = c.id_content
JOIN lecture l ON l.id_lecture = cl.id_lecture
JOIN lecture_equipment le ON le.id_lecture = l.id_lecture
JOIN equipment e ON e.id_equipment = le.id_equipment
JOIN lecturer lr ON lr.id_lecturer = l.id_lecturer
Java uses JDBC to communicate with a relational database. If your database has a JDBC driver you're all set. If not, you're out of luck. Both SQL Server and MySQL have JDBC drivers, so they should not be a problem. You'll need those and the JDBC tutorial.
Looks like EyeDB is an object database that I've never heard of. It claims to be usable by C++ and Java.
I'd expect at least one "hello world" connection example. I'm suspicious of any software that doesn't even get the HTML on its web page right.
There's Java documentation available. Looks awful. Is this a requirement? There are better databases, both relational (MySQL, SQLLite, or PostgreSQL), object (e.g. JODB), or graph (NEO4J). Must you use this one?
The EyeDB documentation makes the steps clear. They are spelled out for you in the sample problem:
Define the schema using the EyeDB Object Definition Language (ODL)
Generate the Java classes from the schema using the EyeDB eyedbodl tool, which comes with your download.
Write a client program using the generated classes that interact with the database.
It's all there. Give it a try.
I have two tables in my PostgreSQL database:
CREATE TABLE tableOne (id int, name varchar(10), address varchar(20))
CREATE TABLE tableTwo (id int, info text, addresses varchar(20)[])
now I want to create a join as follows:
SELECT * FROM tableOne JOIN tableTwo ON address = ANY(addresses)
I tried to achieve this using Hibernate - class TableOne:
#Entity
#Table(name = "tableOne")
class TableOne {
private int id;
private TableTwo tableTwo;
private String address;
#Id
#Column(name = "id")
public getId() { return id; }
#ManyToOne
#JoinFormula(value = "address = any(tableTwo.addresses)",
referencedColumnName = "addresses")
public TableTwo getTableTwo(){
return tableTwo;
}
// Setters follow here
}
But Hibernate keeps generating queries with non-sense JOIN clauses, like:
... JOIN tableTwo ON _this.address = any(tableTwo.addresses) = tableTwo.addresses
How do I tell Hibernate using annotations to format my join query correctly? Unfortunately, our project must be restricted only to the Criteria API.
EDIT:
After suggestion from ashokhein in the comments below, I annotated the method getTableTwo() with just #ManyToOne - and now I would like to do the join using Criteria API, presumably with createAlias(associationPath,alias,joinType,withClause) method where withClause would be my ON clause in the join.
But Im not sure what to put as associationPath and alias parameters.
Any hints?
To support PostgreSQL array you need a custom Hibernate Type. Having a dedicated user type will allow you to run native SQL queries to make use of the type:
String[] values = ...
Type arrayType = new CustomType(new ArrayUserType());
query.setParameter("value", values, arrayType);
HQL supports ANY/SOME syntax but only for sub-queries. In your case you'll need a native query to use the PostgreSQL specific ANY clause against array values.
You can try Named Query.
#NamedQuery(name="JOINQUERY", query="SELECT one FROM tableOne one JOIN tableTwo two ON one.address = :address" )
#Entity
class TableOne{......
Retrieving part is:
TypedQuery<TableOne> q = em.createNamedQuery("query", TableOne.class);
q.setParameter("address", "Mumbai");
for (TableOne t : q.getResultList())
System.out.println(t.address);
You might need to do some permutations on the query
So after a lot of time searching for the right answer, the only real solution that works for us is creating a view:
CREATE VIEW TableA_view AS SELECT TableOne.*,TableTwo.id FROM TableA JOIN TableTwo ON TableOne.address = ANY(TableTwo.addresses)
and mapping it to an entity TableOne instead of the original table.
This was the only solution for us besides, of course, using a named query, which was a no-go as we needed to stick to the Criteria API.
As #ericbn has mentioned in the comments this is really an example where ORM gets really annoying. I would never expect that custom join clause like this is not possible to do in Hibernate.
#JoinFormula should contain SQL instead of HQL.
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/JoinFormula.html
How can I join two tables by using java play framework and jpa, I really have a hardtime converting my MySQL query to jpa query.
Here is the MySQL query that I used in my old Java code:
SELECT * FROM tbl_majors
INNER JOIN tbl_lookup_user_major
ON tbl_majors.id=tbl_lookup_user_major.majorId
WHERE tbl_lookup_user_major.userId=12
//Table 1:
#Entity
#Table(name="tbl_majors")
public class Major extends Model {
public Major(){
}
#Column(name="major_name")
private String name;
#Column(name="major_desc")
private String description;
}
//Table 2
#Entity
#Table(name="tbl_lookup_user_major")
public class LookupUserMajor extends Model {
public LookupUserMajor(){
}
private int majorId;
private int userId;
}
Dont know if I get the exact point here, but in the tutorial blog "YABE", this kind of join table is used and created automatically by Play :
http://www.playframework.org/documentation/1.2.4/guide6#tagging
The many-to-many relation is described in the Model (between "Post" and "Tag" here for the blog sample) :
#ManyToMany(cascade=CascadeType.PERSIST)
public Set<Tag> tags;
public Post(User author, String title, String content) {
...
this.tags = new TreeSet<Tag>();
...
this.title = title;
this.content = content;
...
}
The YAML for the Posts data is :
Post(jeffPost):
title: The MVC application
postedAt: 2009-06-06
author: jeff
tags:
- play
- architecture
- mvc
After running the app, I check the database and the table "post_tag" is automatically created and all the links between the two tables are done (post_ids and tags_ids are filled).
Retrieving data seems as easy as :
"select distinct p from Post p join p.tags as t"
Can someone confirm that ? Because new to Java and JPA and Play ^^
If this is correct, it looks easier than managing the join table "manually".
Every time you have a field names "xxxId" in an entity, and "xxxId" is the ID of another entity, you did something wrong. The point of JPA is to manipulate objects, and associations between objects using object references or object collections.
Your tbl_lookup_user_major looks like a join table to me. Such a join table means that you have a many-to-many (or one-to-many, is one of the IDs is unique) between Major and User. So, your Major entity should have the following field :
#ManyToMany
#JoinTable(...) // details omitted
private Set<User> users;
And your JPA query should look like
select m from Major m
inner join m.users user
where user.id = :userId
Example Jpa query try like this...
Query query = JPA.em().createQuery(" SELECT * FROM "+User.class.getName() +" AS a JOIN "+
Role.class.getName()+" AS b WHERE a.roleId=b.roleId ");
My desired query is to get a list of Course objects that belong to a Category. My objects are as follows:
public class Course{
String name;
List<Category> categories;
}
public class Category{
String name;
Category parent;
}
Since the categories reference each other, they can have an infinite depth:
A
A.A
A.A.A
A.A.B
A.B
A.B.A
B
B.A
B.B
C
How can I query for courses within the category "A.A", and return all Courses associated with A.A, A.A.A, and A.A.B?
If you are willing to use native SQL and your database supports recursive common table expressions (basically all major DBMS except MySQL) it's pretty easy:
WITH RECURSIVE course_tree (name) AS (
SELECT name
FROM course
WHERE name = 'A.A'
UNION ALL
SELECT name
FROM course
WHERE parent_id = course_tree.id
)
SELECT *
FROM course_tree
Because you do not know how deep is the tree, you can use some kind of pattern as follows
select distinct
c
from
Course c
left join fetch
c.categories c
where
c.name like 'A.A%'