I'm trying to learn JPA/Hibernate and I'm real green in this field. I tend to usually veer off and try things without knowing much about the API. So I decided to create a simple entity that retrieves information from multiple tables see this is easily implementable with JPA. The reason behind this is, if, hypothetically, the involving tables each has a few hundred columns and we only have a business need to retrieve a very few data, and we only need to focus on retrieval rather than inserts/updates/deletions, I would assume it is best to only retrieve the entire entity (specially if multiple rows need to be returned) then join them across other entities to derive a few columns.
I started up with two simple tables:
EMPLOYEES
EMPLOYEE_ID, EMAIL, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, etc...
DEPARTMENTS
DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, etc...
I want my entity to retrieve only the following columns solely based on EMPLOYEES.EMPLOYEE_ID:
EMPLOYEES.EMPLOYEE_ID
EMPLOYEES.MANAGER_ID
EMPLOYEES.DEPARTMENT_ID
DEPARTMENT.DEPARTMENT_NAME
One thing to notice here is that EMPLOYEES.MANAGER_ID is a self-referencing foreign key to EMPLOYEES.EMPLOYEE_ID.
I might create the following...
#SecondaryTable(name="DEPARTMENTS",
pkJoinColumns=#PrimaryKeyJoinColumn(name="managerId",referencedColumnName="employeeId")
)
#Table(name="EMPLOYEES")
#Entity
public class EmployeesDepartment {
#Id
private String employeeId;
private String managerId;
private String email;
private int departmentId;
#Column(name="DEPARTMENT_NAME",table="DEPARTMENTS")
private String departmentDesc;
// Setters and getters
}
Obviously this doesn't give us the correct answer due to the fact that the join between the secondary table (DEPARTMENTS) occurs between its MANAGER_ID and EMPLOYEES.EMPLOYEE_ID, rather than DEPARTMENTS.MANAGER_ID = EMPLOYEES.MANAGER_ID.
I cannot replace referencedColumnName="employeeId" with referencedColumnName="managerId" as managerId of #Entity EmployeesDepartment is not a primary key of EMPLOYEES.
And I can't do the following:
#OneToOne
#JoinColumn(name="managerId",table="DEPARTMENTS",referencedColumnName="employeeId")
private String managerId;
My question is, how can I make my join to be on DEPARTMENTS.MANAGER_ID = EMPLOYEES.MANAGER_ID while the WHERE clause of the query is based on EMPLOYEES.EMPLOYEE.ID? In other word, how can I have the entity that is mapped to the following query:
SELECT
E.EMPLOYEE_ID,
E.MANAGER_ID,
E.DEPARTMENT_ID,
D.DEPARTMENT_NAME
FROM EMPLOYEES E LEFT OUTER JOIN DEPARTMENTS D ON E.MANAGER_ID = D.MANAGER_ID
WHERE E.EMPLOYEE_ID = ?
Or are there better solution with less side effects, e.g. order of updates of tables, loading, etc.?
Related
I have 2 models, a workout and an exercise as follows:
#Entity
public class Workout extends Model {
#Id
public Integer id;
#ManyToMany(cascade = CascadeType.REMOVE)
public List<Exercises> exercises;
}
#Entity
public class Exercise extends Model {
#Id
public Integer id;
#ManyToMany
public Workout workout;
}
When I load a workout and attempt to display it, I want the exercises to be displayed in the order that the relationship (between the workout and exercise) was created. However, the exercises are instead displayed in the order that the exercises were created. Here's a sample of the display (in case it helps):
<ul>
#workout.exercises.map { exercise =>
<li>
#exercise.name
</li>
}
</ul>
Any ideas on how I can achieve this? I've tried adding #OrderBy to the property definition, but this doesn't allow me to order by the relationship table fields, which would be ideal (with the addition of a created_date field on that table, as well).
Much appreciated!
There is no easy way as far as I can tell. Just create a getSortedExcercises() which returns the list sorted to use in the templates:
public List<Exercises> getSortedExcercises() {
List<Exercises> l = new ArrayList(this. exercises);
Collections.sort(l, new ExercisesComparator());
return l;
}
In terms of SQL you need an order by clause to guarantee the order of the returned rows. That is the behaviour you are seeing of '... displayed in the order that the exercises were created.' ... is actual specific to your DB's convention (and not portable across DB's).
If you want SQL result ordering by 'order that the relationship (between the workout and exercise) was created' ... then what that strictly means is that you need a db column to store that on the intersection table and have the SQL order by that column.
Now, with #ManyToMany by default the generated intersection table does not have a 'When Created' column. You can either model the intersection table explicitly (change from #ManyToMany to use 2 #OneToMany etc) or manually define your intersection table with a 'When Created' column and DB triggers to populate that ... and then reference that db column in an order by clause.
Hello all and sorry for my English =)
I works with Hibernate using criteria API. Everything was fine, but I have several denormalized tables with data for reports, and I faced some troubles.
For one of that tables I created #Entity class for mapping like
#Entity
#Table(name= "table")
public class Report {
#Id
Integer id;
Integer product_id;
Integer warehouse_id;
String some_data;
}
with simple queries all works fine. But I need to make queries like pivot table or self join etc.
for example
select
t1.product_id,
t2.warehouse_id
from repost t1
join report t2
on t1.product_id = t2.product_id
and t1.warehoise_id = ?
where t1.some_data in (?)
Such query does not contain logical dependencies between entities like Primary_Key - Foreign_Key and can return custom object data (it can be Map<>, List<>, Pair<>, Long...)
Is it possible to make query like this without using HQL?
Thanks
ADDED
As I was understood it's not possible using HQL too
The Hibernate documentation states the following:
5.1.6.5. Mapping one entity to several tables
While not recommended for a fresh schema, some legacy databases force
your[sic] to map a single entity on several tables.
I have done a lot of reading on database normalization, and I don't really understand how you can both normalize a database and NOT map an entity on several database tables, unless you map several different entities and do a weird join statement manually. Or, you map 7 different entities and transfer them to one POJO. Is there somewhere that shows how to develop a "fresh schema" that is both normalized and takes a single entity to turn from database data to a java object?
Also, I understand that complete normalization isn't necessary in today's world of cheap storage, but I just found this statement to be at odds with everything else I've read. I am looking for a balance of normalized to de-normalized, but haven't found a simple way with the the Java Persistence API.
EDIT:
Example:
If I have a user Entity with the following:
#Entity
public class User {
long id;
String name;
String email;
int countryCode;
List<Images> uploadedImages;
}
I am not going to store the user's country name and collection of images on the same table, rather I will have the country on one table:
Table Countries Country_Code Country_Name
AF Afghanistan etc....
with country code and country name, then on , and uploaded images on a separate table with user id's,
Table UploadedImages User_Id Image_Name Image_Url
1 Hello.jpg Amazon S3
1 Goodbye.jpg Photobucket
So how do I do this with only one entity? Or do I get three separate entities from the database using information from the first entity? As you can tell, I'm a bit confused on the basic schema, How would I translate the data above to a java object?
Given your example
#Entity
public class User {
long id;
String name;
String email;
int countryCode;
List<Images> uploadedImages;
}
Here you have 3 entities - a User, a Country and an Image entity with each one probably mapping to 3 tables - one entity per table and a join table for the list of images.
So your User class then becomes
#Entity
public class User {
long id;
String name;
String email;
Country country
List<Images> uploadedImages;
}
#Entity
public class Country {
long id;
String name;
}
#Entity
public class Image{
long id;
String name;
String url;
}
And you'd add annotations to map the properties to the correct tables and columns.
What the documentation says has nothing to do with data normalization. This has to do with data partition/table optimization. So, if you have a table with a column that holds a big binary, you probably don't want that to be part of the same table as the main data. This was more common in the past, as data considered small by today's measures were considered "big" back then.
Normalization, on the other hand, is very welcome in OOP (and Hibernate). It's all about keeping things on their places, to minimize redundancy and dependency
I would like to write a JPA entity class that has a one to many relationship. I would like to avoid defining the relationship in the parent class to avoid the data to be loaded every time a reference is made for the object even when there is no need for the associated data. I have read that even with Lazy loading, the data can be loaded so i need to avoid that.
In the following example,
Customer table
------------------------
customerid, customerName
1 John
2 Bob
Order Table - customerId is a foreign key to Customer
orderId, customerId, orderDate
1 1 12MAY2012
1 1 13MAY2012
1 2 16MAY2012
JPA Entities
#Entity
public class Customer {
// all Customer-related fields WITHOUT #OneToMany relationship with Order
}
#Entity
public class Order {
String orderDate;
#ManyToOne
private Customer owner;
}
How would i retrieve data from the customer table based on a condition on the order table? For example, how can i say "Get me all customers who have made an order where the orderDate is between two dates?
using hbm.xml there are query only properties to achieve what you want
http://ayende.com/blog/4054/nhibernate-query-only-properties
Is there a way I can map a field in an hibernate object to be loaded with a table query?
As an example lets say Table_Message has fields id(int),message_key(varchar),message_content(Clob),language(varchar). This table will hold messages in different languages(locale).
And another table thats mapped to an entity using hibernate. Comments with fields id(int),comment_message_id(varchar),created_date(datetime). comment_message_id refers to Table_Message's message_key column.
EDIT: Table_Message is NOT a mapped Entity in hibernate
Assuming my comment class is
public class Comment
{
int id;
String message;
Date createdDate;
}
Is there a way to tell hibernate to load message by joining Comment table and Table_Message table by message_key with a default locale (for example 'en').
Basically is there a way to tell hibernate to load a field by running a specific query? And if so what is that way?
I know about writing a Custom SQL query for loading the entity. But since I'm using XDoclet there doesn't seem to be a way to do that. Also it will be very convenient if there's a way to do that for a single field.
I guess ResultTransformer may help you in this. Please check
http://docs.jboss.org/hibernate/orm/3.3/api/org/hibernate/transform/ResultTransformer.html
http://stackoverflow.com/questions/6423948/resulttransformer-in-hibernate-return-null
You must join the tables by comment_message_id with message_key and further filter the result by language. I assume the message_key is unique.
As a side notice: you should use integer keys to have better performance.
You can try to write a database view in SQL and create an entity to opaque the view:
CREATE VIEW Comment_Table_Message AS
SELECT c.id, c.comment_message_id, c.created_date, m.id AS mid,
m.message_content, m.language
FROM Comment c, Table_Message m
WHERE c.comment_message_id = t.message_key;
Now you can create an entity CommentTableMessage and use JPQL to filter results by language:
SELECT x FROM CommentTableMessage x WHERE x.language=?1
If Table_Message was a Hibernate entity you would write (in JPA terms):
#Entity
public class Comment
{
int id;
#ManyToOne()
#JoinColumn(name="comment_message_id")
TableMessage tableMessage;
String message;
Date createdDate;
}
#Entity
public class TableMessage {
int id;
#Id
String messageKey;
bytes[] messageContent; //I don't know how you want to deal with Blobs?
String language;
}
Having that you can write a simple JPA Query: (Can you use JPA ? - next assumption)
SELECT c FROM Comment c WHERE c.tableMessage.language=?1