Mapping in Hibernate on another field than the PK - java

I'm stuck for a moment now and I need some help.
I wanna have a mapping in hibernate between 2 fields that are not PK in any of the both Tables
Table Category (
catId Numeric(10),
categoryBusinessRef Numeric(10)
)
Table Product (
productId Numeric(10),
categoryBusinessRef Numeric(10)
)
The Sql query would be :
SELECT *
from Category as a
Join Product as b on a.categoryBusinessRef = b.categoryBusinessRef
But in Hibernate it make me this mapping
SELECT *
from Category as a
Join Product as b on a.categoryId = b.categoryBusinessRef
My hbms looks likes
<class name="Category" table="A">
<id name="categoryId" length="10">
<column name="categoryid" />
<generator class="sequence">
<param name="sequence">S_category</param>
</generator>
</id>
<set name="categoryBusinessRefs" table="B" >
<key>
<column name="categoryBusinessRef" />
</key>
<one-to-many class="ProductClass" />
</set>
</class>
<class name="Product" table="B">
<id name="productId" length="10">
<column name="productid" />
<generator class="sequence">
<param name="sequence">S_product</param>
</generator>
</id>
<property name="categoryBusinessRef" length="10">
<column name="categoryBusinessRef" />
</property>
</class>
So it's a One-to-many relation but It have to map with another values as the PK of Category
Thanks for the help
EDIT :
If I can't do that ok ! But If I can I want to do that, I know all the stuff with the primary key but my question is not "Mapping in Hibernate on PK" but "Mapping in Hibernate on another field than the PK", so the answers who said map to the PK doesn't interest me :p

Your model is wrong, unless you want to have a cartesian product (it is a rare requirement) you should always join by the primary key.
You can have one-to-one, one-to-many or many-to-many relationships (the last two are more common).
In one-to-many you need the pk of the one side in the many side.
In many-to-may you need a relationship table that makes the mapping.
Assuming that you want you want that many B's connect to the same A (so one-to-many).
CREATE TABLE a (
id NUMERIC(10) PRIMARY KEY,
disc NUMERIC(10) NOT NULL
)
CREATE TABLE b (
id NUMERIC(10) PRIMARY KEY,
a_id NUMERIC(10) NOT NUL REFERENCES a (id)
)
Now you can query
SELECT *
FROM a
JOIN b ON b.a_id = a.id
and recover all discs from a with the proper relation to all b.

Related

Hibernate: mapping nested objects -EDITED-

For example, I have the following tables:
USER:
| USERID | USERNAME | USERTYPEID |
USERTYPE:
| USERTYPEID | USERTYPENAME |
So clearly USERTYPEID is a foreign key that a user use to refer to usertype. The JAVA implementation is as such:
I have a class User and a class UserType, where looks like:
public class User {
private int id;
private UserType ut;
....
}
public class UserType {
...
}
In the User.hbm.xml:
<hibernate-mapping>
<class name="com.pretech.User" table="User">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="id" type="int" column="UserID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
So my questions follows:
1) what should I put to map the UserType here and indicate UserType as foreign key?
2) In the case of User includes a list of UserType (may not conceptually true but just want to use as an example), such as:
public class User {
private int id;
private List<UserType> uts;
}
what shoud I do about the hibernate mapping?
EDIT:
Added explanation about foreign key stuff.
There are so many examples available in net for your example, you can also check the hibernate documentation:
For example if you want to have a User entity with a set of UserType's then you can use one-to-many relationship and the mapping file will be:
<class name="User">
<id name="id" column="id">
<generator class="native"/>
</id>
<set name="uts">
<key column="userId"
not-null="true"/>
<one-to-many class="UserType"/>
</set>
</class>
<class name="UserType">
<id name="id" column="id">
<generator class="native"/>
</id>
</class>
And here is another example from the documentation that uses annotations and List instead of Set:
It should be OneToOne or OneToMany depending on UserType is a single reference or multiple (collection list).
Search on Google and you will get numerous tutorials with both xml based and annotations based

Creating unique Index for table in hibernate with one column being a foreign key

well I am not that advanced in hibernate and I tried to search a lot, found lots of similar questions but none really applying my case. I have a table TABLE1 which has an index "id" and another table which has as index (TABLE1_ID and NUMBER). Only TABLE1_ID is a foreign key while number doesn't reference anything specific
TABLE 1 has the following hibernate mapping
<class name="com.test.basic.BASICTABLE1"
entity-name="com.test.TABLE1" table="TABLE1"
dynamic-update="true" optimistic-lock="version">
<id name="id" type="long">
<column name="id" />
<generator class="native">
<param name="sequence">${table1_id.generator.sequence}</param>
</generator>
</id>
<version name="versionNumber" column="verno" generated="always"
access="field" />
<property name="column1" column="column1" type="string" length="19"
not-null="true" access="field" />
</class>
I am not sure what should be the mapping for the other table. I did it the following way
<class name="com.test.basic.BASICTABLE2"
entity-name="com.test.TABLE2" table="table2"
dynamic-update="true" optimistic-lock="version">
<composite-id name="id" class="com.test.basic.TABLE1TABLE2Id" >
<key-property name="TABLE1_ID" column="TABLE1_ID" type="long" />
<key-property name="NUMBER" column="NUMBER" type="short"/>
</composite-id>
</class>
Please note that I created the class TABLE1TABLE2Id after I read somewhere that there should be some intermediate mapping.
I'm sure there's something wrong with TABLE2 mapping (I am not so advanced with hibernate) but when trying to install the app, I am getting the following error
Foreign key (TABLE1 [id]) must have same number of columns as the referenced primary key (TABLE2 [TABLE1_ID, NUMBER])
I appreciate anybody's help thanks :)
Identity copy (foreign generator)
Finally, you can ask Hibernate to copy the identifier from another
associated entity. In the Hibernate jargon, it is known as a foreign
generator but the JPA mapping reads better and is encouraged.
The primary key from one entity might be as foreign key be primary key for another table.
#Entity
class MedicalHistory implements Serializable {
#Id Integer id;
#MapsId #OneToOne
#JoinColumn(name = "patient_id")
Person patient;
}
#Entity
class Person {
#Id #GeneratedValue Integer id;
}

Hibernate runs all tables doing a simple login

I have done some maintenances in a web system, this system was done in Java, Struts2 and Hibernate 3. One of the items that my customer asked me urgently was to correct the Login, because it's very slow.
I could identify when the user does the login, the Hibernate generates many queries on the database in different tables which does not have relation with login.
My relationship are three tables: User, Student, Teacher
Student
ID
Name
Teacher
ID
Name
User
ID
Login
Password
Every time that a student does a login the query check if it's the correct Login and Password and if the ID exists in a Student table. As you can see there is no foreign key ID, as for example, ID_USER in a Student table.
My Query:
Student student =(Student)session.createCriteria(Student.class)
.add( Property.forName("login").eq(login) )
.add( Property.forName("password").eq(password) )
.setMaxResults(1)
.uniqueResult();
Student inherits from User...
My User.hbm.xml
...
<class name="com.xxx.User" table="TBL_USERS" discriminator-value="0" lazy="true">
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">seq_users</param>
</generator>
</id>
<discriminator column="TYPE" insert="true" />
<subclass name="com.xxx.Student" discriminator-value="1" lazy="true">
<join table="TBL_STUDENTS">
<key column="ID"/>
<property name="name" column="NAME"/>
</join>
</subclass>
<subclass name="com.xxx.Teacher" discriminator-value="2" lazy="true">
<join table="TBL_TEACHERS">
<key column="ID" />
<property name="name" column="NAME" />
</join>
</subclass>
...
In my Hibernate log many strange queries, like bellow:
It's happening in more than 20 different tables and repeating all the times, because that, the login action is slow. But I don't know why it's happening, I don't have much experience with Hibernate.
Hibernate:
select
exerc0_.ID_CLASS as ID6_1_,
exerc0_.ID as ID1_,
exerc0_.ID as ID14_0_,
exerc0_.DATE_ENT as DATA2_14_0_,
exerc0_.TITLE as TITTLE14_0_,
exerc0_.TEXT as TEXT14_0_,
exerc0_.TYPE_EXERC as TYPE5_14_0_,
exerc0_.ID_CLASS as ID6_14_0_
from
TBL_EXERC exerc0_
where
exerc0_.ID_CLASS=?
What could be happening?
Just to not leave this question open forever.
The problem was fixed many time ago, but I forgot completely to reply.
I had to enable few lazy-loadings and make sure to choose LIST instead SET in few other places. Seemed more performatic with LIST.
#Firo was right in somehow.

Hibernate 'join' fetching weird behaviour

I have a user and a message table. User to Message are one-to-many relationships and Message to User are many-to-one relationships. I've marked one of the many-to-one as fetch join. When I 'get' a single Message, Hibernate runs a join query, but when I fetch all the messages, Hibernate runs select queries instead of a join. What could be the reason? Following are the details:
Relationship between them:
User
<set name="messagesForFromUserUid" lazy="true" table="message" inverse="true" cascade="save-update">
<key>
<column name="from_user_uid" not-null="true" />
</key>
<one-to-many class="repository.Message" />
</set>
<set name="messagesForToUserUid" lazy="true" table="message" fetch="select">
<key>
<column name="to_user_uid" not-null="true" />
</key>
<one-to-many class="repository.Message" />
</set>
Message
<many-to-one name="userByFromUserUid" class="repository.User" fetch="join" lazy="false">
<column name="from_user_uid" not-null="true" />
</many-to-one>
<many-to-one name="userByToUserUid" class="repository.User" fetch="select" lazy="proxy">
<column name="to_user_uid" not-null="true" />
</many-to-one>
When I fetch a single message object, Hibernate runs one join query as expected:
Message m = (Message) s.get(Message.class, 2);
Hibernate:
select
message0_.message_uid as message1_1_1_,
message0_.from_user_uid as from2_1_1_,
message0_.to_user_uid as to3_1_1_,
message0_.message_text as message4_1_1_,
message0_.created_dt as created5_1_1_,
user1_.user_uid as user1_0_0_,
user1_.user_name as user2_0_0_,
user1_.user_password as user3_0_0_,
user1_.email as email0_0_,
user1_.first_name as first5_0_0_,
user1_.last_name as last6_0_0_,
user1_.created_dt as created7_0_0_
from
hello.message message0_
inner join
hello.user user1_
on message0_.from_user_uid=user1_.user_uid
where
message0_.message_uid=?
But when I fetch all the messages in one go, Hibernate runs select queries instead:
List<Message> l = s.createQuery("from Message").list();
Hibernate:
select
message0_.message_uid as message1_1_,
message0_.from_user_uid as from2_1_,
message0_.to_user_uid as to3_1_,
message0_.message_text as message4_1_,
message0_.created_dt as created5_1_
from
hello.message message0_
Hibernate:
select
user0_.user_uid as user1_0_0_,
user0_.user_name as user2_0_0_,
user0_.user_password as user3_0_0_,
user0_.email as email0_0_,
user0_.first_name as first5_0_0_,
user0_.last_name as last6_0_0_,
user0_.created_dt as created7_0_0_
from
hello.user user0_
where
user0_.user_uid=?
Hibernate:
select
user0_.user_uid as user1_0_0_,
user0_.user_name as user2_0_0_,
user0_.user_password as user3_0_0_,
user0_.email as email0_0_,
user0_.first_name as first5_0_0_,
user0_.last_name as last6_0_0_,
user0_.created_dt as created7_0_0_
from
hello.user user0_
where
user0_.user_uid=?
Looks like Hibernate does not always use fetch strategies defined in the mapping, for HQL or Criteria queries. They are typically used for get/load. Found a reference here: https://forum.hibernate.org/viewtopic.php?f=1&t=957561
I am not sure but this might be the reason. If you run single query which is join or subquery. It is ok, there may not be any performance difference for single query. But if you are running multiple queries(in your case multiple messages), There may raise Performance issue. I would definitely choose simple select queries instead of join/subqueries. This definitely makes sense if we consider performance. This is what Hibernate does.

How to add collections to a data transfer class in Hibernate

I have the following setup where a class contains a collection. When querying instances of this class I like to populate a data transfer class rather than the data class. However, Hibernate generates a wrong SQL query. What am I missing?
The Hibernate mapping:
<class name="Thread" table="tbl_threads" schema="dbo">
<id name="Id" type="integer">
<column name="i_id"/>
<generator class="identity"/>
</id>
<set name="keywords" inverse="true" lazy="false" cascade="all-delete-orphan" optimistic-lock="false">
<key>
<column name="thread_id" not-null="true"/>
</key>
<one-to-many class="Comment"/>
</set>
<!-- ... -->
</class>
and
<class name="ThreadKeyword" table="tbl_keywords" schema="dbo">
<composite-id name="id"
class="se.ericsson.eab.sdk.fido.server.api.pojos.report.ReportThreadKeywordId">
<key-property name="keywordId" type="integer">
<column name="keyword_id" />
</key-property>
<key-property name="threadId" type="integer">
<column name="thread_id" />
</key-property>
</composite-id>
<!-- ... -->
</class>
The HQL I am using is
SELECT new Composite(t.id, t.keywords, ...)
FROM Thread t, ThreadKeyword tk
WHERE t.id = tk.id.threadId
This generates a SQL where the SELECT part contains only a dot for the keyword attribute:
select thread1_.report_id as col_0_0_, . as col_92_0_
from dbo.tbl_thread reportthre0_ inner join
dbo.tbl_keywords keywords4_ on reportthre0_.i_id=keywords4_.thread_id
It works fine when I query for the data class directly, i.e.
SELECT t
FROM Thread t, ThreadKeyword tk
WHERE t.id = tk.id.threadId
As I understand will Hibernate not find a column name for keywords in the thread table. That is right, since it is a collection. It rather needs to be populated using subsequent queries. If I omit the keywords in the constructor for the Composite class the query gets right but Hibernate won't populate the Set.
How do I get the keywords set populated?
You cannot do that with a collection.
t.id is a column/value
so Hibernate translates that into thread1_.report_id as col_0_0_. Hibernate even gave it the alias col_0_0_
t.keywords is a set of values so Hibernate just can't translate the collection into a column/value.
A query includes a list of columns to be included in the final result
immediately following the SELECT keyword - Wikipedia
Now the
SELECT t FROM Thread t, ThreadKeyword tk WHERE t.id = tk.id.threadId
works fine because Hibernate knows how to translate the query you have there into SQL.

Categories

Resources